选择正确的 Fragment#commitXXX() 函数
slpm7993
8年前
<p>最新版本(v24.0.0)的 Support v4 库中的 FragmentTransaction 添加了 commitNow() 和 commitNowAllowingStateLoss () 两个函数,这样 提交一个 Fragment 就有如下4个函数可以选择:</p> <p>– <a href="/misc/goto?guid=4959713300886604276" rel="nofollow,noindex">commit()</a></p> <p>– <a href="/misc/goto?guid=4959713300981862593" rel="nofollow,noindex">commitAllowingStateLoss()</a></p> <p>– <a href="/misc/goto?guid=4959713301064798456" rel="nofollow,noindex">commitNow()</a></p> <p>– <a href="/misc/goto?guid=4959713301151334025" rel="nofollow,noindex">commitNowAllowingStateLoss()</a></p> <p>另外,在使用 Fragment 的过程中,可能您已经使用过了 <a href="/misc/goto?guid=4959713301231351287" rel="nofollow,noindex">executePendingTransactions()</a> 这个函数了。</p> <p>下面来深入分析下每个函数是干啥用的,你应该使用哪个函数。</p> <p>commit() vs commitAllowingStateLoss()</p> <p>大部分使用 Fragment 的开发者可能都遇到过如下的异常:</p> <p>java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState</p> <p><a href="/misc/goto?guid=4959713301313377561" rel="nofollow,noindex">Alex Lockwood</a> 写过一篇文章来详细解释为何会出现该异常。但是开发者更多的是想知道他们的应用出现了该问题意味着什么?</p> <p>commit() 和 commitAllowingStateLoss() 的实现几乎是一样的。唯一的区别就是在调用 commit() 的时候 FragmentManager 会检查是否已经保存了其状态,如果状态已经保存了,则就抛出 IllegalStateException 异常。</p> <p>那么,在 onSaveInstanceState() 函数执行以后,您调用 commitAllowingStateLoss() 会丢失那些状态呢? 答案就是你可能丢失 FragmentManager 的状态,这里面包含在 onSaveInstanceState() 执行之后添加和删除的 Fragment。</p> <p>例如:</p> <p>1. 当前您的 Activity 在显示 FragmentA</p> <p>2. 您的 Activity 被切换到后台了((onStop() 和 onSaveInstanceState() 函数被调用了)</p> <p>3. 这个时候您的 Activity 的逻辑发生了一些变化,您使用 FragmentB 替换了 FragmentA 并调用了 commitAllowingStateLoss() 函数来提交这个变化。</p> <p>这个时候,当用户再次切回您的 Activity 的时候可能出现如下两种状态:</p> <ol> <li>如果系统内存不足并且杀死了您的应用,当用户重新打开您的 应用的时候,系统将会恢复您的应用到上面第二步的状态,而 FragmentB 是不会显示的。</li> <li>如果系统没有杀死您的应用,用户则可以看到 FragmentB。当 Activity 再次回到后台的时候(onStop), FragmentB 的状态才会被保存起来。</li> </ol> <p><a href="/misc/goto?guid=4959713301398677966" rel="nofollow,noindex">Github 上有个示例项目</a> 演示该情况。在运行该示例的时候,如果打开开发者选项中的 “Don’t Keep Activities” 设置,则可以用来显示第一种情况,FragmentB 的状态完全丢失了。 如果没有打开 “Don’t Keep Activities” 选项,则可以查看第二种情况。</p> <p>这两个函数使用哪个取决于您提交的 Fragment 和 该 Fragment 状态是否重要,如果状态丢失了也没关系,则您可以使用 commitAllowingStateLoss() 函数。</p> <p>commit(), commitNow(), 和 executePendingTransactions()</p> <p>其他版本的 commit() 指定了提交发生的时机。 commit() 的文档有如下说明:</p> <p>安排一个针对该事务的提交。提交并没有立刻发生,会安排到在主线程下次准备好的时候来执行。 (Schedules a commit of this transaction. The commit does not happen immediately; it will be scheduled as work on the main thread to be done the next time that thread is ready.)</p> <p>上面文档说明的意思是,你可以同时执行多次提交,这些提交都没有立刻执行,知道下次主线程准备好了才一起执行这些提交的 Fragment。这些针对 Fragment 的提交操作包含 添加、删除、替换以及通过函数 popBackStack() 来返回前一个 Fragment。</p> <p>有时候,您需要针对 Fragment 的操作立刻执行。之前都是通过在调用函数 commit() 后调用 executePendingTransactions() 来实现的。</p> <p>在 24.0.0 版本的 Support 库中添加了 commitNow() 函数来更好的支持这种操作。commitNow() 只同步的执行当前的提交操作,而 executePendingTransactions() 则会执行所有等得执行的操作。 commitNow() 可以避免您执行之前提交的但是无需立刻执行的操作。</p> <p>commitNow() 有个限制,无法把当前提交的 Fragment 添加到堆栈(back stack)中。假设有如下的情况:</p> <p>通过 commit() 函数把 Fragment A 添加到堆栈中,然后立刻使用 commitNow() 把另外一个 Fragment B 添加到堆栈中,这样当前的堆栈应该是何种状态? 是 Fragment A 在前面还是 Fragment B 在前面呢?</p> <p>popBackStack() 和 popBackStackImmediate() 与 commit() 和 commitNow() 的情况是一样的。</p> <p>最后来总结下该如何选择这些函数:</p> <ul> <li>如果你需要同步提交 Fragment 并且无需添加到 堆栈 中,则使用 commitNow()。 Support 库中在 FragmentPagerAdapter 中使用这个函数,来确保更新 Adapter 的时候 正确的页面被添加和删除了。一般来说,只要不添加到堆栈中,都可以使用这个函数来提交。</li> <li>如果执行了多次提交,并且不需要是同步的,或者把每次提交都添加到 堆栈 中,那么就使用 commit()。</li> <li>如果 您需要把多次提交操作的同一个时间点一起执行,则使用 executePendingTransactions()</li> </ul> <p>From: <a href="/misc/goto?guid=4959713301485285531" rel="nofollow,noindex">Bryan Herbst</a></p> <p> </p> <p>来自:http://blog.chengyunfeng.com/?p=1016</p> <p> </p>