Android基础——控件的混合生命周期

Posted David-Kuper

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android基础——控件的混合生命周期相关的知识,希望对你有一定的参考价值。

一、Activity下的Fragment生命周期

android官方给出:基本情况下Activity与Fragment的生命周期对应关系如下图:

图1.0 静态布局情况下fragment与Activity的生命周期对应图

这个生命周期的严格对应关系成立条件为:fragment通过静态布局的方式插入到Activity的布局中。但是通过FragmentManager动态管理的时候就有些许差别了。


* FragmentManager动态管理Fragment的生命周期:*


这里演示的是动态加载的Fragment(通过FMManager),其实通过xml静态插入的Fragment基本生命周期根下面是一样的,有些许不同(如上图1.0⬆️)。不同的原因是Activity里面的onCreate()方法下会调用
到setContentView()加载静态布局,这个方法会按照深度优先的算法解析xml里面的View树结构,加载嵌套在里面的的Fragment以及View。而动态加载的Fragment的生命周期总体时依附于Activity,但是由于不会在
setContentView()中得到解析构造,因此生命周期前面部分(onAttach、onCreate、onCreateView、onActivityCreated()、onStart、onResume)未与用户交互的阶段,这些周期调用时机随着在Activity生命周期的不同时刻
通过FragmentManager加载会有不同的表现。

假设:Activity:A。 Fragment: F1、F2 通过FragmentManager 在onCreate()方法进行Fragment的add。

(1)Fragment自身生命周期

阶段一:启动时
A-onCreate()
F1-onAttach()
F1-onCreate()
F2-onAttach()
F2-onCreate()
F1-onCreateView()
F1-onViewCreated()
F1-onActivityCreated()
F2-onCreateView()
F2-onViewCreated()
F2-onActivityCreated()\\
F1-onStart()
F2-onStart()
A-onStart()
A-onResume()
F1-onResume()
F2-onResume()
A-onAttachedToWindow()
阶段二:按Home/菜单键
F1-onPause()
F2-onPause()
A-onPause()
F1- onSaveInstanceState()
F2-onSaveInstanceState()
A-onSaveInstanceState()
F1-onStop()
F2-onStop()
A-onStop()
阶段三:从阶段二返回
A-onRestart()
F1-onStart()
F2-onStart()
A-onStart()
A-onResume()
F1-onResume()
F2-onResume()
阶段四:直接按下Back键返回
F1-onPause()
F2-onPause()
A-onPause()
F1-onStop()
F2-onStop()
A-onStop()
F1-onDestroyView()
F1-onDestroy()
F1-onDetach()
F2-onDestroyView()
F2-onDestroy()
F2-onDetach()
A-onDestroy()
A-onDetachedFromWindow()。

可以看出,与图1.0所示的生命周期对应基本一样。

(2)Fragment自身生命周期对View生命周期的影响

任何一个View也是有生命周期的,从构造——>onFinishInflate()——>attachToWindow() ——>onMesure()——>onLayout()——>onDraw——>响应事件交互——>onDetachFromWindow()。

假设有两个View:V1、V2。V1在Activity A中,V2在Fragment F中。 View在xml布局中,Fragment通过FragmentManager加载。

情况一、在启动到可交互时:


//Activity创建
A-onCreate()——>
//Activity的xml静态布局中,按照深度优先顺序先对view解析
V1-onFinishInflate()——>
//创建Fragment
F1-onAttach()——>
F-onCreate()——>
//创建Fragment视图
F-onCreateView()——>
//解析Fragment里面的view视图
V2-onFinishInflate()——>
F1-onViewCreated()——>
F-onActivityCreated()——>
F-onStart()——>
A-onResume()——>
F-onResume()——>
//Activity、View将要界面将要绘制显示
A-onAttachedToWindow()——>
V1-onAttachedToWindow()——>
V2-onAttachedToWindow()——>
//View视图绘制、按照V1、V2加载顺序
V1-onMeasure()——>
V2-onMeasure()——>
V1-onLayout()——>
V2-onLayout()——>
V1-onDraw()——>
V2-onDraw()

情况二、当销毁时

