多多色-多人伦交性欧美在线观看-多人伦精品一区二区三区视频-多色视频-免费黄色视屏网站-免费黄色在线

國內最全IT社區(qū)平臺 聯系我們 | 收藏本站
阿里云優(yōu)惠2
您當前位置:首頁 > php開源 > 綜合技術 > FragmentTransaction解析 Fragment Transactions和Activity狀態(tài)丟失

FragmentTransaction解析 Fragment Transactions和Activity狀態(tài)丟失

來源:程序員人生   發(fā)布時間:2017-02-23 09:26:36 閱讀次數:3285次

關于FragmentTransaction之前用到過但是了解不全面,只是會簡單使用。今天再次碰到所在在此將它詳細記錄:通過兩篇比較好的文章總結1下,相信看完這兩篇文章你暫時的問題都會得到解決,如果還有甚么疑問大家可以留言討論。

我轉的第1篇文章是作者對他人的文章進1步修改得到的更容易懂的作品:

      之前在使用Fragment的時候偶爾會有這么1個報錯,Can not perform this action after onSaveInstanceState,意思為沒法再onSaveInstanceState以后履行該操作,這個操作就是指commit(),之前也沒怎樣在乎,后來通過查看源碼去了解了1下這個問題,以下是對這個問題的解析及對應解決辦法的對照。

        Fragment是我們常常用到的東西,經常使用的操作有添加(add)、移除(remove)、替換(replace)等,這些操作組成1個集合transaction,我們在通過調用getSupportFragmentManager().beginTransaction()來獲得這個FragmentTransaction類的實例來管理這些操作,將他們存進由activity管理的back stack中,這樣我們就能夠進行fragment變化的回退操作,也能夠這樣去獲得FragmentTransaction類的實例:

[java] view plain copy 在CODE上查看代碼片派生到我的代碼片
  1. FragmentManager  mFragmentManager = getSupportFragmentManager();    
  2. FragmentTransaction  mFragmentTransaction = mFragmentManager.beginTransaction();    
        為何我們會有這類報錯呢,由于我們在使用add(),remove(),replace()等方法將Fragment的變化添加進去,然后在通過commit去提交這些變化(另外,在commit之前可以去調用addToBackState()方法,將這些變化加入到activity管理的back stack中去,這樣用戶調用返回鍵就能夠回退這些變化了),提交完成以后這些變化就會利用到我們的Fragment中去。但是,這個commit()方法,你只能在avtivity存儲他的狀態(tài)之前調用,也就是onSaveInstanceState(),我們都知道activity有1個保存狀態(tài)的方法和恢復狀態(tài)的方法,這個就不詳細解釋了,在onSaveInstanceState()方法以后去調用commit(),就會拋出我們遇到的這個異常,這是由于在onSaveInstanceState()以后調用commit()方法,這些變化就不會被activity存儲,即這些狀態(tài)會被丟失,但我們可以去用commitAllowingStateLoss()這個方法去代替commit()來解決這個為題,下面我們通過源碼去看這兩個方法的區(qū)分。

        首先從我們獲得FragmentTransaction類的實例開始,即getSupportFragmentManager(),源碼是這樣的:

[java] view plain copy 在CODE上查看代碼片派生到我的代碼片
  1. /** 
  2.  * Return the FragmentManager for interacting with fragments associated 
  3.  * with this activity. 
  4.  */  
  5. public FragmentManager getSupportFragmentManager() {  
  6.     return mFragments;  
  7. }  
        而這個返回的mFragments是1個FragmentManagerImpl類 的實例,他繼承自FragmentManager這個類:
[java] view plain copy 在CODE上查看代碼片派生到我的代碼片
  1. final FragmentManagerImpl mFragments = new FragmentManagerImpl();  
        我們在FragmentManager這個類中還看到beginTransaction()這個抽象方法,打開他的實現類
