Jetpack | Lifecycle全解析

2023-01-10  本文已影响0人  代码我写的怎么

前言

Lifecycle即生命周期,作为Android开发者,我们对生命周期可太熟悉了,因为我们不仅经常在ActivityFragment的生命周期函数比如onCreateonResume等中做一些逻辑操作;而且组件的生命周期控制不当的话,可能会导致内存泄漏,或者一些无法预想的结果,比如在后台请求网络操作的协程生命周期,要是大于所需要展示UI的界面的生命周期时,就会导致不必要资源浪费。

既然ActivityFragment、协程、ViewModelLiveData,甚至我们常用的Handler等所有对象,都需要严格管理生命周期,所以管理这些组件的生命周期就非常重要,这里大概能得到2点关于生命周期的需求:

所以Lifecycle组件就被开发出来了,面对需要对接多个组件的复杂情况,一个有用的方法就是抽象出一层,而Lifecycle的思想也是如此,让本来开发者需要多个组件的情况下,现在就变成了对接一个组件。

如上图所示,有了Lifecycle后,就可以统一组件的生命周期了,这样我们在开发时就可以面向Lifecycle这一个组件了,这里我们以使用StateFlow为例,一般是调用LifecycleOwner(生命周期持有者)的repeatOnLifecycle方法:

//该方法为lifecycle-runtime-ktx中方法,用于监听Satate类型的uiState的变化
public suspend fun LifecycleOwner.repeatOnLifecycle(
    state: Lifecycle.State,
    block: suspend CoroutineScope.() -> Unit
): Unit = lifecycle.repeatOnLifecycle(state, block)

该方法配合StateFlow使用以达到和使用LiveData一样的效果,测试代码如下:

class MyActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
            // 当LifecycleOwner的状态在STARTED及以上时,会触发block执行
            // 当ON_STOP事件发生时,block协程会被取消
            // 当LifecycleOwner接收到ON_START事件时,会重新执行block
            lifecycleScope.launch {
                repeatOnLifecycle(Lifecycle.State.STARTED) {
                    uiStateFlow.collect { uiState ->
                        updateUi(uiState)
                    }
                }
            }
        }
    }

从这里代码注释我们可以看出,这里其实实现了类似LiveData的效果:会在Activity可见时绘制UI,在Activity销毁时取消协程,保证协程的生命周期可控,可以有效地节省资源和防止内存泄漏。

而这个的关键方法repeatOnLifecycleLifecycleOwner的扩展函数,这里不仅仅ActivityLifecycleOwnerFragment也是实现该接口,这也就是前面所说的我们开发新功能时只需要面对Lifecycle这一个抽象层的组件即可。

正文

既然我们想把生命周期给抽象出来,再结合生命周期的作用,这里我们就可以思考一下,应该给抽象为几个部分。在Android中的Lifecycle框架库中,主要涉及的类如下:

这里如果对UML类图还不熟悉的,可以查看文章:# 效率提升 | UML类图

从上面UML类图我们可以知道如下信息:

这也印证了文章刚开始我们抽象Lifecycle的本意,我们先不急着探究其原理,我们先来思考一下,我们如何使用这些提供的API

Lifecycle的基本用法

第一种用法就是前面所说的,可以监听Lifecycle所派发的生命周期事件,测试代码如下:

//Activity中代码
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    lifecycle.addObserver(object : LifecycleEventObserver {
        override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
            Logger.d("source = $source  event = $event")
        }
    })
}

由于Activity实现了LifecycleOwner接口,所以这里可以获取Lifecycle对象,通过addObserver()方法可以添加观察者,这里的LifecycleEventObserverLifecycleObserver的子接口:

而这里的生命周期事件就是Event,对于Activity来说,其就是对应了其几个生命周期方法,所以上面测试代码中,我们把APP页面打开再关闭,打印如下:

第二种用法就是前面所说的可以调用getCurrentState()方法来获取当前Lifecycle的状态,对于状态State一共定义了5种枚举,至于为什么是5种,我们等会说原理时细说,这里我们大概和生命周期函数执行完的状态对应起来。

