FyListen——生命周期监听器(设计原理之理解生命周期)
FyListen——生命周期监听器(设计原理之理解生命周期)
FyListen 的核心原理有两个:
- 通过子Fragment对Activity、Fragment进行生命周期监听
- Java8 接口特性 default
1. 什么是上下文Context
这是一个装饰器模式, ContextImpl 是 Context 的基础实现类, ContextWrapper 是对 ContextImpl 的包装类。ContextThemeWrapper 是对装饰器的再装饰,又增加了一些功能。
1.1 如何获取 Context?
获取 Context 的方法有很多:getContext(), getBaseContext(), getApplication(), getApplicationContext()。我们来看一下他们的区别:
getApplication()和 getApplicationContext() 获取到的就是同一个对象,只是使用的范围不一样:
- getApplication() 只有 Activity 和 Service 才有。
- 想要在其他地方获取 Application 只能通过 getApplicationContext().
getContext(), getBaseContext()和 getApplicationContext() 有什么区别?
-
Activity,Service 和 Application 都有 getBaseContext(),getApplicationContext() 这两个方法,但没有 getContext() 方法。
-
getContext() 方法只有在 Fragment 中才有,获取的是寄主对象,也就是 Activity。
所以 Fragment 必须依赖于 Activity 存在
1.2 Application 有什么用?
Application 是全局单例,可以通过 getApplication() 和 getApplicationContext() 获取。
需要注意的是,如果在 application 中调用 context 的方法,必须在 attachBaseContext() 之后,在此之前 context 是没有赋值的。
2. Activity 与 Fragment 生命周期绑定原理
先来看一张Fragment状态转移图:(图来自稀土掘金)
这张图的解读可以看到下面这张表,Activity 管理 Fragment 生命周期的方式是在 Activity 的生命周期方法中调用 FragmentManager 的对应方法,通过 FragmentManager 将现有的 Fragment 迁移到下一个状态,同时触发相应的生命周期函数:
Activity生命周期函数 | FragmentManager触发的方法 | Fragmet你状态转移 | Fragment生命周期回调 |
---|---|---|---|
onCreate() | dispatchCreate | INITIALIZING->CREATED | onAttach()、onCreate() |
dispatchActivityCreated() | CREATED->ACTIVITY_CREATED | onCreateView()、onActivityCreated()、 | |
onStart() | dispatchStart | ACTIVITY_CREATED->STARTED | onStart() |
onResume() | dispatchResume | STARTED->RESUMED | onResume() |
onPause | dispatchPause | RESUMED->STARTED | onPause() |
onStop | dispatchStop | STARTED->STOPPED | onStop() |
onDestroy | dispatchDestroy | STOPPED->ACTIVITY_CREATED->CREATED->CREATED->INITIALIZING | onDestroyView()、onDestroy()、onDetach() |
我们从源码角度来看一下是如何绑定的生命周期,首先我们需要知道,Activity 生命周期是由 AMS(ActivityManagerService)管理、回调的,这部分请大家在 AMS 专题学习。便于理解,我们提前看到源码中关于Fragment的状态转移代码处理:
需要提醒的Java基础是,switch: 一旦switch表达式与某个case分支匹配,则从该分支的语句开始执行,一致执行下去!其后所有case分支的语句也会被执行,直到遇到break语句!!!
//[FragmentManager.java]
void moveToState(Fragment f, int newState, int transit, int transitionStyle,boolean keepActive) {//newState是Fragment将要被转移到的状态state//f.mState是Fragment现有的状态stateif(f.mState <= newState){switch(f.mState){case Fragment.INITIALIZING://Fragment当前状态还在INITIALIZING,Activity调用了onCreate(),newState是CREATED,Fragment进行状态转移if(newState > Fragment.INITIALIZING){f.onAttach();dispatchOnFragmentAttached();if(!f.mIsCreated){//如果fragment还没create()f.performCreate();dispatchOnFragmentCreated();}else{//如果fragment过去已经create()过了,只不过是detach()了,并没有销毁。也就是这个Fragment虽然生命结束了,但后来又被加载使用了(复用了)f.mState = Fragment.CREATED;}}case Fragment.CREATED:if(newState > Fragment.CREATED){//Fragment当前状态在CREATED,但Activity紧接着又将状态调整为ACTIVITY_CREATED,Fragment进行状态转移f.mView = f.performCreateView();if(f.mView!=null){//如果没有view,就不往它的子Fragment进行生命周期回调分发了dispatchOnFragmentViewCreated();}f.performActivityCreated();dispatchOnFragmentActivityCreated();}case Fragment.ACTIVITY_CREATED:if(newState > Fragment.ACTIVITY_CREATED){//只进行状态转移f.mState = Fragment.STOPPED;}case Fragment.STOPPED:if(newState > Fragment.STOPPED){f.performStart();dispatchOnFragmentStarted();}case Fragment.STARTED:if(newState > Fragment.STARTED){f.performResume();dispatchOnFragmentResumed();}}}else if(f.mState > newState){//反向转移switch(f.mState){case Fragment.RESUMED:if(newState < Fragment.RESUMED){f.performPause();dispatchOnFragmentPaused();}case Fragment.STARTED:if(newState < Fragment.STARTED){f.performStop();dispatchOnFragmentStopped();}case Fragment.STOPPED:case Fragment.ACTIVITY_CREATED:if(newState < Fragment.ACTIVITY_CREATED){f.performDestroyView();dispatchOnFragmentViewDestroyed();}case Fragment.CREATED:if(newState < Fragment.CREATED){f.performDestroy();dispatchOnFragmentDestroy();}f.performDetach();dispatchOnFragmentDetached();}if(f.mState != newState){f.mState = newState;}}
}
我们了解了 Fragment 的生命周期是由于 Activity 生命周期的回调,设定了 Fragment 应当到的 newState,然后让 Fragment 进行状态转移,转移的过程中回调 Fragment 的生命周期。所以我们主要需要看 Activity 是如何设置 newState 的。从 AMS 回调 Activity 的 performXXX() 看起:
2.1 AMS->Activity.performCreate()
- 在 Activity 的 onCreate() 中将 Fragment 的状态转移目标 newState 设置为了 CREATED,状态转移期间回调了Fragment的 onAttach() 和 onCreate()
- performCreate()又将 Fragment 的状态转移目标newState设置为了 ACTIVITY_CREATED,Fragment在状态转移期间回调了Fragment的 onCreateView() 和 onActivityCreate()
//[Activity.java]
final void performCreate(Bundle icicle, PersistableBundle persistentState) {//调用Activity的onCreate()方法,其中回调了Fragment的onAttach()和onCreate()方法if (persistentState != null) {onCreate(icicle, persistentState);} else {onCreate(icicle);}//调用到moveToState,且newState为Fragment.ACTIVITY_CREATED,状态转移过程中,回调了Fragment的onCreateView()和onActivityCreate().mFragments.dispatchActivityCreated();
}protected void onCreate(@Nullable Bundle savedInstanceState) {//调用到moveToState,且newState为Fragment.CREATED,在状态转移期间回调了Fragment的onAttach()和onCreate()方法mFragments.dispatchCreate();//Application的Lifecycle回调dispatchActivityCreated(savedInstanceState);
}
2.2 AMS->performStart()
- 调用了 Activity 的 onStart()
- 将Fragment的状态转移目标newState设置为了STARTED,Fragment在期间回调了Fragment的 onStart() 方法
//[Activity.java]
final void performStart(String reason){//回调了 activity.onStart(),其中进行Application的Lifecycle回调mInstrumentation.callActivityOnStart(this);//调用到moveToState,且newState为Fragment.STARTED,其中回调了onStart()方法。虽然在switch中将Fragment的状态停留在了STOPPED,但在moveToState()方法的最后,又将Fragment的状态设置到了STARTED。mFragments.dispatchStart()
}
2.3 AMS->performResume()
- 调用了 Activity 的 onResume()
- 将Fragment的状态转移目标newState设置为了RESUMED,Fragment在状态转移期间回调了Fragment的 onResume();
//[Activity.java]
final void performResume(boolean followedByPause, String reason){//如果activity之前不是started的,会进入此方法,确保将activity变为startperformRestart(true,reason);//如果activity之前是start的://回调activity.onResume(),里面回调了 Application的LifecyclemInstrumentation.callActivityOnResume(this);//调用到moveToState,且newState为Fragment.RESUMED,状态转移期间回调了Fragment的onResume()方法mFragments.dispatchResume();
}
2.4 AMS->performPause()
【注意!!!】我们发现,从这里开始,是先从Fragment开始状态转换,而且是往回转移,newState<f.mState,然后才调用Activity的生命周期回调
- 注意,状态转移设为newState=STARTED,开始往回转移!!!
- Fragment状态转移期间回调了onPause()方法
- 然后才调用Activity的onPause()方法
//[Activity.java]
final void performPause(boolean preserveWindow, String reason) {//在此之前状态为RESUMED,由于f.mState<newState,将进行往回状态转移!//Fragment状态转移到STARTED期间回调了Fragment的onPause()mFragments.dispatchPause();//调用Activity的onPause(),同时回调Application的LifecycleonPause();
}
2.5 AMS->performStop()
【注意!!!】先从Fragment开始状态转换,而且是往回转移,newState<f.mState,然后才调用Activity的生命周期回调
- 注意,状态转移目标设为newState = STOPPED,往回状态转移
- Fragment状态转移期间回调了 onStop()方法
- 然后才调用Activity的onStop()方法
//[Activity.java]
final void performStop(boolean preserveWindow, String reason) {//Fragment状态转移到STOPPED,往回状态转移期间,回调了Fragment的onStop()方法mFragments.dispatchStop();//其中回调了Activity的onStop(),同时回调Application中的监听LifecyclemInstrumentation.callActivityOnStop(this);
}
2.6 AMS->performRestart()
Activity可能会在onStop()之后又onRestart(),需要注意的是,这里并没有回调Fragment的生命周期,只调用了activity的onRestart(),并调用performStart(),后续的生命周期回调就和onStart()之后的一致了
//[Activity.java]
final void performRestart(boolean start, String reason) {//先判断之前是否为Stop状态if(mStopped){mStopped = false;//调用Activity的onRestart()回调mInstrumentation.callActivityOnRestart(this);//里面调用了Activity的onStart(),并进行Fragment状态转移与生命周期回调performStart();}
}final void performStart(String reason) {//调用Activity.onStart(),并回调Application的Lifecycle,通知Application这个activity的生命周期现状mInstrumentation.callActivityOnStart(this);//newState=STARTED,Fragment状态从STOPPED到STARTED状态转移,期间回调了Fragment的onStart()mFragments.dispatchStart();
}
2.7 AMS->performDestroy()
【注意!!!】先从Fragment开始状态转换,而且是往回转移,newState<f.mState,然后才调用Activity的生命周期回调
- 先进行Fragment状态转移,newState=INITIALIZING,期间回调了Fragment的onDestroyView,onDestroy,onDetach
- Fragment状态转移完成,才进行Activity的onDestroy()
//[Activity.java]
final void performDestroy(){//先进行Fragment状态转移,newState=INITIALIZING,期间回调了Fragment的onDestroyView,onDestroy,onDetachmFragments.dispatchDestroy();//Fragment状态转移完成,才进行Activity的onDestroy()onDestroy();
}
我们回到 moveToState 来看一下是怎么连着回调三个Fragment生命周期的,需要注意,此时f.mState = STOPPED,newState=INITIALIZING:
需要提醒的Java基础是,switch: 一旦switch表达式与某个case分支匹配,则从该分支的语句开始执行,一致执行下去!其后所有case分支的语句也会被执行,直到遇到break语句!!!
//[FragmentManager.java]
void moveToState(Fragment f, int newState, int transit, int transitionStyle,boolean keepActive) {if(f.mState 《= newState){//...}else if(f.mState > newState){switch(f.mState){//...case Fragment.STOPPED://分支判断成功,继续执行后续case分支语句,直到遇到breakcase Fragment.ACTIVITY_CREATE:if(newState < Fragment.ACTIVITY_CREATED){//1. 回调Fragment的onDestroyView()f.performDestroyView();//把该事件也分发给f的子FragmentdispatchOnFragmentViewDestroyed();}case Fragment.CREATED:if(newState < Fragment.ACTIVITY_CREATED){//2. 回调Fragment的onDestroy()f.performDestroy();//把该事件也分发给f的子FragmentdispatchOnFragmentDestroyed();//3. 回调Fragment的onDetach()f.performDetach();//把该事件也分发给f的子FragmentdispatchOnFragmentDetached(); }//switch end}}//状态转移完成,最终更新f.mState = INITIALIZINGif(f.mState != newState){f.mState = newState;}
}
至此,我们分析完了生命周期回调。如果你比较细心,会发现这里除了处理fragment的生命周期,还有的时候会处理fragment的动画和view的显示。
2. 补充:Fragment生命周期除了自动由 Activity 来回调,也可以由用户操控 FragmentTansaction 进行 Fragment 的调度时候回调生命周期:
先来看这张图:(图源稀土掘金)
我们经常使用 FragmentTransaction 中的 add()、remove()、replace()、attach()、detach()、hide()、show() 等方法对 Fragment进行操作,这些方法都会使 Fragment 的状态发生变化,出发对应的生命周期函数。默认 Activity 处于 Resume 状态:
- add/remove 操作会引起 Fragment 在 INITIALIZING 和 RESUMED 这两个状态之间迁移
- attach/detach操作会引起 Fragment 在 CREATED 和 RESUMED 两个状态之间迁移
add() 需要注意的是,如果Activity处于 STARTED 状态,Fragment 是无法进入 RESUMED 状态的,只有当 Activity 进入 RESUME 状态才会通知 Fragment 进入 RESUMED。
hide/show方法内部其实调用了 FragmentTransaction 的add/remove 方法。
3. Fragment 与 子Fragment 生命周期绑定原理
我们观察到 Fragment 中也持有一个 FragmentManager:mChildFragmentManage 用于管理子Fragment。所以在生命周期事件分发过程中,会呈树形结构向所有子Fragment进行分发:
//[Fragment.java]
public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListener {//上层管理当前Fragment的FragmentManagerFragmentManagerImpl mFragmentManager;//当前Fragment用于管理子Fragment的fm,在生命周期回调中,会同时分发给 MChildFragmentManager 中的所有子FragmentFragmentManagerImpl mChildFragmentManager;//所属的父FragmentFragment mParentFragment;
}
FragmentManager 中管理着 Fragments:mAdds 和 mActive:
//[FragmentManager.java]
final class FragmentManagerImpl extends FragmentManager implements LayoutInflater.Factory2 {//当前活跃的FragmentSparseArray<Fragment> mActive;//所有已添加的Fragmentfinal ArrayList<Fragment> mAdded = new ArrayList<>();//管理的其他信息ArrayList<BackStackRecord> mBackStack;ArrayList<Fragment> mCreatedMenus;//...//分发生命周期:以dispatchResume()为例public void dispatchResume(){mStateSaved = false;//进行Fragment的状态转移dispatchMoveToState(Fragment.RESUMED);}//其中通过moveToState先进行预处理,即找到所有当前FragmentManager所管理的Fragment进行逐个事件分发private void dispatchMoveToState(int state) {if (mAllowOldReentrantBehavior) {moveToState(state, false);} else {try {mExecutingActions = true;moveToState(state, false);} finally {mExecutingActions = false;}}execPendingActions();}//拿到 mAdds 和 mActive 中的Fragment去分发事件(逐个让他们去进行状态转移)//注意这个是两个参数的 moveToState(),真正状态转移的moveToState()方法是四个参数的,注意区分void moveToState(int newState,boolean always){if (mActive != null) {final int numAdded = mAdded.size();for (int i = 0; i < numAdded; i++) {Fragment f = mAdded.get(i);//逐个fragment进行状态转移moveFragmentToExpectedState(f);}final int numActive = mActive.size();for (int i = 0; i < numActive; i++) {Fragment f = mActive.valueAt(i);if (f != null && (f.mRemoving || f.mDetached) && !f.mIsNewlyAdded) {//逐个fragment进行状态转移moveFragmentToExpectedState(f);}}}}//moveFragmentToExceptedState()通过四个参数的moveToState()让fragment进行状态转移void moveFragmentToExceptedState(final Fragment f){int nextState = mCurState;//新状态被标记在成员变量,不作为参数传入,简化代码//进行状态转移,具体的我们之前已经看过了moveToState(f, nextState, f.getNextTransition(), f.getNextTransitionStyle(), false);}}
4. Activity与Fragment的树形结构
既然我们已经知道了 Fragment 的生命周期可以由 Activity 或者 Fragment 进行回调,那么我们就可以通过这个特性进行 Activity 或者 父Fragment生命周期的监听!!!主流框架 Glide 中生命周期监听的方式,也是利用了这个特性。我画了两张图,大家先感性地认知一下 Fragment 监听 Activity 生命周期的构造:
Activity与Fragment的树形结构:
我们可以看到,这是一个可以找到父节点的多叉树。
fragment想要与Activity通信可以通过Fragment中的 FragmentHostCallback类型对象mHost:里面存了Fragment所依存的Activity实例、主线程Handler。Fragment中启动Activity,也是通过这个对象,让上层去执行启动Activity的请求。
5. 使用 Java8 接口特性 default 进行外观设计
java8 之前,往接口里新加一个方法,那么所有的实现类都需要变动,都需要同步实现这个方法。
java8 给接口新增了两个关键字:default static
使得可以往接口里添加默认方法,子类可以无需变动。
同时,这也使得接口中的方法并不需要全都实现,只需要重写需要的方法即可!