Lifecycles 简介

Lifecycles 即生命周期,属于 Jetpack 架构组件之一的 Lifecycles 组件是可以为其它组件提供生命周期感知能力的一个组件,而具备了生命周期感知能力的组件就叫生命周期感知组件,注意加粗部分多读两遍,我看网上很多文章直接把 Lifecycles 组件叫生命周期感知组件😂

那么什么是生命周期感知能力呢?我们知道四大基础组件有生命周期,能感知这些生命周期的组件就具备了生命周期感知能力,所以我认为以前我们使用的 MVP 模式中的 Presenter 如果它通过接口等方式间接获得了 Activity 的生命周期,它也算是一个生命周期感知组件,但是这种获取生命周期的方式和 Lifecycles 比起来还是太 low 了,我们接着对比传统生命周期感知方式,聊一聊为什么使用 Lifecycles 组件

为什么需要 Lifecycles 组件 ?

我们知道,一个 Android App 之所以能够完成一系列复杂任务,都是基于各个基础组件之间的密切配合,我们熟知的四大组件(Activity、Service、Broadcast、Content Provider)是有生命周期的,并且 Android 的操作系统层或者 FrameWork 框架层会为其提供相应的回调,以便程序在不同的状态下做相对正确的事;而普通的组件或者我们自己开发的一些组件就没有这个待遇了,我们通常需要在合适的时候,手动将这些第三方组件的状态控制方法,诸如启动/关闭/注册/拆除等,添加到具备生命周期的组件的回调中去才能使它们正确工作。

然后,随着时间推移版本迭代,引用的第三方组件越来越多,你的代码逐渐变成了这样

override fun onStart() {
    a.init()
  	b.start()
  	c.initialize()
  	...
}
...
override fun onStop(){
  	if(a!=null) a.remove()
  	if(b!=null&&b.c!=null) {
      	b.c.tearDown()
      	b.stop()
    } 
  	c.stop()
  	...
}

你可能会在 Presenter 中完成这一系列操作,但这其实没有解决根本问题:随着第三方组件越来越多的引入,你的 Presenter 也越来越难以维护和测试。而且还可能出现内存泄露的情况

override fun onStart() {
		Util.checkUserStatus { result ->
        // 此回调可能在 onStop 之后被调用
				if(result){
		      	b.start()
		    }
		}                     
}
...
override fun onStop(){
		b.stop()
}

我们接着来看 Lifecycles 组件如何解决这些问题

了解 Lifecycles 组件

Lifecycles 组件主要包含两个部分: LifecycleLifecycleOwner

1、Lifecycle

Lifecycle 类包含 LifecycleOwner 组件生命周期状态的信息(如 Activity 或 Fragment),并允许其他对象观察此状态

Lifecycle 使用两个主要的枚举类型来跟踪与他相关的组件的生命周期状态:

Event

Event 分发自 framework 和 Lifecycle 类,这些 Event 映射到 Activity 和 Fragment 中的回调事件

State

被 Lifecycle 对象跟踪的组件的当前状态

Event 和 State 的关系

你可以把 State 看作事件的节点,而 Event 就是这些节点之间的过程

被观察者(observer)可以通过添加注解来将其方法和观察者组件(observerOwner)的生命周期状态进行绑定。怎么知道谁是它的观察者呢?观察者通过调用 Lifecycle.addObserver(observer) 添加被观察者即可,如下所示:

class MyObserver : LifecycleObserver {

    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
    fun connectListener() {
        ...
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
    fun disconnectListener() {
        ...
    }
}

...

myLifecycleOwner.getLifecycle().addObserver(MyObserver())

myLifecycleOwner 是一个实现了 LifecycleOwner 接口的观察者对象,我们来看看 LifecycleOwner

2、LifecycleOwner

LifecycleOwner 是一个单方法接口,表示该类具有生命周期,它只有一个抽象方法 abstract getLifecycle()

/**
 * A class that has an Android lifecycle. These events can be used by custom components to
 * handle lifecycle changes without implementing any code inside the Activity or the Fragment.
 *
 * @see Lifecycle
 */
@SuppressWarnings({"WeakerAccess", "unused"})
public interface LifecycleOwner {
    /**
     * Returns the Lifecycle of the provider.
     *
     * @return The lifecycle of the provider.
     */
    @NonNull
    Lifecycle getLifecycle();
}

此接口从各个类中( AppCompatActivity / Fragment 等)抽象出生命周期所有权,并允许任何自定义应用程序类都可以实现 LifecycleOwner 接口

实现一个简单的生命周期感知组件

我们通过一个简易计时器的例子来进行调整,在 Android Jetpack - ViewModel 中,我们实现了一个简易计时器,该计时器可以在 Activity 处于配置更改或后台的情况下继续计时,我们现在利用 Lifecycle 修改它,让它只能在 Activity 处于可见状态的情况下计时

1、标记 ViewModel 为 LifecycleObserver 并实现 start/stop

这一步很简单也很重要,只有通过 LifecycleObserver 标记的类才能被 LifecycleOwner 添加并观察

class TimerViewModel : ViewModel(),LifecycleObserver{
  ...
}

在 ViewModel 中添加 start()stop()

...
@OnLifecycleEvent(Lifecycle.Event.ON_START)
fun start(){
    startTime = SystemClock.elapsedRealtime() - stopTime
    // 让 UI 立即更新最新结果
    _elapsedTime.postValue((SystemClock.elapsedRealtime() - startTime!!)/1000)
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
fun stop(){
    stopTime = elapsedMilliseconds
}
...

2、在 Activity 中注册观察者

class MainActivity : AppCompatActivity() {
    private val viewModel by lazy {
        ViewModelProviders.of(this).get(TimerViewModel::class.java)
    }
    @SuppressLint("SetTextI18n")
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        viewModel.elapsedTime.observe(this, Observer {
            tv_time.text = "$it second elapsed"
        })
      	// 添加观察者
        lifecycle.addObserver(viewModel)
    }

注意这里的 MainActivity 继承自 AppCompatActivity,由于 AppCompatActivity 继承自 ComponentActivity,而 ComponentActivity 实现了 LifecycleOwner,所以不需要手动注册 MainActivity 为 LifecycleOwner,直接调用 getLifecycle() 即可

public class ComponentActivity extends androidx.core.app.ComponentActivity implements
        LifecycleOwner,
        ViewModelStoreOwner,
        SavedStateRegistryOwner,
        OnBackPressedDispatcherOwner { 
          	... 
        		@NonNull
						@Override
						public Lifecycle getLifecycle() {
						    return mLifecycleRegistry;
						}
          	...
        }

现在,ViewModel 带有 @OnLifecycleEvent(Lifecycle.Event.ON_START) 注解的函数就会在 MainActivity onStart 时被调用,onStop 同理,这样我们就实现了后台暂停计时的效果

示例代码

https://github.com/realskyrin/jetpack_lifecycles

生命周期感知组件的最佳实践和用例

最佳实践
  • 保持 UI 控制器(Activity/Fragment)尽可能精简。他们不应该试图获取自己的数据;相反,使用 ViewModel 执行此操作,并观察 LiveData 对象以将更改反映回 UI
  • 尝试编写数据驱动的 UI,其中 UI 控制器负责在数据更改时更新视图,或将用户操作通知给 ViewModel
  • 将您的数据逻辑放在 ViewModel 类中。 ViewModel 应该充当 UI 控制器和应用程序其余部分之间的连接器。但要注意,ViewModel 不负责获取数据(例如,从网络获取)。相反,ViewModel 应调用适当的组件来获取数据,然后将结果提供回 UI 控制器
  • 使用数据绑定来维护视图和 UI 控制器之间的干净界面。这使您可以使视图更具说明性,并最大限度地减少在活动和片段中编写所需的更新代码。如果您更喜欢用 Java 编程语言执行此操作,请使用像 Butter Knife 这样的库来避免样板代码并具有更好的抽象
  • 如果您的 UI 很复杂,请考虑创建一个 presenter 类来处理 UI 修改。这可能是一项艰巨的任务,但它可以使您的 UI 组件更容易测试
  • 避免在 ViewModel 中引用 View 或 Activity 上下文。一旦 ViewModel 存活时间超过活动(在配置更改的情况下 Activity 会被多次重建),Activity 会因为垃圾回收器没有妥善处理而发生内存泄露
  • 使用 Kotlin 协程来管理长时间运行的任务以及可以异步运行的其他操作
使用场景
  • 在高精度和低精度的定位模式之间切换,使用生命周期感知组件可以让你的 App 在可见状态下使用高精度定位,当 App 处于后台的情况下切换到低精度定位,LiveData 是一个生命周期感知组件,允许你的应用在用户更改位置时自动更新UI
  • 停止/开始视频缓冲。使用生命周期感知组件尽快启动视频缓冲,但推迟播放直到应用程序完全启动。您还可以使用生命周期感知组件在销毁应用程序时终止缓冲
  • 启动和停止网络连接。使用生命周期感知组件在应用程序处于前台时启用网络数据的实时更新(流式传输),并在应用程序进入后台时自动暂停
  • 暂停和恢复动画 drawables 。当 app 在后台时使用生命周期感知组件处理暂停动画 drawables ,并在 app 在前台后恢复 drawables

参考

https://developer.android.com/topic/libraries/architecture/lifecycle

https://developer.android.com/reference/androidx/lifecycle/Lifecycle.html

https://developer.android.com/reference/androidx/lifecycle/LifecycleOwner.html

https://codelabs.developers.google.com/codelabs/android-lifecycles/index.html