这里我们以LiveData来举例,LiveData具有生命周期感知能力的观察者容器,所以有个特性:使用LiveData为数据源,做数据驱动来更新UI时,当UI页面不可见时,是不会更新UI的

既然和生命周期有关,那就必须得用Lifecycle了,下面是利用LiveData更新UI:

//Activity中代码
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    viewModel.uiState.observe(this){
        updateUI(it)
    }
}

这里调用了LiveDataobserve方法:

public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer){
        assertMainThread("observe");
        //注释1
        if (owner.getLifecycle().getCurrentState() == DESTROYED) {
            // ignore
            return;
        }
        ...
}

这里我们可以发现该方法第一个参数是LifecycleOwner,即生命周期持有者,而我们所熟悉的Activity就实现了该接口。在注释1处,会判断Lifecycle的当前状态,如果是DESTROYED的话,就没必要继续操作了,即当前生命周期组件已经是销毁状态了,完全没必要更新UI了。

这只是LiveData利用LifecyclegetCurrentState()方法获取状态的一个使用之处,更多关于LiveData的生命周期感知能力,我们后面介绍LiveData时再说。

第三种使用Lifecycle组件的用法就是简化代码,核心思想是分离关注点,让原来需要在Activity/Fragment的生命周期方法中写的逻辑,给整理到一个生命周期观察者中,这里我们使用官方例子:

class MyActivity : AppCompatActivity() {
    //地理位置监听器
    private lateinit var myLocationListener: MyLocationListener

    override fun onCreate(...) {
        //在onCreate方法中,创建监听器,根据监听器更新UI
        myLocationListener = MyLocationListener(this) { location ->
            // update UI
        }
    }

    public override fun onStart() {
        super.onStart()
        //判断监听器是否被调用,开启服务
        Util.checkUserStatus { result ->
            // what if this callback is invoked AFTER activity is stopped?
            if (result) {
                myLocationListener.start()
            }
        }
    }

    public override fun onStop() {
        super.onStop()
        //及时暂停服务,防止资源浪费和内存泄漏
        myLocationListener.stop()
    }

}

上述代码我相信很多开发者都写过,为了服务能正常创建、启动以及关闭,我们必须保持服务的生命周期和Activity一致,这样可以防止内存泄漏和避免资源浪费,但是这就导致每个生命周期函数中逻辑过多,这时我们就可以利用Lifecycle来进行优化。

类似第一种使用方法,我们需要监听Lifecycle的生命周期改变事件,同时做一些处理,所以我们自定义一个观察者,代码如下:

//继承至DefaultLifecycleObserver
internal class MyLocationListener(
        private val context: Context,
        private val lifecycle: Lifecycle,
        private val callback: (Location) -> Unit
): DefaultLifecycleObserver {

    private var enabled = false

    init{
        lifecycle.addObserver(this)
    }

    //说明ON_START事件发生,可以看成Activity的onStart函数回调
    override fun onStart(owner: LifecycleOwner) {
        if (enabled) {
            // connect
        }
    }

    fun enable() {
        enabled = true
        if (lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED))  {
            // connect if not connected
        }
    }

    //说明ON_PAUSE事件发生,可以看成Activity的onStop函数回调
    override fun onStop(owner: LifecycleOwner) {
        // disconnect if connected
    }

    ...
}

既然是添加观察者,所以必须是继承至LifecycleObserver接口,前面说了,这是一个空方法接口,第一种使用方法中,我们是使用了LifecycleEventObserver,这里我们使用DefaultLifecycleObserver接口,继承关系如下:

这里的区别显而易见,就是Event给细分为了这几个回调函数,同时提供了默认实现。

定义完自定义的观察者后,使用如下:

class MyActivity : AppCompatActivity() {
    private lateinit var myLocationListener: MyLocationListener

    override fun onCreate(...) {
        //只需要在onCreate中调用即可
        myLocationListener = MyLocationListener(this, lifecycle) { location ->
            // update UI
        }
        Util.checkUserStatus { result ->
            if (result) {
                myLocationListener.enable()
            }
        }
    }
}

