android Fragment的commit与commitAllowingStateLoss

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了android Fragment的commit与commitAllowingStateLoss相关的知识,希望对你有一定的参考价值。

参考技术A 通常使用FragmentTransaction来add、replace、remove需要操作的fragment,然后通过commit或commitAllowingStateLoss应用这些变化。

关于Fragment的commit与commitAllowingStateLoss,有什么差别呢?
比如:

commit() 和 commitAllowingStateLoss() 的具体实现是在BackStackRecord这个类里,
如下:

mManager.enqueueAction(this, allowStateLoss) 则在FragmentManager中,在此进行了状态检查 checkStateLoss() ,如下:

DialogFragment的 dismiss() 与 dismissAllowingStateLoss() 也是同理,最终也是通过FragmentTransaction的 commit 与 commitAllowingStateLoss 来操作。

Fragment装载事务的各种commit()

FragmentTransaction 中的 Commit 方法

  • commit():int

  • commitAllowingStateLoss():int

  • commitNow():void

  • commitNowAllowingStateLoss():void

喜欢直接看官网介绍的也可以。这里我们就直接阐述结果了,仅做参考,各人理解也不一定对。

commit() vs commitAllowingStateLoss()

用commit()提交有时候会遇到IllegalStateException("Can not perform this action after onSaveInstanceState"), 说你在onSaveInstanceState()之后提交, onSaveInstanceState() 方法在 onStop() 方法之前执行,确保 Activity 因为各种原因被系统杀掉后能正确的保存和恢复状态,onSaveInstanceState() 方法 和 onPause() 方法之间没有必然的执行先后顺序。

commit()和commitAllowingStateLoss()在实现上唯一的不同就是当你调用commit()的时候, FragmentManger会检查是否已经存储了它自己的状态, 如果已经存了, 就抛出IllegalStateException.

那么如果你调用的是commitAllowingStateLoss(), 并且是在onSaveInstanceState()之后, 你可能会丢失掉什么状态呢?

答案是你可能会丢掉FragmentManager的状态, 即save之后任何被添加或被移除的Fragments.

举例说明:

  1. 在Activity里显示一个FragmentA;

  1. 然后Activity退到后台, onStop()和onSaveInstanceState()被调用;

  1. 在某个事件触发下, 你用FragmentB replace FragmentA , 使用的是 commitAllowingStateLoss().

这时候, 用户再返回应用, 可能会有两种情况发生:

  1. 如果系统杀死了你的activity, 你的activity将会重建, 使用了上述步骤2保存的状态, 所以A会显示, B不会显示;

  1. 如果系统没有杀死你的activity, 它会被提到前台, FragmentB就会显示出来, 到下次Activity stop的时候, 这个包含了B的状态就会被存下来.

(上述测试可以利用开发者选项中的"Don't Keep Activities”选项).

那么你要选择哪一种呢? 这就取决于你提交的是什么, 还有你是否能接受丢失。

commit(), commitNow() 和 executePendingTransactions()

使用commit()的时候, 一旦调用, 这个commit并不是立即执行的, 它会被发送到主线程的任务队列当中去, 当主线程准备好执行它的时候执行.

popBackStack()的工作也是这样, 发送到主线程任务队列中去. 即它们都是异步的.

但是有时候你希望你的操作是立即执行的, 之前的开发者会在commit()调用之后加上 executePendingTransactions()来保证立即执行, 即变异步为同步.

support library从v24.0.0开始提供了 commitNow()方法, 之前用executePendingTransactions()会将所有pending在队列中还有你新提交的transactions都执行了, 而commitNow()将只会执行你当前要提交的transaction. 所以commitNow()能避免你会不小心执行了那些你可能并不想执行的transactions.

但是你不能对加在back stack中的transaction使用commitNow(), 即addToBackStack()和commitNow()不能同时使用.

为什么呢?

想想一下, 如果你有一个提交使用了commit(), 紧接着又有另一个提交使用了commitNow(), 两个都想加入back stack, 那back stack会变成什么样呢? 到底是哪个transaction在上, 哪个在下? 答案将是一种不确定的状态, 因为系统并没有提供任何保证来确保顺序, 所以系统决定干脆不支持这个操作.

前面提过popBackStack()是异步的, 所以它同样也有一个同步的兄弟popBackStackImmediate().

所以实际应用的时候怎么选择呢?

如果你需要同步的操作, 并且你不需要加到back stack里, 使用commitNow().
support library在FragmentPagerAdapter里就使用了commitNow()来保证在更新结束的时候, 正确的页面被加上或移除.
如果你操作很多transactions, 并且不需要同步, 或者你需要把transactions加在back stack里, 那就使用commit().
如果你希望在某一个指定的点, 确保所有的transactions都被执行, 那么使用executePendingTransactions().

以上是关于android Fragment的commit与commitAllowingStateLoss的主要内容,如果未能解决你的问题,请参考以下文章

android的Fragment切换时如何保存状态?

android的Fragment切换时如何保存状态?

Android开发-Crimina

android多个fragment返回键层层返回

android 多个Fragment切换时的注意事项

Android 怎么将fragment 动态地添加到布局中的任意位置?