【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);
    }

}

你可能感兴趣的:(【Android】App 或 Activity 销毁重建的状态恢复对回调带来的影响)