//Fragment生命周期跟随Activity生命周期销毁变化而变化
F-onPause()——>
A-onPause()——>
F-onStop()——>
A-onStop()——>
//Fragment视图销毁
F-onDestroyView()——>
//由于Fragment是动态加载,因此view由FragmentManager管理,此时跟随Fragment视图一同销毁
V2-onDetachedFromWindow()——>
F-onDestroy()——>
F-onDetach()——>
A-onDestroy()——>
//由于V1是静态插入Activity布局文件里面,因此它的生命结束跟随Activity视图销毁而销毁
V1-onDetachedFromWindow()——>
A-onDetachedFromWindow()

总结:View在Activity中和在Fragment中有两个点不太一样:

  • inflate:简单来说,无论fragment是通过源代码FragmentManager添加还是通过xml布局插入,Activity的View在setContentView()时被Inflate,Fragment的View在onCreateView时被inflate;

  • onAttachedToWindow():这个View的生命周期的调用无论在fragment、Activity都一样,因为Activity才是整个界面的承载者和入口,因此Activity的onAttachedToWindow处于使得window处于前台之后,View的添加
    就会调调用自身的onAttachedToWindow

  • onDetachFromWindow():当销毁时,情况稍微不同:如果fragment、view都在xml文件中插入,则V1、V2都在Activity的onDetachedFromWindow时触发。但是当fragment时通过FragmentManager中添加的时候,Activity的View在Activity调用onDetachedFromWindow()时会触发,而Fragment里面的View会在Fragment的onDestroyView()时触发。


二、Activity+ ViewPager+ Fragment+ View的生命周期


ViewPager也是一个View,Fragment根ViewPager的结合其实也是通过FragmentManager来管理的,因此归结来说还是通过动态加载的方式来管理Fragment的生命周期。

假设:
含有ViewPager的Activity:A;
Fragment:F1、F2、F3、F4,通过FM交ViewPagerAdapter。
嵌在Fragment中的View:V1、V2、V3、V4

(1)启动Activity:

//Activity的启动
A-onCreate()——>
A-onStart()——>
A-onResume()——>
A-onAttachedToWindow()——>
//不一样的是在ViewPager显示后才加载Fragment
//按照getItem中的顺序加载
F1-onAttach()——>
F1-onCreate()——>
F2-onAttach()——>
F2-onCreate()——>
//加载F1视图
F1-onCreateView()——>
V1-onFinishInflate()——>
V1-onAttachedToWindow()——>
F1-onViewCreated()——>
F1-onActivityCreated()——>
F1-onStart()——>
F1-onResume()——>
//加载F2视图
F2-onCreateView()——>
V2-onFinishInflate()——>
V2-onAttachedToWindow()——>
F2-onViewCreated()——>
F2-onActivityCreated()——>
F2-onStart()——>
F2-onResume()——>
//按照F1、F2的顺序绘制V1、V2
V1-onMeasure()——>
V2-onMeasure()——>
V1-onLayout()——>
V2-onLayout()

从上面我们可以看出
第一,与fragment被添加至Acvitity的时候不一样,fragment的构造是在Activity调用onAttachedToWindow()之后;
第二,预先加载了一个fragment,并且生命周期执行到了onResume()。

(2)滑动Fragment翻页:

关于这个的全部情况就不放在上面了,下面的例子是使用FragmentPagerAdapter说明的,FragmentStatePagerAdapter在是否销毁不保存Fragment上有所不同。默认情况下FragmentPagerAdapter会预加载/保留包括当前fragment的前后3个fragment(如果有的话)的状态,便于切换界面的时候更加流畅。同时,对于未保留状态的其他fragment并未销毁,只是执行到了onDestroyView()方法,将其内部的view销毁,重新返回加载的时候便会从onCreateView()进行,而不会重新调用整个生命周期。

因此对于ViewPager下的Fragment的状态保存一般在onDestroyView之前保存下来,用于下一次的初始化(FragmentStatePagerAdapter则不会销毁所有不保存状态的Fragment)。
下面的例子是使用FragmentPagerAdapter。

1、从第二个Fragment切换到第三个Fragment