相比于最开始的代码,我们只需要在onCreate这一个生命周期函数中进行初始化即可,可以免去在多个生命周期函数中写逻辑,可以简化代码

第四种用法主要是预防内存泄漏,在代码设计中,有个非常重要的原则是架构和业务分离,就比如我们常见的网络传输协议,TCP已经提供了可靠交付,就不需要我们在业务应用层里面再写关于可靠交付的逻辑了。

在整个Jetpack组件都是遵循这种原则的,比如我们文章最开始的例子,repeatOnLifecycle()方法中收集数据的协程,就会当Lifecycle处于DESTROYED时自动取消,这样就把合适时机取消协程,防止资源浪费和内存泄漏这个架构设计和业务逻辑给分开了,而不用我们在业务处理时,还考虑这些。

除此之外,还有非常多的小例子,比如这里防止内存泄漏的Handler:

//这里继承Handler,实现LifecycleEventObserver接口
class LifecycleHandler(val lifecycleOwner: LifecycleOwner) : Handler(),
    LifecycleEventObserver {

    init {
        addObserver()
    }

    private fun addObserver() {
        lifecycleOwner.lifecycle.addObserver(this)
    }

    //当生命周期持有者状态为DESTROYED时,移除所有消息和回调,保证可以正常被GC回收
    override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
        if (event == Lifecycle.Event.ON_DESTROY) {
            removeCallbacksAndMessages(null)
            lifecycleOwner.lifecycle.removeObserver(this)
        }
    }
}

由于ActivityFragment都是实现了LifecycleOwner接口,所以所有依赖ActivityFragment的类,都可以采取类似策略,比如Dialog等,将内存泄漏消灭在架构层逻辑。

看到这里,我相信你会有一些疑问。比如DefaultLifecycleObserver中的onCreate()回调是如何和Activity中的onCreate()回调保持联动的?有没有先后调用顺?换成Fragment为生命周期持有者,会不会有什么变化?

带着这些疑问,我们来分析一下其源码。

Lifecycle解析

既然Lifecycle生命周期的抽象,主要用于获取当前生命周期状态以及派发生命周期事件,那么ActivityFragment的生命周期函数个数是不一样的,所以这里需要定义一个更加通用的抽象层。

按照设计,表示Lifecycle状态是State,它一共有5种状态;而表示状态切换的事件,一共有7个,这里理解起来比较关键,官方有一个图如下:

这里我们Activity为例,来解释一下这几种State含义:

这里不算onRestart()方法一共有6个,但是在前面我们定义的LifecycleState却没有这么多,所以这里要注意,这里的State更多的表示是一种状态,而非和生命周期函数一一对应

上面我们以Activity生命周期为例,分析了各种State的作用,可以总结为下图:

这里我们可以知道一个完整的Activity生命周期,State状态是从小到大,再到小,这也是符合我们抽象生命周期的理念:我们更侧重想知道当前Lifecycle的状态,比如是否可见,是否已经被销毁,方便对应逻辑操作,而生命周期变化的事件,由Event来定义和派发。

说完了State,我们来看看其向观察者所派发的Event事件,这里搞清楚一个设计思路:Lifecycle的状态切换是由Event所导致,所以可以认为State就是一个图中的节点,而Event就是连接节点的边。

前面我们是以Activity来说明State的,其实真实的Event设计的差不多,也是对应着几种回调函数来设计的,Event是枚举类,共有6种事件,以及一系列状态切换方法,我们举个简单例子来说明Event工作流程:

所以Event的定义共如下几种:ON_CREATE、ON_START、ON_RESUME、ON_PAUSE、ON_STOP和ON_DESTROY,至于各自的含义,这里不多说了,就是和生命周期回调函数对应。

这里有个难点,就是Event中定义的几个方法,配合State使用,我们来分析一下。

downFrom方法定义如下:

public static Event downFrom(@NonNull State state) {
    switch (state) {
        case CREATED:
            return ON_DESTROY;
        case STARTED:
            return ON_STOP;
        case RESUMED:
            return ON_PAUSE;
        default:
            return null;
    }
}