[java] view plain copy 在CODE上查看代碼片派生到我的代碼片
  1. final class FragmentManagerImpl extends FragmentManager {  
  2.   
  3.     ... ...  
  4.   
  5.     @Override  
  6.     public FragmentTransaction beginTransaction() {  
  7.         return new BackStackRecord(this);  
  8.     }  
  9.   
  10.     .... ...  
  11.   
  12. }  
        我們看到這個實現類中的該方法是返回1個BackStateRecord類的實體,我們繼續(xù)去追蹤這個類,就會發(fā)現,這個類實際上是繼承自FragmentTransaction的,并且,我們在這里看到我們熟習的方法:
[java] view plain copy 在CODE上查看代碼片派生到我的代碼片
  1. final class BackStackRecord extends FragmentTransaction implements  
  2.         FragmentManager.BackStackEntry, Runnable {  
  3.   
  4.     public BackStackRecord(FragmentManagerImpl manager) {  
  5.         mManager = manager;  
  6.     }  
  7.   
  8.     public int commit() {  
  9.         return commitInternal(false);  
  10.     }  
  11.   
  12.     public int commitAllowingStateLoss() {  
  13.         return commitInternal(true);  
  14.     }  
  15.       
  16.     int commitInternal(boolean allowStateLoss) {  
  17.         if (mCommitted) throw new IllegalStateException("commit already called");  
  18.         if (FragmentManagerImpl.DEBUG) {  
  19.             Log.v(TAG, "Commit: " + this);  
  20.             LogWriter logw = new LogWriter(TAG);  
  21.             PrintWriter pw = new PrintWriter(logw);  
  22.             dump("  "null, pw, null);  
  23.         }  
  24.         mCommitted = true;  
  25.         if (mAddToBackStack) {  
  26.             mIndex = mManager.allocBackStackIndex(this);  
  27.         } else {  
  28.             mIndex = -1;  
  29.         }  
  30.         mManager.enqueueAction(this, allowStateLoss);  
  31.         return mIndex;  
  32.     }  
  33.   
  34. }  
        終究找到了我們有用的東西了,這里省略了其他沒必要要的代碼,只留下我們需要用的核心代碼,有興趣可以自己去查看源碼,這里還有省略掉我們經常使用的add、remove、replace等方法,回歸正題,看我們要找的兩個方法commit()和commitAllowingStateLoss(),他們都調用了commitInternal(boolean allowStateLoss)這個方法,只是傳入的參數不同,我們去看commitInternal方法,該方法首先去判斷是不是已commit,這個簡單,直接跳過,最后他履行的是FragmentTransactionImpl類的enqueueAction方法,好,不要嫌麻煩,我們繼續(xù)去追蹤這個方法:
[java] view plain copy 在CODE上查看代碼片派生到我的代碼片
  1. public void enqueueAction(Runnable action, boolean allowStateLoss) {  
  2.     if (!allowStateLoss) {  
  3.         checkStateLoss();  
  4.     }  
  5.     synchronized (this) {  
  6.         if (mActivity == null) {  
  7.             throw new IllegalStateException("Activity has been destroyed");  
  8.         }  
  9.         if (mPendingActions == null) {  
  10.             mPendingActions = new ArrayList<Runnable>();  
  11.         }  
  12.         mPendingActions.add(action);  
  13.         if (mPendingActions.size() == 1) {  
  14.             mActivity.mHandler.removeCallbacks(mExecCommit);  
  15.             mActivity.mHandler.post(mExecCommit);  
  16.         }  
  17.     }  
  18. }  
        通過這個方法我們可以看到,他首先去根據commit和commitAllowingStateLoss這兩個方法傳入的參數不同去判斷,然后將任務扔進activity的線程隊列中,這里我們重點去看的是checkStateLoss()這個方法:
