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

  • 相关阅读:
    MySQL DNS反查导致连接缓慢
    重写官方TodoList,对于初学react+redux的人来说,很有好处
    第一个博客试一试
    需求分析
    原来==的优先级比&高 (转自地址为http://blog.163.com/cynicly@126/blog/static/1206105820103893715302/的网易博客)
    E: 无法获得锁 /var/lib/apt/lists/lock open (11 Resource temporarily unavailable)
    我的博客园终于开通啦!
    (转自CSDN日新为道的专栏)printf的格式控制符,在CSDN看到的,转在这里
    qrc_image.cpp未找到
    QT打开保存的html文件无法显示图片
  • 原文地址:https://www.cnblogs.com/linghu-java/p/12176933.html
Copyright © 2020-2023  润新知