解释:当从参数state离开往更低的状态切换,需要分发的Event。这里的downFrom函数名我们要分开看,其中down表示动词,表示向下from就是说明从哪个状态开始,这里结合前面的状态图一起,看代码实现:

类似的方法还有downTo

public static Event downTo(@NonNull State state) {
    switch (state) {
        case DESTROYED:
            return ON_DESTROY;
        case CREATED:
            return ON_STOP;
        case STARTED:
            return ON_PAUSE;
        default:
            return null;
    }
}

其中down是动词表示向下,to表示期望到达某个状态,这里我们需要注意一点:不论是down,还是后面要说的up,状态变化只能一步一步来

我想从RESUMED状态,下降到CREATED状态,根据状态图必须先下降到STARTED状态,再下降到CREATED状态,所以我们再看前面downTo的实现,当期望下降的状态是DESTROYED时,根据一次只能下降一步的原则,其实它的上一个状态就是CREATED,所以需要派发ON_DESTROY事件来完成状态切换。

这里还有类似的upFrom()upTo()方法,我们就不分析了,但是我们要理解其设计思路:这些方法有什么用?

不管是down还是up,它的目的都是改变状态State的值,那我直接改变其值不就好了吗?为什么要知道每一步改变的事件呢?

这就涉及一个思想:永远是事件驱动状态变化,即事件导致状态变化。这里体现为2个方面:

这2个方面第一个非常好理解,我们来看第二点,第二点其实就对应着Lifecycle的2个重要函数:addObserverremoveObserver

这里暂时还没有到分析原理的地方,我们可以先说结论,等后面分析原理时再验证一下:

被添加的观察者状态,会被提高到和被观察者一样的状态。比如LifecycleOwner的状态是STARTED,这个刚被添加的观察者,就需要派发ON_CREATEON_START事件。

到这里我们仅仅在接口层分析了Lifecycle的使用,那么接下来就是重点工作了,我们来分析一下Lifecycle是如何对ActivityFragment进行抽象和封装的。

Lifecycle原理解析

从前面接口和使用分析,我们知道Lifecycle是观察者模式,可以用来获取当前LifecycleState,以及通过观察者来监听Lifecycle所派发的Event

所以这里我们需要知道Lifecycle是如何进行抽象的,是如何添加/移除观察者,以及如何派发Event,即

这一部分的工作原理,我们先以Activity为例来解析源码。

话不多说,我们还是先来看看相关类的关系:

这里我们可以发现ComponentActivity实现了LifecycleOwner接口,即我们平时使用Activity就是生命周期持有者。

我们调用getLifecycle()方法时,其实就是返回ComponentActivity中定义的LifecycleRegistry类型的mLifecycleRegistry成员变量:

public Lifecycle getLifecycle() {
    return mLifecycleRegistry;
}

private final LifecycleRegistry mLifecycleRegistry = new LifecycleRegistry(this);

而且LifecycleRegistry实现了Lifecycle接口,所以这里使用了代理模式,所有操作都由这个LifecycleRegistry来实现

我们先不去看其源码,我们先来分析一下Activity生命周期事件是什么时候派发给LifecycleRegistry的。

原本我以为分发生命周期事件,会在ComponentActivity的各个生命周期回调函数中调用LifecycleRegistry方法来派发事件,看了源码发现是利用了ReportFragment来实现的,调用关系如下:

上面黄色的Note表示Lifecycle的当前State,由这里我们可以验证最开始说的State含义:其中INITIALIZED初始化状态表示组件已经被创建,但是没有收到ON_CREATE事件,当收到ON_CREATE事件,状态切换到CREATED状态

这里为什么可以使用ReportFragment的生命周期回调函数中来分发Activity的生命周期Event呢?这就涉及了ActivityFragment生命周期函数的调用关系,如下图:

虽然这里FragmentActivity多几个生命周期函数,但是丝毫不影响我们使用Fragment的生命周期来派发其所依赖的Activity的生命周期事件。

通过简单查看源码,对应关系如下:

这里分发事件的方法就是调用LifeycleRegistryhandleLifeEvent()方法,该方法我们后面细说。

