【Android】App 或 Activity 銷毀重建的狀態(tài)恢復(fù)對回調(diào)帶來的影響

問題背景

在開發(fā) PassportSDK 時遇到的此類問題,測試反饋說當(dāng)打開 App 進入登錄頁面,此時如果切換出去到手機設(shè)置頁面將App 的定位權(quán)限設(shè)置為「拒絕授予」,在切換回 App 會發(fā)生登錄信息完全正確也登錄不上的情況。

問題分析

經(jīng)過抓包及打斷點查找問題根源,發(fā)現(xiàn)是用戶在操作應(yīng)用權(quán)限授予時,App 對象及 Activity 經(jīng)過了銷毀重建,此時一些基礎(chǔ)的 UI 狀態(tài)數(shù)據(jù)可以通過 onSaveInstanceBundle 方法進行保存,在 onCreate 方法中取出保存的狀態(tài)進行恢復(fù)。但是登陸成功的回調(diào) loginListener 由于無法序列化且是隨著啟動視圖的方法設(shè)置進來的,所以無法恢復(fù)。這造成系統(tǒng)恢復(fù)的 Activity 對象持有的回調(diào) loginListener 為空,所以即使用戶輸入的登陸信息完全正確也無法登陸。

問題解決及帶來的思考

今后的開發(fā)中,要避免這種無法序列化的回調(diào)類實例等與啟動視圖的方法綁定的 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);
}

可以看到上面的方法中,我們將設(shè)置登錄回調(diào)的步驟放在了啟動視圖的方法中。這在平時并沒有什么問題,我們只需要在某個按鈕的點擊事件中調(diào)用此方法,傳入一個回調(diào),即可完成正常的業(yè)務(wù)邏輯。修改之前的代碼邏輯如下:

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 經(jīng)過上述的權(quán)限更改、屏幕旋轉(zhuǎn)、在后臺時間過久等場景,導(dǎo)致 App 對象及 Activity 銷毀重建時,由于重建的 Activity 不是通過上述的啟動視圖的方法展示的,并且在 Activity 中也無法將回調(diào)類對象序列化并恢復(fù),所以我們以后一定要記住需要將回調(diào)與啟動視圖的方法分離開來,并在上文對象(上個頁面 Activity 對象或 App 對象)的 onCreate 生命周期中重新調(diào)用設(shè)置回調(diào)的方法即可。修改之后的代碼如下:

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

        public void onError(){
            //TODO
        }

    }
    public void onCreate(Bundle savedInstanceBundle) {
        // 放在這里可以保證系統(tǒng)恢復(fù)此上文 Activity 時就為 LoginActivity 添加了回調(diào)類實例
        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);
    }

}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 學(xué)習(xí)資料: Android群英傳 Android開發(fā)藝術(shù)探索 Activity是與用戶交互的第一接口,感覺說是四大...
    英勇青銅5閱讀 2,855評論 15 41
  • Activity https://developer.android.com/guide/components/a...
    XLsn0w閱讀 776評論 0 4
  • Activity 在應(yīng)用中的表現(xiàn)主要是顯示各種UI元素,并且為這些UI元素設(shè)置時間處理函數(shù),使得用用戶可以與這些U...
    sssssss_閱讀 777評論 0 1
  • Activity是一個應(yīng)用組件,用戶可與其提供的屏幕進行交互。以執(zhí)行撥打電話,拍攝照片,發(fā)送電子郵件或查看地圖等操...
    DanieX閱讀 1,165評論 0 4
  • 該系列主要是記錄、回顧之前的學(xué)習(xí)和一些筆記。轉(zhuǎn)載請注明! Activity在應(yīng)用中的表現(xiàn)為界面,它會加載指定的布局...
    Angki閱讀 429評論 0 0

友情鏈接更多精彩內(nèi)容