? ? ? 最近在bugly看到兩個關于java.lang.IllegalStateException的異常,一個是java.lang.IllegalStateException: Activity has been destroyed。如果不懂的請參考java.lang.IllegalStateException: Activity has b... - 簡書,還有一個就是本文講的一個異常情況。不過出現本文這種異常還有一種情況。按照我以往的風格直接貼上bug詳情吧。
? ? ?一種是調用DialogFragment的dismiss()方法產生的異常,一種是點擊back鍵產生的異常 。項目中采用DialogFragment來實現加載框的效果,至于為啥要用DialogFragment如果不懂請參考DialogFragment的整理,看bugly上面的詳情我們可以知道這個bug是在調用DialogFragment的dismiss方法時產生的或者是點擊back鍵產生的。這兩種情況產生的原因是一致的,下面我重點講解一下本文異常的第一種情況。不過說到這里我們首先要弄清一個知識點,onSaveInstanceState這個方法啥時候回調用呢?
? ? ? ??
? ? ? 概括的講,onSaveInstanceState 這個方法會在activity 將要被kill之前被調用以保存每個實例的狀態,以保證在將來的某個時刻回來時可以恢復到原來的狀態,但和activity 的生命周期方法onStop 和 onPause 不一樣,與兩者并沒有絕對的先后調用順序,或者說并非所有場景都會調用onSaveInstanceState 方法。那么onSaveInstanceState 方法何時會被調用呢,或者這么問,什么時候activity 會被系統kill 掉呢?有以下幾種比較常見的場景:?
(1)用戶主動按下home 鍵,系統不能確認activity 是否會被銷毀,實際上此刻系統也無法預測將來的場景,比如說內存占用,應用運行情況等,所以系統會調用onSaveInstanceState保存activity狀態 ;
(2)activity位于前臺,按下電源鍵,直接鎖屏;?
(3)橫豎屏切換;?
(4)activity B啟動后位于activity A之前,在某個時刻activity A因為系統回收資源的問題要被kill掉,A通過onSaveInstanceState保存狀態。
? ? ?接下來我們來分析一下為啥調用dismiss()產生bug的原因了,看到這里一定會有人說看源碼不就得了??!沒錯你說的太對了,那么我們就從dismis()的源碼里面分析了。首先dismiss回調用dismissInternal(false)
繼續看源碼:
有沒有覺得很熟悉啊!我們繼續看commit()源碼,
有人看到這里覺得眼花了,不過你要相信自己的眼睛.沒錯又看到了熟悉的代碼了吧!commitInternal(false)
因為allowStateLoss為false因此回調用checkStateLoss(),至此我們可以很清楚的知道了bugly上面介紹的報錯的內容了。
?那我們就來查看一下哪個地方會將mStateSaved置為true,經過查找我們發現saveAllState()方法里面
接下來我們就來查找一下saveAllState()方法被調用的地方。我們知道activity生命周期有一個方法是onSaveInstanceState(Bundle outState)
繼續看源碼
? ? ? 至此我們終于可以知道了報錯的原因所在了,我們的activity在某種場景下處于被kill 掉的邊緣,系統就調用了onSaveInstanceState 方法,這個方法里面會調用 FragmentManager saveAllState 方法,將DialogFragment 的狀態保存,如果此時調用DialogFrament的dismiss()方法就會報這個異常了。因此我們可以調用DialogFragment的dismissAllowingStateLoss()方法來替換掉dismiss(),說到這里也許有人會問dismissAllowingStateLoss最終也會走enqueueAction方法,allowStateLoss為true那么可能會報?"Activity has been destroyed"這個異常了,你這個問題問的非常好,不過如果你看了我上一篇的文章你也許就知道你想要的答案了。
? ? ?第二種情況是因為activity調用了onSaveInstanceState 方法,這個方法里面會調用 FragmentManager saveAllState 方法,將fragment 的狀態保存,在狀態保存后用戶又主動調了 onBackPressed ,而這個方法的超類super.onBackPressed 方法會判斷FragmentManager 是否保存了狀態,如果已經保存就會拋出IllegalStateException 的異常 。源碼如下:
? ?又看到了熟悉的方法checkStateLoss();然后你就知道了問題的所在了??針對onbackpress 導致的異常,也有幾種解決手段?
(1)在api11 以上 不調用super 的onSaveInstanceState 方法
protectedvoidonSaveInstanceState(Bundle outState) {
//No call for super(). Bug on API Level > 11.
}
這樣做的后果會導致activity 和 fragment 的所有狀態,在activity 被系統殺死掉后無法保存,所以如果有保存狀態的需要,這個方法是不適用的。
(2)重寫 onBackPressed 方法,不調用super.onBackPressed ,直接調用finish 。原因很簡單,super.onBackPressed 里面會調用FragmentManager popBackStackImmediate() 方法,如果直接掉finish 就不會觸發異常,但這種情況只建議在沒有使用 Fragments api時調用。
(3)通過反射手段在onSaveInstanceState 方法里調用 FragmentManagerImpl noteStateNotSaved方法將 mStateSaved 變量置為false ,這樣既不會導致activity狀態丟失,也能確保退出時不會拋出異常,算是比較優雅的處理途徑。
至此:本篇文章結束,謝謝!