Fragment的FragmentTransaction 的commit()和commitAllowingStateLoss()以及commitNow()和commitNowAllowingStateLoss()


android開發中肯定用到過Fragment

 

 1      fragmentManager = getSupportFragmentManager();
 2 
 3       lifeFragment1 = new FragmentLife();
 4         Bundle bundle = new Bundle();
 5         bundle.putString("extra_test", "FragmentLife1");
 6         lifeFragment1.setArguments(bundle);
 7 
 8         //其實是通過FragmentManagerImpl獲取一個BackStackRecord,
 9 //        只能在activity存儲它的狀態(onSaveInstanceState(),當用戶要離開activity時)之前調用commit(),如果在存儲狀態之后調用commit(),將會拋出一個異常。
10 //        這是因為當activity再次被恢復時commit之后的狀態將丟失。如果丟失也沒關系,那么使用commitAllowingStateLoss()方法。
11 //        commit和CommitNow都會拋出異常,如果在onSaveInstanceState()后執行的話。allowStateLoss=false,方法后面會執行檢查checkStateLoss(),就是已經保存狀態或stop的就會拋出異常IllegalStateException
12 //        commitNow不允許addToBackStack,commitNow是不允許加入BackStack中去的,會將mAddToBackStack標志設置為false
13 
14         //class BackStackRecord extends FragmentTransaction implements BackStackEntry, OpGenerator
15         FragmentTransaction transaction = fragmentManager.beginTransaction();
16         transaction.add(R.id.fragment_container, lifeFragment1);
17 //        transaction.addToBackStack("frag1"); //設置BackStackRecord的mAddToBackStack標志為true
18         //int類型的返回值,而commitNow是void類型返回值。
19         transaction.commit();
20         transaction.commitAllowingStateLoss();
21         //同commit一樣調用內部的commitInternal()方法,只不過傳遞的參數不同,commitAllowStateLoss的allowStateLoss是true,允許丟失狀態不做檢查,所以不會拋異常。
22         //commit、commitAllowingStateLoss調用了FragmentManagerImpl.enqueueAction的方法,丟進線程隊列中
23 
24         transaction.commitNow();  

這段代碼我們經常寫,會很熟悉。但有時我們可能會碰到一個異常,信息如下:

1 Caused by: java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState

大意是在activity的onSaveInstanceState調用后再commit的Transaction導致的異常。為了不拋出異常有人建議使用commitAllowingStateLoss來代替commit。

那么commit和commitAllowingStateLoss有什么區別?

1 public int commit() {
2     return commitInternal(false);
3 }
4 
5 public int commitAllowingStateLoss() {
6     return commitInternal(true);
7 }

commit和commitAllowingStateLoss都調用了commitInternal()方法,只是一個傳了false,一個傳了true,接着往下看: 

 1 int commitInternal(boolean allowStateLoss) {
 2     if (mCommitted) {
 3         throw new IllegalStateException("commit already called");
 4     }
 5     ......
 6     mCommitted = true;
 7     if (mAddToBackStack) {
 8         mIndex = mManager.allocBackStackIndex(this);
 9     } else {
10         mIndex = -1;
11     }
12     mManager.enqueueAction(this, allowStateLoss);
13     return mIndex;
14 }

主要是mManager.enqueueAction(this, allowStateLoss)來執行這個任務,根據傳入的參數繼續往下走,可以看到:

 1 public void enqueueAction(Runnable action, boolean allowStateLoss) {
 2     if (!allowStateLoss) {
 3         checkStateLoss();
 4     }
 5     synchronized (this) {
 6         if (mDestroyed || mHost == 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             mHost.getHandler().removeCallbacks(mExecCommit);
15             mHost.getHandler().post(mExecCommit);
16         }
17     }
18 }

可以看到最開始傳進來的allowStateLoss在這里只做了檢查狀態的操作;

 1 private void checkStateLoss() {
 2     if (mStateSaved) {
 3         throw new IllegalStateException("Can not perform this action after onSaveInstanceState");
 5     }
 6     if (mNoTransactionsBecause != null) {
 7         throw new IllegalStateException("Can not perform this action inside of " + mNoTransactionsBecause);
 9     }
10 }


如果activity的狀態被保存了,這里再提交就會檢查這個狀態,符合這個條件就拋出一個異常來終止應用進程。也就是說在activity調用了onSaveInstanceState()之后,再commit一個事務就會出現該異常。那如果不想拋出異常,也可以很簡單調用commitAllowingStateLoss()方法來略過這個檢查就可以了,但是這是危險的,如果activity隨后需要從它保存的狀態中恢復,這個commit是會丟失的。因此它僅僅適用在ui狀態的改變對用戶來說是可以接受的,允許丟失一部分狀態。

總結

  1. 在Activity的生命周期方法中提交事務要小心,越早越好,比如onCreate。盡量避免在onActivityResult()方法中提交。
  2. 避免在異步的回調方法中執行commit,因為他們感知不到當前Activity生命周期的狀態。
  3. 使用commitAllowingStateLoss()代替commit()。相比於異常crash,UI狀態的改變對用戶來說是可以接受的。
  4. 如果你需要在Activity執行完onSaveInstanceState()之后還要進行提交,而且不關心恢復時是否會丟失此次提交,那么可以使用 commitAllowingStateLoss()commitNowAllowingStateLoss()

二、 commitNow以及commitNowAllowingstateLoss()

  在API_24版本FragmentTranslation里添加了該兩個方法:

       下面拿commitNow為例: 

1   public void commitNow() {
2         this.disallowAddToBackStack();
3         this.mManager.execSingleAction(this, false);
4     }

  該方法不支持加入BackStack回退棧中,disallowAddToBackStack()。

  源碼沒有再使用Handler,而是直接執行(源碼如下)  

 1 public void execSingleAction(FragmentManagerImpl.OpGenerator action, boolean allowStateLoss) {
 2         if (!allowStateLoss || this.mHost != null && !this.mDestroyed) {
 3             this.ensureExecReady(allowStateLoss);
 4             if (action.generateOps(this.mTmpRecords, this.mTmpIsPop)) {
 5                 this.mExecutingActions = true;
 6 
 7                 try {
 8                     this.removeRedundantOperationsAndExecute(this.mTmpRecords, this.mTmpIsPop);
 9                 } finally {
10                     this.cleanupExec();
11                 }
12             }
13 
14             this.doPendingDeferredStart();
15             this.burpActive();
16         }
17     }

  官方更推薦使用commitNow()commitNowAllowingStateLoss()來代替先執行commit()/commitAllowingStateLoss()


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM