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()

posted @ 2020-01-10 16:47  linghu_java  阅读(7393)  评论(0编辑  收藏  举报