文中的源代码基于Support包27.1.1版本
1 Fragment生命周期
大家都知道Fragment
的生命周期,以及其对应的一些生命周期函数:
Fragment
的生命周期函数很多,但其实Fragment
中只定义了6种状态
1 | 复制代码static final int INITIALIZING = 0; // Not yet created. |
Fragment
的整个生命周期一直在这6个状态中流转,调用对应的生命周期方法然后进入下一个状态,如下图
1.1 Fragment与Activity
Fragment
的生命周期与Activity
的生命周期密切相关Activity
管理Fragment
生命周期的方式是在Activity
的生命周期方法中调用FragmentManager
的对应方法,通过FragmentManager
将现有的Fragment
迁移至下一个状态,同时触发相应的生命周期函数
Activity生命周期函数 | FragmentManager触发的函数 | Fragment状态迁移 | Fragment生命周期回调 |
---|---|---|---|
onCreate | dispatchCreate | INITIALIZING->CREATED | onAttach、onCreate |
onStart | dispatchStart | CREATED->ACTIVITY_CREATED->STOPPED->STARTED | onCreateView、onActivityCreated、onStart |
onResume(准确来讲是onPostResume) | dispatchResume | STARTED->RESUMED | onResume |
onPause | dispatchPause | RESUMED->STARTED | onPause |
onStop | dispatchStop | STARTED->STOPPED | onStop |
onDestroy | dispatchDestroy | STOPPED->ACTIVITY_CREATED->CREATED->INITIALIZING | onDestroyView、onDestroy、onDetach |
上个图更加清晰:
1.2 Fragment与FragmentTransaction
我们经常使用FragmentTransaction
中的add
、remove
、replace
、attach
、detach
、hide
、show
等方法对Fragment
进行操作,这些方法都会使Fragment
的状态发生变化,触发对应的生命周期函数
(假设此时Activity
处于RESUME
状态)
FragmentTransaction中的方法 | Fragment触发的生命周期函数 |
---|---|
add | onAttach->onCreate->onCreateView->onActivityCreated->onStart->onResume |
remove | onPause->onStop->onDestoryView->onDestory->onDetach |
replace | replace可拆分为add和remove, |
detach | (在调用detach之前需要先通过add添加Fragment)onPause->onStop->onDestoryView |
attach | (调用attach之前需要先调用detach)onCreateView->onActivityCreated->onStarted->onResumed |
hide | 不会触发任何生命周期函数 |
show | 不会触发任何生命周期函数 |
通过对Fragment
生命周期的变化的观察,我们可以很容易发现,add/remove
操作会引起Fragment
在INITIALIZING
和RESUMED
这两个状态之间迁移。
而attach/detach
操作会引起Fragment
在CREATED
和RESUMED
这两个状态之间迁移。
注:add函数这里有一个需要注意的点,如果当前Activity处于
STARTED
状态,Fragment是无法进入RESUMED
状态的,只有当Activity进入RESUME
状态,然后触发onResume
->FragmentManager.dispatchStateChange(Fragment.RESUMED)
,然后调用Fragment.onResume
函数之后Fragment
才会进入RESUMED
状态。
1.3 Fragment与ViewPager
通过FragmentPagerAdapter
我们可以将Fragment
与ViewPager
结合起来使用,那么ViewPager
中的Fragment
的生命周期又是怎样的呢?
其实也简单,FragmentPagerAdapter
内部其实就是通过FragmentTransaction
对Fragment
进行操作的,主要涉及add
、detach
、attach
这三个方法。
1 | 复制代码@SuppressWarnings("ReferenceEquality") |
通过上述源码可知,FragmentPagerAdapter
通过FragmentTransaction.add
方法添加Fragment
,后续通过attach
和detach
来操作。这些方法对应的生命周期我们可以参照上面的图即可。
我们举例来模拟一下看看,假设有ViewPager
有5个页面,以及offscreenPageLimit为1,
- 第一次加载时,第一第二页通过
add
函数被加载,处在RESUMED
状态 - 滑动到第二页,第三页被加载,也是通过
add
函数被加载的,处在RESUMED
状态 - 继续滑动到第三页,此时第一页通过
detach
函数被回收,处在CREATED
状态,同时第四页通过add
被加载处于RESUMED
状态 - 滑动到第二页,此时第一页通过
attach
被加载,处于RESUMED
状态,第四页被detach
处于CREATED
状态
总结:ViewPager
中当前页与当前页左右两页都处于RESUMED
状态,其他页面要么未被创建,要么处于CREATED
状态,滑动过程中Fragment
的生命周期变化我们可以通过上面这个例子得到。
1.4 Fragment与DialogFragment
在使用DialogFragment
的时候我们习惯使用它提供的show
、hide
方法进行显示或者隐藏。这两方法内部其实使用了FragmentTransaction
的add
、remove
方法,这些方法对应的生命周期我们已经讲过了就不在赘述了。
1 | 复制代码public void show(FragmentManager manager, String tag) { |
DialogFragment
比较特别的是内部还维护了一个Dialog
,DialogFragment
设计之初就是使用FragmentManager
来管理Dialog
,主要使用了Dialog
的show
、hide
、dismiss
这三个方法。对应关系如下
Fragment生命周期函数 | 对应的Dialog的方法 |
---|---|
onStart | show |
onStop | hide |
onDestoryView | dismiss |
2 不同的添加方式对Fragment的生命周期有什么影响
Fragment
的添加方式有两种:
- 通过在xml文件中使用fragment标签添加
- 在代码中使用
FragmentTransaction
添加
这里我们就来聊聊,这两种不同的添加方式对于Fragment
的生命周期回调会产生什么样的影响。
2.1 使用fragment标签添加
xml中的Fragment的实例创建最终会交由FragmentManager负责,方法为onCreateView
1 | 复制代码//FragmentManager.java |
onCreateView
的工作基本上就是创建Fragment
实例并将其迁移至指定状态了,我们以一个Activity
正常启动的流程作为分析的场景,那么此时Fragment
将最终进入CREATED
状态。
在前面学习Fragment
生命周期的时候,我们有提到过Activity
进入onCreate
之后会触发Fragment
的onAttach
和onCreate
的生命周期回调。但在当前这种场景下,Fragment
会提前触发onCreateView
来创建视图,这一点可以在moveToState
的源码中得到印证:
1 | 复制代码void moveToState(Fragment f, int newState, int transit, int transitionStyle, |
2.2 在代码中使用FragmentTransaction添加
此处我们以在Activity.onCreate
方法中add一个Fragment作为分析场景
1 | 复制代码public class DemoActivity extends FragmentActivity{ |
先不管add
里面进行了什么操作,我们知道如果不调用commit方法,那么add操作是不会起效的的。commit
方法会经历以下调用链commit
->commitInternal
->FragmentManager.enqueueAction
1 | 复制代码//FragmentTransaction的实现类为BackStackRecord |
当mExecCommit
被触发就会经历下面的调用链FragmentManager.execPendingActions
->BackStackRecord.generateOps
->
…->BackStackRecord.executeOps
->FragmentManager.xxxFragment
->FragmentManager.moveToState
最终发生了Fragment
的状态迁移
那么mExecCommit
是否真的就老老实实待在消息队列中等待被执行呢?答案是否定的。
我们来看看FragmentActivity.onStart
方法
1 | 复制代码protected void onStart() { |
可以看到,execPendingActions
被提前触发了,再搭配下面的dispatchStart
,那么Fragment
将从INITIALIZING
一下子迁移至STARTED
(execPendingActions
方法触发后会将mExecCommit
从消息队列中移除)。FragmentActivity
在onStart
、onResume
和onPostResume
生命周期回调中都会调用FragmentManager.execPendingActions
,因此当我们在Activity.onStart
、Activity.onResume
中通过代码添加Fragment
时,Fragment
的状态迁移分别会发生在Activity.onResume
、Activity.onPostResume
之后。
那么在onPostResume
之后再添加Fragment会发生什么呢?
此时由于onPostResume
方法中的FragmentManager.execPendingActions
已经在super
中调用过了,因此mExecCommit
将会被触发,这里有一个最大的不同点就是Fragment
的生命周期变化与Activity
的生命周期变化不处于同一个消息周期。
2.3 总结
我们以一张图对本节内容进行总结:
28.0.0版本的support包中移除了
STOPPED
状态,但是经过测试,其生命变化与上图保持一致
本文转载自: 掘金