问题背景
在开发 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);
}
}
网友评论