[java] view plain copy 在CODE上查看代碼片派生到我的代碼片
  1. private void checkStateLoss() {  
  2.     if (mStateSaved) {  
  3.         throw new IllegalStateException(  
  4.                 "Can not perform this action after onSaveInstanceState");  
  5.     }  
  6.     if (mNoTransactionsBecause != null) {  
  7.         throw new IllegalStateException(  
  8.                 "Can not perform this action inside of " + mNoTransactionsBecause);  
  9.     }  
  10. }  
        這個方法很簡單,就只是1個簡單的判斷而已,并且只有調用commit方法才會履行這里,commitAllowingStateLoss則直接跳過這步,這里我們調用commit方法時,系統系判斷狀態(tài)(mStateSaved)是不是已保存,如果已保存,則拋出"Can not perform this action after onSaveInstanceState"異常,這就是我們遇到的問題了,而用commitAllowingStateLoss方法則不會這樣,這就與我們之前分析的activity去保存狀態(tài)對應上了,在activity保存狀態(tài)完成以后調用commit時,這個mStateSaved就是已保存狀態(tài),所以會拋出異常。



通過第1篇相信大多數人知道了FragmentTransaction的commit方法與commitAllowingStateLoss方法的區(qū)分和前者產生異常的緣由,同時也知道

FragmentTransaction.addToBackStack(String name)方法的作用是將transactions存到activity的backstack中,以便進行回退。


第2篇文章是1篇比較有深度的文章,相信作者是個牛人,要說明的東西很好理解:

Fragment Transactions和Activity狀態(tài)丟失

  • 微信小程序入門與實戰(zhàn)經常使用組件 API 開發(fā)技能 項目實戰(zhàn)
  • 扛得住的MySQL數據庫架構
  • 算法與數據結構C++精解
  • 所向無敵的響應式開發(fā)

下面的堆棧跟蹤和異常代碼,自從Honeycomb的初始發(fā)行版本就1直使得StackOverflow很迷惑。

Java
1
2
3
4
5
6
; html-script: false ]
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)

這篇博客將會解釋,這個異常在甚么時候產生和為何會產生?并且提供幾種方法讓這類異常不會產生在你的利用中。

為何會拋出這個異常?

這類異常的出現是由于,在Activity的狀態(tài)保存以后,嘗試去提交1個FragmentTransaction。這類現象被稱為活動狀態(tài)丟失(Activity State Loss)。但是,在我們了解這類異常的真正含義之前,讓我們先看看當onSaveInstanceState()函數被調用的時候到底產生了甚么。

正如最近我在關于Binders & Death Recipients博客里面討論的那樣,Android利用在Android運行環(huán)境里很難決定自己的命運。Android系統可以在任什么時候候通過結束1個進程以釋放內存,而且background activities可能在沒有任何正告的情況下被清算。為了確保這類不肯定的行動對用戶是透明的,在Activity可以燒毀之前,通過調用onSaveInstanceState()方法,架構給每一個Activity1個保存本身狀態(tài)的機會。在重新加載已保存的狀態(tài)時,對foreground和background Activities的切換,為用戶帶來了無縫切換的體驗。用戶不用去關心這個Activity是不是被系統燒毀了。

在框架調用onSaveInstanceState()方法時,給這個方法傳遞了1個Bundle對象。Activity可以通過這個對象來存儲它的狀態(tài),而且Activity把它的dialogs、fragments和views的狀態(tài)都保存在這個對象里面。當這個函數返回時,系統打包這個Bundle對象通過1個Binder接口傳遞給系統服務處理,然后它會被安全的存儲下來。當系統決定重新創(chuàng)建這個Activity的時候,它會給這個利用傳回1個相同的Bundle對象,通過這個對象可以重新裝載Activity燒毀時的狀態(tài)。

那為何會拋出這個異常呢?這個問題源于這樣的事實,Bundle對象代表1個Activity在調用onSaveInstanceState()方法的1個瞬間快照,僅此而已。這意味著,當你在onSaveInstanceState()方法調用后會調用FragmentTransaction的commit方法。這個transaction將不會被記住,由于它沒有在第1時間記錄為這個Activity的狀態(tài)的1部份。從用戶的角度來看,這個transaction將會丟失,可能致使UI狀態(tài)丟失。為了保證用戶的體驗,Android不惜1切代價避免狀態(tài)的丟失。因此,不管甚么時候產生,都將簡單的拋出1個IllegalStateException異常。