//预加载第四个Fragment
F4-onAttach()
F4-onCreate()
//销毁第一个Fragment的视图
F1-onPause()
F1-onStop()
F1-onDestroyView()
V1-onDetachedFromWindow()
//创建第一个fragment视图,继续生命周期回调
F4-onCreateView()
V4-onFinishInflate()
V4-onAttachedToWindow()
F4-onViewCreated()
F4-onActivityCreated()
F4-onStart()
F4-onResume()
//View视图绘制
V4-onMesure()
V4-onLayout()
V4-onDraw()

2、从第三个Fragment切换到第二个Fragment

//恢复第一个Fragment的视图
F1-onCreateView()
V1-onFinishInflate()
V1-onAttachToWindow()
F1-onViewCreated()
F1-onActivityCreated()
//销毁第四个不在保存范围内的Fragment视图
F4-onPause()
F4-onStop()
F4-onDestroyView()
V4-onDetachedFromWindow()
//继续生命周期回调
F1-onStart()
F1-onResume()
//View视图绘制
V1-onMesure()
V1-onLayout()
V1-onDraw()

(2.1)直观地比较FragmentPagerAdapter和FragmentStatePagerAdapter的区别

网上挖出来的图,很直观。
FragmentPagerAdapter的Fragment管理:

FragmentStatePagerAdapter的Fragment管理:

默认情况下,ViewPager保留三个Frament的状态,但是通过setOffscreenPageLimit(int limit),可以调整预加载/缓存的数量,预先加载数 = limit,缓存数目= 2*limit +1 :

//默认的缓存页面数量(常量)
private static final int DEFAULT_OFFSCREEN_PAGES = 1;

//缓存页面数量(变量)
private int mOffscreenPageLimit = DEFAULT_OFFSCREEN_PAGES;

public void setOffscreenPageLimit(int limit) 
    //当我们手动设置的limit数小于默认值1时,limit值会自动被赋值为默认值1(即DEFAULT_OFFSCREEN_PAGES)
    if (limit < DEFAULT_OFFSCREEN_PAGES) 
        Log.w(TAG, "Requested offscreen page limit " + limit + " too small; defaulting to "+ DEFAULT_OFFSCREEN_PAGES);
        limit = DEFAULT_OFFSCREEN_PAGES;
    
    if (limit != mOffscreenPageLimit) 
        //经过前面的拦截判断后,将limit的值设置给mOffscreenPageLimit,用于
        mOffscreenPageLimit = limit;
        populate();
    




(3)退出Activity:

退出Activaty的时候,会按照ViewPager添加Fragment的顺序依次销毁Fragment,调用(Fragment.onDestoyView()——>View.onDetachFromWindow()——>Fragment.onDestroy()——>Fragment.onDetach(),对于未保存状态的Fragment,则直接从onDestroy()——>onDetach() )。

三、小结

  • 静态加载:xml布局内容属于静态加载,静态加载的View控件是跟随Activity的生命周期的。在Activity的setContentView()中按照深度优先的方式解析View树,创建View对象,在Fragment的onCreateView()中解析View树,然后将其view嵌至Fragment的fl_container中。静态加载的控件内容都是由Activity来管理,因此在Activity的xml布局中声明的View的detachFromWindow是在Activity调用detachFromWindow的时候调用,包括静态嵌入的fragment中的View。

  • 动态加载:代码中加载(FMManager、addView等)属于动态加载,动态加载的方式下View的生命周期由其管理者来确定(容器ViewGroup通过addView、removeView会触发View的attachFromWindow和detachFromWindow)。这种情况下Fragment、View的生命周期都是由FragmentManager(对于View也可以是它的父容器ViewGroup)来管理,因此当Fragement销毁时,其内部的view会在它调用destroyView的时候触发detachFromWindow事件,而View会在其ViewGroup容器调用removeView的时候触发detachFromWindow。

以上是关于Android基础——控件的混合生命周期的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Android 的生命周期感知协程范围内返回函数值?

android 自定义控件之ViewGroup生命周期执行步骤

Android中Service的生命周期与启动方法有啥区别?

安卓基础之 四大基本组件介绍与生命周期

[android] surfaceview的生命周期

重温Android和Fragment生命周期