说完了Activity是如何适配Lifecycle的,主要就是能够正确地、合理地派发EventLifecycleRegistry,我们接着来看一下Fragment的流程,因为Fragment有一点不一样,它在派发Event有2套逻辑

我们一般使用LiveData作为观察者容器,在FragmentonViewCreated()方法中进行数据观察和更新UI:

这里我们使用thisFragment对象作为LifecycleOwner居然会报错,提醒使用viewLifecycleOwner作为生命周期持有者,那这里这2个生命周期持有者有什么区别呢?如果强行使用this会有什么问题呢?

带着问题看源码,就有了探索思路,首先就是Fragment它是实现了LifecycleOwner接口的,相关类如下:

Activity一样,通过getLifecycle获取的依旧是LifecycleRegistry实例,工作机制一样,当Fragment的生命周期变化时,通过调用handleLifeEvent()来保存和处理事件,我们只需要知道在什么时候会派发事件即可,下面是源码总结:

上面的代码调用流程再配合文章刚开始说的图,是不是可以完美契合上了!尤其是派发事件和生命周期回调函数的先后问题

从这里的生命周期回调来看,我们可以得出结论:getLifecycle()获取的Lifecycle,其代表Fragment的生命周期

getViewLifecycleOwner()获取的Lifecycle有什么区别呢?话不多说,直接看源码,涉及的相关类:

可以发现这里使用了代理模式,但是代理的是LifecycleOwner象,所以这里的核心代码就是FragmentViewLifecycleOwner的实现,在这里面我们依旧还是利用LifecycleRegstry来管理和处理Event,所以我们的关键还是看看在什么时候调用了Event,总结如下:

从这里的调用链和前面Fragment自己的Lifecycle做比较,我们可以明显发现ON_STARTON_RESUMEON_PAUSEON_STOP事件的派发时机和Fragment自己的Lifecycle是一样的。

而其他几个Event的派发就和FragmentView的关系更加密切了,这里也就说明一件事:Fragment的生命周期和其View的生命周期是不一样的getViewLifecycleOwner()返回的viewLifecycleOwner,表示的是FragmentView的生命周期

至于区别,我们可以看一张官方的图:

从这张图我们可以得出以下结论:

分析完上面结论,我们就可以解决这个问题了:

为什么这里要使用ViewLifecycle而不是选择FragmentLifecycle

原因非常简单,也就是我们期望更新UI是根据FragmentView的生命周期来做处理,而不是Fragment的生命周期,那如果用错了,会导致什么问题呢?

这里涉及LiveData一个特性:LifecycleOwnerLifecycle处于DESTROYED状态时,会自动removeLiveData观察者

所以这里的uiData会在owner处于DESTROYED时自动移除监听,可以防止内存泄漏,并且只更新可见的有效的UI信息

这里出问题的场景就是Fragment切换时,可以把FragmentA加入到回退栈中,并且打开新FragmentB,这时FragmentA会执行onDestroyView()方法,但是不会执行onDestroy()方法,这时因为Fragment并没有被销毁;

这里如果使用Fragment的作为LifecycleOwnerFragmentA就不会移除uiData这个观察者,因为它没有到DESTROYED状态;这时再按返回键,FragmentB出栈,FragmentA又重新显示出来,这时会重新回调onViewCreated()方法,又会重新添加一遍uiData这个观察者,导致重复添加观察者

但是使用viewLifecycleOwner就不会出现这种情况,根据前面生命周期图可知,当执行到onDestroyView()方法时,Lifecycle就处于DESTROYED状态了,这时可以正常移除观察者

总结

不知不觉已经写了一万多字了,不得不说,这种优秀的库的源码就是一座宝藏,看源码的过程真的是一个持续学习的过程,不仅仅可以加深理解,更可以学习设计思路、实现思路和方法等。

还是做个总结吧,如下:

结合图中示意,理解每个State的含义,以及关键的的downFromupFrom等方法设计思路和含义。

作者:元浩875
链接:https://juejin.cn/post/7186533178213924923

上一篇 下一篇

猜你喜欢

热点阅读