Android異常退出時Activity資料的儲存和恢復
本文章是基於Kotlin版的簡單Activity-Fragment狀態切換及資料保留的探討,原問題如下: 頁面長時間放置後臺,或者在其他頁面crash後,退到主頁面,發現智護頁面出現重疊。 該頁面架構簡單:主Activity對兩個Fragment,一個智護Fragment,一個報告Fragment。
一、Activity建立時讀取資料
正常情況下Activity的生命週期比較簡單,這裡就不詳細敘述了,不清楚的可以自行百度~ Activity在建立的時候會執行生命週期的OnCreate方法,該方法的引數為Bundle型別。那麼這個資料是從哪裡得來的呢?
override fun onCreate(savedInstanceState: Bundle?) { }
1、Activity如何保持資料
當橫豎屏切換時,Activity會被銷燬,生命週期方法onPause,onStop,onDestory等均會被呼叫,此時Activity屬於異常情況下終止的,所以系統會呼叫onSaveInstanceState方法對Activity的狀態進行儲存。該方法在onStop之前呼叫,與onPause沒有既定的時序關係。 當出現Crash時,除了其他的生命週期方法不會執行外,也會執行儲存資料的操作。 當Activity被重新建立後,系統會呼叫onRestoreInstanceState,將之前onSaveInstanceState儲存的資料Bundle傳遞給onRestoreInstanceState和onCreate方法,因此我們可以通過onRestoreInstanceState和onCreate方法判讀Activity是否被重建了,如果被重建了,那麼我們就可以取出之前儲存的資料並進行恢復,onRestoreInstanceState的呼叫時機在onStart之後。 注意:正常情況下Activity的建立和銷燬不會呼叫onSaveInstanceState和onRestoreInstanceState方法。
// 此方法用來儲存當前異常退出的Activity的資料 override fun onSaveInstanceState(outState: Bundle?, outPersistentState: PersistableBundle?) { super.onSaveInstanceState(outState, outPersistentState) } // 此方法用來過載當前異常退出的Activity的資料 override fun onRestoreInstanceState(savedInstanceState: Bundle?) { super.onRestoreInstanceState(savedInstanceState) }
我們只需要在onSaveInstanceState中儲存需要儲存的引數,然後再onRestoreInstanceState獲取儲存的資料並進行設定即可。
2、Activity恢復資料
OnCreate方法的Bundle型別引數,其實和onRestoreInstanceState中的Bundle引數是一樣的,不過需要我們自己進行判斷,而onRestoreInstanceState如果Bundle為Null時則不會呼叫。因此我們也可以對onCreate方法的Bundle引數進行判斷,當Bundle不為null時對資料進行恢復。
2、如何防止Activity重建
1、設定Activity的方向固定
onCreate方法中設定 但是這種情況不適合手機已經設定為自動旋轉的情況。需要在清單檔案下設定 android:screenOrientation=“portrait”
//
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
2、Manifest清單設定
不僅僅當螢幕方向切換時會重建Activity,當系統配置發生改變的時候Activity都會被重建,例如使用者插入外接鍵盤,運營商改變,介面模式(例如開啟夜間模式)等都會導致Activity重建。如果我們不希望當系統配置發生變化介面重建,那麼我們需要在AndroidManifest.xml中對Activity的configChange屬性進行配置。例如我們不希望螢幕旋轉時重建,則需要如下設定:
android:screenOrientation="portrait"
android:configChanges="orientation"
3、解決上述問題
因為Activity的重建,導致圖中的問題出現。
解決方式一:
使用tag標記Fragment,再次使用的時候判斷該Fragment是否銷燬:
var monitorFragment = fragmentManager.findFragmentByTag("monitor")
monitorFragment?.let {
fragmentTransaction.show(it)
}?:let {
monitorFragment = MonitorFragment()
fragmentTransaction.add(R.id.fg_content,monitorFragment,"monitor")
}
解決方式二:
在Oncreate方法中,判斷savedInstanceState是否為空,如果不為空,直接finish掉,直接讓當前頁面掛掉(實用於負責頁面,複雜資料,避免引發更多的錯誤)。
override fun onCreate(savedInstanceState: Bundle?) {
if (savedInstanceState != null){
finish()
}
}
其實和Activity類似,Fragment也有onSaveInstanceState的方法,在此方法中進行儲存資料,然後在onCreate或者onCreateView或者onActivityCreated進行恢復都可以。網上相關文章很多,學習不能停,如果你願意,總能發現驚喜!