下面自從Honeycomb釋出後,下面棧跟蹤資訊和異常資訊已經困擾了StackOverFlow很久了。
java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState at android.support.v4.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1341) at android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1352) at android.support.v4.app.BackStackRecord.commitInternal(BackStackRecord.java:595) at android.support.v4.app.BackStackRecord.commit(BackStackRecord.java:574)
這篇文章會解釋這個異常什麼時候會抛出以及原因,并且會以一些建議收尾。這些建議會幫助你不會因為這個異常導緻程式崩潰。
當Android架構調用onSaveInstanceState(),它将一個Bundle對象通過這個方法傳遞,以便Activity後面恢複狀态。Activity可以将它的Dialog、fragment以及view的狀态儲存在Bundle中。當這個方法傳回的時候,系統通過Binder結果打包Bundle對象然後傳給系統服務程序。系統服務程序負責保證Bundle對象安全地儲存下來。當系統後面決定重新建立Activity的獲釋後,它就會将相同的Bundle對象發揮應用程式,以便于用它來回複舊的Activity狀态。
是以為什麼這個異常随後抛出?這個問題導緻的原因是因為那些Bundle對象代表Activity在onSaveInstanceState()被調用那時候的一個快照,沒更多了。這就意味着當你在onSaveInstanceState()之後調用FragmentTransaction#commit()的時候,transation不會被記錄。因為它不會作為之前Activity的狀态被儲存。從使用者的角度來說,這個transaction就像丢失了,導緻UI狀态意外的丢失。為了保證使用者體驗,Android不計一切代價避免狀态丢失,也就是當它發生的時候簡單地抛出一個IllegalStateException。
如果你之前已經碰到過這個異常,你可能會注意到異常抛出的時機因為不同的Android版本而不一緻。比如,如可能會發現老版本的裝置上,這個異常抛出比較不頻繁,或者當你的程式中使用support library而不是官方架構中的類時更容易觸發這個異常。這些輕微的一緻讓很多人都以為support library有bug,不值得信任。然而,這些假設都不是正确的。
這些輕微的不一緻是因為在Honeycomb版本中的Activity生命周期有了重要的變化。Honeycomb之前的版本,activity被認為在pause之前都不會被殺掉,這意味着onSaveInstanceState()會在onPause()之前被調用。從HoneyComb開始,Activity被認為隻會在stopped隻會被殺掉,意味着onSaveInstanceState()現在會在onStop()之前被調用而不是在onPause()之前。這些變化在下表中總結:
Honeycomb之前
Honeycomb之後
Activity是否可以在onPause()之前被殺掉?
NO
Activity是否可以在onStop()之前被殺掉?
YES
onSaveInstanceState(Bundle) 保證在...之前被調用
onPause()
onStop()
由于Activity生命周期的輕微變化,support library有時候需要根據系統版本選擇他的行為。比如,在Honeycomb及以上裝置,每次在onSaveInstanceState()之後調用commit()都會抛出一個異常,以便警告開發者已經發生了狀态丢失。然而,在每次這種情況抛出異常在Honeycomb之前的裝置上就顯得太具有限制性了,它們的onSaveInstanceState()調用發生在Activity生命周期中更早的一段時期,并且更容易導緻意外的狀态丢失。Android團隊被迫做出妥協:為了更好地跟老版本相容,舊裝置可能必須要忍受在onPause()和onStop()之間意外的狀态丢失。Support library在不同兩個版本的行為如下表總結:
commit在onPause()之前
OK
commit在onPause() 和onStop()之間
STATE LOSS
commit在onStop()之後
EXCEPTION
一旦你懂得了真正發生了什麼,避免Activity狀态丢失就簡單多了。如果你已經在讀這篇文章之間就已經解決過這個問題了,希望你能對support library有一個更深的了解,并且知道為什麼避免狀态丢失對你的程式這麼重要。為了友善你通過這篇文章尋找快速的解決方案,這裡有一些建議希望你記得在使用FragmentTransactions的時候使用:
避免是異步調用方法中執行transactions 。這個包括經常被使用的方法比如AsyncTask#onPostExecute() 和LoaderManager.LoaderCallbacks#onLoadFinished() 。在這些方法中執行transactions會有問題,因為他們當這些方法被回調的時候,他們不知道Activity目前的生命周期。比如,考慮下面的事件序列:
一個Activity執行一個AsyncTask
使用者按下Home鍵,導緻這個Activity的onSaveInstanceState()和onStop() 方法被回調。
AsyncTask完成然後onPostExecute()被調用,而不知道Activity已經處于stopped狀态。
在onPostExectute()方法中的FragmentTransaction被committed,導緻一個異常被抛出。
隻使用commitAllowingStateLoss()作為最後的解決方案。commit()和commitAllowingStateLoss()唯一的差別是後者在狀态丢失的時候不會抛出異常。通常你不會想使用這個方法因為它意味着狀态丢失可能發生。更好的解決方案當然是修改你的程式以便commit()被保證在activity的狀态被儲存前調用,因為這樣可能會讓使用者體驗更好。除非狀态丢失是不可避免的,否則commitAllowingStateLoss()就不應該被使用。
<a href="http://www.androiddesignpatterns.com/2013/08/fragment-transaction-commit-state-loss.html">譯文連結Fragment transaction commit state loss</a>
本文轉自陳哈哈部落格園部落格,原文連結http://www.cnblogs.com/kissazi2/p/4181093.html如需轉載請自行聯系原作者
kissazi2