【Android】App 或 Activity 销毁重建的状态恢复对回调带来的影响

问题背景

在开发 PassportSDK 时遇到的此类问题,测试反馈说当打开 App 进入登录页面,此时如果切换出去到手机设置页面将App 的定位权限设置为「拒绝授予」,在切换回 App 会发生登录信息完全正确也登录不上的情况。

问题分析

经过抓包及打断点查找问题根源,发现是用户在操作应用权限授予时,App 对象及 Activity 经过了销毁重建,此时一些基础的 UI 状态数据可以通过 onSaveInstanceBundle 方法进行保存,在 onCreate 方法中取出保存的状态进行恢复。但是登陆成功的回调 loginListener 由于无法序列化且是随着启动视图的方法设置进来的,所以无法恢复。这造成系统恢复的 Activity 对象持有的回调 loginListener 为空,所以即使用户输入的登陆信息完全正确也无法登陆。

问题解决及带来的思考

今后的开发中,要避免这种无法序列化的回调类实例等与启动视图的方法绑定的 Case,例如,PassportSDK 中启动登录页面的方法如下:

public void login(int loginType, MCLoginListener listener) {
    LoginActivity.setLoginListener(listener);
    Intent intent = new Intent(Global.getContext(), LoginActivity.class);
    intent.putExtra(LOGIN_TYPE, loginType);
    Global.getContext().startActivity(intent);
}

可以看到上面的方法中,我们将设置登录回调的步骤放在了启动视图的方法中。这在平时并没有什么问题,我们只需要在某个按钮的点击事件中调用此方法,传入一个回调,即可完成正常的业务逻辑。修改之前的代码逻辑如下:

public class ActivityDemo extend Activity {
    MCLoginListener mListener = new MCLoginListener() {
        public void onSuccess(){
            //TODO
        }

        public void onError(){
            //TODO
        }

    }
    public void onCreate(Bundle savedInstanceBundle) {
        Button btn = findViewById(R.id.btn);
        btn.setOnClickListener(v -> {
            login(Global.CODE_PSD_LOGIN, mListener);
        })    
    }
}

但是假如 App 经过上述的权限更改、屏幕旋转、在后台时间过久等场景,导致 App 对象及 Activity 销毁重建时,由于重建的 Activity 不是通过上述的启动视图的方法展示的,并且在 Activity 中也无法将回调类对象序列化并恢复,所以我们以后一定要记住需要将回调与启动视图的方法分离开来,并在上文对象(上个页面 Activity 对象或 App 对象)的 onCreate 生命周期中重新调用设置回调的方法即可。修改之后的代码如下:

public class ActivityDemo extend Activity {
    MCLoginListener mListener = new MCLoginListener() {
        public void onSuccess(){
            //TODO
        }

        public void onError(){
            //TODO
        }

    }
    public void onCreate(Bundle savedInstanceBundle) {
        // 放在这里可以保证系统恢复此上文 Activity 时就为 LoginActivity 添加了回调类实例
        LoginActivity.setLoginListener(listener);
        Button btn = findViewById(R.id.btn);
        btn.setOnClickListener(v -> {
            login(Global.CODE_PSD_LOGIN);
        })    
    }

    // 只保留纯粹的启动视图方法
    public void login(int loginType) {
        Intent intent = new Intent(Global.getContext(), LoginActivity.class);
        intent.putExtra(LOGIN_TYPE, loginType);
        Global.getContext().startActivity(intent);
    }

}
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • 学习资料: Android群英传 Android开发艺术探索 Activity是与用户交互的第一接口,感觉说是四大...
    英勇青铜5阅读 2,838评论 15 41
  • Activity https://developer.android.com/guide/components/a...
    XLsn0w阅读 765评论 0 4
  • Activity 在应用中的表现主要是显示各种UI元素,并且为这些UI元素设置时间处理函数,使得用用户可以与这些U...
    sssssss_阅读 766评论 0 1
  • Activity是一个应用组件,用户可与其提供的屏幕进行交互。以执行拨打电话,拍摄照片,发送电子邮件或查看地图等操...
    DanieX阅读 1,160评论 0 4
  • 该系列主要是记录、回顾之前的学习和一些笔记。转载请注明! Activity在应用中的表现为界面,它会加载指定的布局...
    Angki阅读 408评论 0 0

友情链接更多精彩内容