甚么時候會拋出這個異常?

如果之前你遇到過這個異常,或許你已注意到異常拋出的時間在不同的版本平臺有細微的差別。或許你會發(fā)現,老版本的機器拋出異常的頻率更低,或你的利用使用Support Library比使用官方的框架類的時候更容易拋出異常。這個細微的區(qū)分已致使1些人在猜想Support Library有bug,是不值得相信的。但是,這樣的料想完全毛病。

這些細微區(qū)分存在的緣由是源于Honeycomb上對Activity生命周期所做的巨大改變。在Honeycomb之前,Activity直到暫停后才斟酌被燒毀。這意味著在onPause()方法之前onSaveInstanceState()方法被立即調用。但是,從Honeycomb開始,斟酌燒毀Activity只能是在他們停止以后,這意味著onSaveInstanceState()方法現在是在onStop()方法之前調用,以此代替在onPause()方法之前調用。這些不同總結以下表:

 Honeycomb之前的版本Honeycomb及更新的版本
Activities會在onPause()調用前被結束?NONO
Activities會在onStop()調用前被結束?YESNO
onSaveInstanceState(Bundle)會在哪些方法調用前被履行?onPause()onStop()

作為Activity生命周期已做的細微改變的結果,Support Library有時候需要根據平臺的版本來改變它的行動。比如,在Honeycomb及以上的裝備中,每當1個commit方法在onSaveInstanceState()方法以后調用時,都會拋出1個異常來提示開發(fā)者狀態(tài)丟失產生了。但是,在Honeycomb之前的裝備上,每次它產生時并拋出異常將更受限制,他們的onSaveInstanceState()方法在Activity的生命周期中更早調用,結果更容易產生狀態(tài)丟失。Android團隊被迫做了1個折衷的辦法:為了更好的與老版本平臺交互,老的裝備不能不接受偶然狀態(tài)丟失可能產生在onPause()方法和onStop()方法之間。Support Library在不同平臺的行動總結以下表:

 Honeycomb之前的版本Honeycomb及更新的版本
commit()在onPause()前被調用OKOK
commit()在onPause()和onStop()履行中間被調用STATE LOSSOK
commit()在onStop()以后被調用EXCEPTIONEXCEPTION

如何避免拋出異常?

1旦你了解了到底產生了甚么,避免產生Activity狀態(tài)丟失將會很簡單。如果你讀了這篇博客,那末很榮幸你更好的了解了Support Library是怎樣工作的,和在你的利用中避免狀態(tài)丟失為何如此的重要。假設你查看這個博客是為了查找快速解決的辦法,那末,當你在你的利用中使用FragmentTransactions的時候,應牢記以下的這些建議:

建議1

當你在Activity生命周期函數里面提交transactions的時候要謹慎。大部份的利用僅僅在onCreate()方法被調用的開始時間提交transactions,或在相利用戶輸入的時候,因此將不可能碰到任何問題。但是,當你的transactions在其他的Activity生命周期函數提交,如onActivityResult()onStart()onResume(),事情將會變得奧妙。例如,你不應當在FragmentActivity的onResume()方法中提交transactions。由于有些時候這個函數可以在Activity的狀態(tài)恢復前被調用(可以查看相干文檔了解更多信息)。如果你的利用要求在除onCreate()函數以外的其他Activity生命周期函數中提交transaction,你可以在FragmentActivity的onResumeFragments()函數或Activity的onPostResume()函數中提交。這兩個函數確保在Activity恢復到原始狀態(tài)以后才會被調用,從而避免了狀態(tài)丟失的可能性。(示例:看看我對this StackOverflow question的回答,來想一想如何提交FragmentTransactions作為Activity的onActivityResult方法被調用的響應)。

建議2

避免在異步回調函數中提交transactions。包括經常使用的方法,比如AsyncTask的onPostExecute方法和LoaderManager.LoaderCallbacks的onLoadFinished方法。在這些方法中履行transactions的問題是,當他們被調用的時候,他們完全沒有Activity生命周期確當前狀態(tài)。例如,斟酌下面的事件序列:

  1. 1個Activity履行1個AsyncTask。
  2. 用戶按下“Home”鍵,致使Activity的onSaveInstanceState()onStop()方法被調用。
  3. AsyncTask完成并且onPostExecute方法被調用,而它沒成心識到Activity已結束了。
  4. 在onPostExecute函數中提交的FragmentTransaction,致使拋出1個異常。

1般來講,避免這類類型異常的最好辦法就是不要在異步回調函數中提交transactions。Google工程師似乎同意這個信條。根據Android Developers group上的這篇文章,Android團隊認為UI主要的改變,源于從異步回調函數提交FragmentTransactions引發(fā)不好的用戶體驗。如果你的利用需要在這些回調函數中履行transaction而沒有簡單的方法可以確保這個回調函數不好在onSaveInstanceState()以后調用。你可能需要訴諸于使用commitAllowingStateLoss方法,并且處理可能產生的狀態(tài)丟失。(可以看看StackOverflow上的另外兩篇文章,這1篇和另外一篇)。

建議3

作為最后的辦法,使用commitAllowingStateLoss()函數。commit()函數和commitAllowingStateLoss()函數的唯1區(qū)分就是當產生狀態(tài)丟失的時候,后者不會拋出1個異常。通常你不應當使用這個函數,由于它意味可能產生狀態(tài)丟失。固然,更好的解決方案是commit函數確保在Activity的狀態(tài)保存之前調用,這樣會有1個好的用戶體驗。除非狀態(tài)丟失的可能無可避免,否則就不應當使用commitAllowingStateLoss()函數。

第2篇作者給出了使用"提交"的時機,和使用建議,并總結出:
commit()函數和commitAllowingStateLoss()函數的唯1區(qū)分就是當產生狀態(tài)丟失的時候,后者不會拋出1個異常。通常你不應當使用這個函數,由于它意味可能產生狀態(tài)丟失


生活不易,碼農辛苦
如果您覺得本網站對您的學習有所幫助,可以手機掃描二維碼進行捐贈
程序員人生
------分隔線----------------------------
分享到:
------分隔線----------------------------
關閉
程序員人生
主站蜘蛛池模板: 欧美一线天 | 亚洲黄色在线看 | 国内精品麻豆 | 伊人伊人 | 国产精品亚洲综合五月天 | 日本免费www| 久久精品成人一区二区三区 | 一级欧美毛片成人 | 久久www免费人成_看片高清 | 最近中文字幕高清字幕 | 伊人久久99亚洲精品久久频 | 高清一区二区三区 | 亚洲人成网站观看在线观看 | 久久久影院亚洲精品 | 国产亚洲免费观看 | 日韩欧美视频在线一区二区 | 一级成人毛片 | 午夜看片福利 | 欧美亚洲综合另类在线观看 | 波霸欧美性猛交xxxxxx | 欧美一级毛片一 | 精品亚洲福利一区二区 | 999yy成年在线视频免费看 | 又做又爱高清免费观看 | 欧美亚洲国产片在线观看 | a毛片全部播放免费视频完整18 | a级爱爱视频 | 精品日韩在线视频一区二区三区 | 日本高清不卡免费 | 欧美另类精品xxxx人妖换性 | 欧美精品网站 | 最近在线更新中文字幕1 | 亚洲第一国产 | 日本中文在线播放 | 亚洲精品456人成在线 | 97婷婷色 | 女女女女女女bbbbbb毛片 | 久久另类| 欧美久久久久久久久 | 多人做人爱视频在线观看 | 日韩欧美综合在线二区三区 |