持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第25天,点击查看活动详情
使用生命周期感知型组件
生命周期感知型组件可执行操作来响应另一个组件(如Activity
和Fragment
)的生命周期状态的变化。androidx.lifecycle
软件包提供了可用于构建生命周期感知型组件的类和接口,这些组件可以根据Activity
或Fragment
的当前生命周期状态自动调整其行为。
由于在Android
框架中定义的大多数应用组件都存在生命周期,而生命周期是由操作系统或进程中运行的框架代码管理,所以应用必须遵循生命周期,在合适的生命周期内执行合适的操作。如果不遵循这些生命周期,可能会引发内存泄漏甚至奔溃。
假设有一个在屏幕上显示当前位置的Activity
,常见的实现可能如下:
internal class LocationUtils(
private val context: Context,
private val callback: (Location) -> Unit
) {
fun start(){
//开始请求位置信息
}
fun stop(){
//停止请求位置信息
}
}
这样我们就定义了一个获取位置信息的类,然后我们在Activity
中可以这样使用:
class LifecycleTest1Activity : BaseActivity<ActivityLifecycleTest1Binding>() {
private lateinit var locationUtils: LocationUtils
override fun getLayoutId(): Int = R.layout.activity_lifecycle_test1
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
locationUtils = LocationUtils(this){
Log.e("TAG","获取到的位置信息$it")
}
}
override fun onStart() {
super.onStart()
locationUtils.start()
}
override fun onStop() {
super.onStop()
locationUtils.stop()
}
}
上面的代码是没有问题的,但是在真实的使用的场景中,在同一个页面中我们可能会有很多的管理界面和其它组件的调用,以响应生命周期的当前状态。管理多个组件会在生命周期方法(如onStart
或者onStop
方法中)放置大量的代码,这就会使得它们难以维护。
此外,无法保证组件会在Activity
或Fragment
停止之前启动。在我们需要长时间运行的操作中(如onStart()
中检查某种配置)尤其如此。这可能会导致出现一种竞争条件,在这种条件下onStop()
会在onStart()
之前结束,这使得组件停留的时间比所需要的时间更长。下面是官方代码:
class MyActivity : AppCompatActivity() {
private lateinit var myLocationListener: MyLocationListener
override fun onCreate(...) {
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()
}
}
生命周期
Lifecycle
是一个类,用于存储有关组件(如Activity
或Fragment
)的生命周期信息,并允许其它对象观察此状态。
Lifecycle
使用两种主要枚举跟踪其关联组件的生命周期状态:
Event
从框架和Lifecycle
类分派的生命周期事件,这些事件映射到Activity
或Fragment
中的回调事件。

State
由Lifecycle
对象跟踪的组件的当前状态。
可以将状态看作途中的节点,将事件看作这些节点之间的边。
类可以通过向其方法添加注解来监控组件的生命周期的状态,然后,可以通过调用Lifecycle
类的addObserver()
方法并传递观察者的实例来添加观察者,如:
class MyObserver : LifecycleObserver {
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
fun connectionListener(){
Logs.e("开始连接")
}
fun disconnectionListener(){
Logs.e("断开连接")
}
}
//使用
myLifecycleOwner.getLifecycle().addObserver(MyObserver())
在上面的示例中,myLifecycleOwner
对象实现了LifecycleOwner
接口。
LifecycleOwner
LifecycleOwner
是单一方法接口,表示类具有Lifecycle
.它具有一种方法(即getLifecycle()
),该方法必须由类进行实现,如果尝试管理整个应用的声明周期,则需要使用ProcessLifecycleOwner
此接口从各个类(如Fragment
和AppcompatActivity
)抽象化Lifecycle
的所有权,并允许编写与这些类搭配使用的组件。任何自定义应用类均可实现LifecycleOwner
接口。
实现LifecycleObserver
的组件可与实现LifecycleOwner
的组件无缝协同工作,因为所有者可以提供生命周期,而观察者可以注册以观察声明周期。
对于上面的位置跟踪示例,可以让LocationUtils
类实现LifecycleObserber
接口,然后在Activity
的onCreate()
方法中对其进行初始化。这样,LocationUtils
类便可以自动根据Activity
的生命周期执行相应的操作。同时,这样做意味着对生命周期状态的变化做出响应的逻辑会在LocationUtils
类内部。让各个组件存储自己的逻辑,可使Activity
和Fragment
的逻辑更易于管理。
为了实现上面的效果,主要分成以下三步:
- 在
LocationUtils
类实现LifeObserver
接口,然后再需要监听的方法上面添加需要添加的生命周期状态的注释:
internal class LocationUtils(
private val context: Context,
private val callback: (Location) -> Unit
) : LifecycleObserver {
//在onStart中开始请求
@OnLifecycleEvent(Lifecycle.Event.ON_START)
fun start(){
//开始请求位置信息
Logs.e("开始请求")
}
//在onStop之后停止请求
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
fun stop(){
//停止请求位置信息
Logs.e("停止请求")
}
}
-
在
Activity
中添加需要实现的LifecycleOwner
接口,这里由于Support Library 26.1.0
及更高版本的Activity
和Fragment
中已经默认实现了这个接口,所以可以省略,主要是要看一下编译的版本。 -
在
Activity
的onCreate()
方法中添加LocationUtils
的对象即可:
lifecycle.addObserver(locationUtils)
运行程序之后,会出现如下打印的信息:
2020-04-15 17:24:19.810 4318-4318/com.example.myapplication E/com.example.myapplication: onCreate() finish
2020-04-15 17:24:19.815 4318-4318/com.example.myapplication E/com.example.myapplication: onStart()
2020-04-15 17:24:19.815 4318-4318/com.example.myapplication E/com.example.myapplication: 开始请求
2020-04-15 17:24:19.816 4318-4318/com.example.myapplication E/com.example.myapplication: onResume()
2020-04-15 17:24:29.030 4318-4318/com.example.myapplication E/com.example.myapplication: onPause()
2020-04-15 17:24:29.561 4318-4318/com.example.myapplication E/com.example.myapplication: 停止请求
2020-04-15 17:24:29.563 4318-4318/com.example.myapplication E/com.example.myapplication: onStop()
2020-04-15 17:24:29.566 4318-4318/com.example.myapplication E/com.example.myapplication: onDestroy()
可以看到,确实是在相应的生命周期的方法中执行了相应的操作。
但是,这样仍然是会出现我们之前所说的问题的,所以在官方文档中,对LocationUtils
也做了相应的修改,如:
internal class LocationUtils(
private val context: Context,
private val lifecycle: Lifecycle,
private val callback: (Location) -> Unit
) : LifecycleObserver {
private var enable = false
//在onStart中开始请求
@OnLifecycleEvent(Lifecycle.Event.ON_START)
fun start() {
//开始请求位置信息
if (enable)
Logs.e("开始请求")
}
//在onStop之后停止请求
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
fun stop() {
//停止请求位置信息
Logs.e("停止请求")
}
fun enable() {
enable = true
if (lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) {
//如果没有开始连接,则在这里开始连接
start()
}
}
}
我们主要在LocationUtils
中添加了一个enable
属性和一个enable()
方法,由于enable
默认为false
,所以虽然我们会监听OnStart()
生命周期,但是其实并不会执行操作。然后我们在enable()
方法中做判断,当修改了enable
的状态的时候会判断当前处于何种生命周期的状态,从而决定是否要执行相应的方法。
override fun onCreate(...) {
myLocationListener = MyLocationListener(this, lifecycle) { location ->
// update UI
}
Util.checkUserStatus { result ->
if (result) {
myLocationListener.enable()
}
}
}
在上面,我们在判断完状态之后才会去调用LocationUtils
里面的enable()
开启定位,在enable()
中就会对当前的页面状态进行判断,然后决定是否要开启请求。
至此,我们就可以使用生命周期感知型组件了。
实现自定义LifecycleOwner
如果我们有一个自定义类并且希望使其成为LifecycleOwner
,那么就需要使用LifeCycleRegister
将事件转发到该类,如下:
class MyActivity : Activity(), LifecycleOwner {
private lateinit var lifecycleRegistry: LifecycleRegistry
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
lifecycleRegistry = LifecycleRegistry(this)
lifecycleRegistry.markState(Lifecycle.State.CREATED)
}
public override fun onStart() {
super.onStart()
lifecycleRegistry.markState(Lifecycle.State.STARTED)
}
override fun getLifecycle(): Lifecycle {
return lifecycleRegistry
}
}
总的来说,Lifecycle
允许我们将Activity
或Fragment
等组件的声明周期暴露给其它组件,这样可以将Activity
和其它组件进行解耦,其它组件在自己的类中就可以根据不同的生命周期执行不同的操作,不用再去到Activity
的声明周期的方法中执行操作了。
LiveData
LiveData
是一种可观察的数据存储类,与常规的可观察类不同,LiveData
具有生命周期感知能力,也就是说它遵循其它组件(如Activity
,Fragment
,Service
)的生命周期。这种感知能力可确保LiveData
仅更新处于活跃生命周期状态的应用组件观察者。
如果观察者(由Observer
类表示)的声明周期处于STARTED
或RESUMED
状态,则LiveData
会认为该观察者处于活跃状态。LiveData
只会将更新通知给活跃的观察者。为观察LiveData
对象而注册的非活跃观察者不会收到更改通知。
可以注册与实现LifecycleOwner
接口的对象配对的观察者。有了这种关系,当相应的Lifecycle
对象的状态变为DESTROYED
时,便可移除此观察者。这对于Activity
和Fragment
特别有用,因为它们可以放心地观察LiveData
对象而不必担心泄露(当Activity
或Fragment
的生命周期被销毁时,系统会立即退订它们)
使用LiveData
对象
可以按照以下步骤使用LiveData
对象:
- 创建
LiveData
的实例以存储某种类型的数据,这通常在ViewModel
中完成 - 创建可定义
onChanged()
方法的Observer
对象,该方法可以控制当LiveData
对象存储的数据更改时会发生什么。通常情况下,可以在界面控制器(Activity
或Fragment
)中创建Observer
对象 - 使用
observer()
方法将Observer
对象附加到LiveData
对象。observer()
方法会采用LifecycleOwner
对象。这样会使Observer
对象订阅LiveData
对象,以使其收到有关更改的通知。通常情况下,可以在界面控制器(Activity
或Fragment
)中附加Observer
对象。
注意:可以通过使用observerForever(Observer)
方法来注册未关联LifecycleOwner
对象的观察者。在这种情况下,观察者会被视为始终处于活跃状态,因此它始终会收到关于更改的通知。同时,可以调用removeObserver(Observer)
方法来移除观察者。
当更新存储在LiveData
对象中的值时,它会触发所有已注册的观察者(只要附加的LifecycleOwner
处于活跃状态)。
创建LiveData
对象
LiveData
是一种可用于任何数据的封装容器,其中包括可实现Collections
的对象,如List
。LiveData
对象通常存储在ViewModel
对象中,并可通过getter
方法进行访问,如:
class NameViewModel: ViewModel() {
//创建一个包含String类型的LiveData
val currentName: MutableLiveData<String> by lazy {
MutableLiveData<String>()
}
}
最初,LiveData
中的数据并未经过设置。
需要注意的是:确保将用于更新界面的LiveData
对象存储在ViewModel
对象中,而不是将其存储在Activity
或Fragment
中,原因如下:
-
避免
Activity
和Fragment
过于庞大。现在,这些界面控制器负责显示数据,但不负责存储数据状态 -
将
LiveData
实例与特定的Activity
或Fragment
实例分开,并使对象在配置更改后继续存在。
ViewModel
ViewModel
类旨在以注重生命周期的方式存储和管理界面相关的数据。ViewModel
类让数据可在发生屏幕旋转等配置更改后继续存在。
Android框架可以管理界面控制器(如Activity
或Fragment
)的生命周期。Android框架可能会决定销毁或重新创建界面控制器,以响应完全不受用户控制的某些用户操作或设备事件。
如果系统销毁或重新创建界面控制器,则存储在其中的任何临时性界面相关数据都会丢失。例如,应用的某个Activity
中可能包含用户列表,因配置更改而重新创建Activity
后,新Activity
必须重新提取用户列表。对于简单的数据,Activity
可以使用onSaveInstanceState()
方法从onCreate()
中的Bundle
中恢复其数据,但是此方法仅适合可以序列化再反序列化的少量数据,而不适合数量可能较大的数据,如用户列表或位图。
另一个问题是,界面控制器经常需要进行异步调用,这些调用可能需要一些时间才能返回结果。界面控制器需要管理这些调用,并确保系统再其销毁后清理这些调用以避免潜在的内存泄露。此项管理需要大量的维护操作,并且在因配置更改而重新创建对象的情况下,会造成资源的浪费,因为对象可能需要重新发出已经发出过的调用。
诸如Activity
或Fragment
之类的界面控制器主要用于显示界面数据,对用户操作做出响应或处理操作系统通信(如权限请求)。如果要求界面控制器也负责从网络或数据库加载数据,那么会使类越发膨胀。为界面控制器分配过多的责任可能会导致单个类尝试自己处理应用的所有工作,而不是将工作委托给其它类。以这种方式为界面控制器分配过多的责任也会大大增加的是的难度。
总的来说,其实就是将界面控制器中有关数据的操作分离出去,使得界面控制器仅仅负责和用户的交互,显示数据给用户,接收用户的操作,至于数据怎么来的,怎么保存的,这些都不是界面控制器需要考虑的问题。
实现ViewModel
架构组件为界面控制器提供了ViewModel
辅助程序类,该类负责为界面准备数据。在配置更改期间会自动保留ViewModel
对象,以便它们存储的数据立即可供下一个Activity
或Fragment
实例使用。例如:如果需要在应用中显示用户列表,则需要确保将获取和保留该用户列表的责任分配给ViewModel
,而不是Activity
或Fragment
。
class MyViewModel: ViewModel() {
private val users: MutableLiveData<List<User>> by lazy {
MutableLiveData<List<User>>().also {
loadUsers()
}
}
fun getUsers(): LiveData<List<User>> = users
private fun loadUsers(){
//在异步线程中获取用户信息
}
定义了上面的ViewModel
之后就可以从Activity
访问上面的用户列表了。
val model = ViewModelProviders.of(this)[MyViewModel::class.java]
model.getUsers().observe(this,Observer<List<User>>{
//更新UI
})
需要注意的是:ViewModel
绝不能引用视图,Lifecycle
或可能存储对Activity
上下文引用的任何类,以避免发生潜在的内存泄露。
ViewModel
存在的时间比视图或LifecycleOwners
的特定实例存在的时间更长。这同时意味着,可以更轻松地编写涵盖ViewModel
的测试,因为它不了解视图和Lifecycle
对象。ViewModel
对象可以包含LifecycleObservers
,如LiveData
对象。但是,ViewModel
绝不能观察对生命周期感知型可观察对象(如LiveData
)的更改。如果ViewModel
需要Application
上下文(例如查找系统服务),它可以扩展AndroidViewModel
类并设置用于接收Application
的构造函数,因为Application
类会扩展Context
。
ViewModel
的生命周期
ViewModel
对象存在的时间范围是获取ViewModel
时传递给ViewModelProvider
的Lifecyle
。ViewModel
将一直存在于内存中,直到限定其存在时间范围的Lifecycle
永久消失:对于Activity
是在Activity
完成时,而对于Fragment
,是在Fragment
分离时。
下图说明了ViewModel
的生命周期和Activity
生命周期之间的关系,图片来源于官方文档,点击这里查看原文
通常在系统首次调用Activity
对象的onCreate()
方法时请求ViewModel
。系统可能会在Activity
的整个生命周期内多次调用onCreate()
方法,如在旋转设备屏幕时。ViewModel
存在的时间范围是从首次请求ViewMoel
直到Activity
完全销毁。
针对上面所说的多次调用同一个Activity
的onCreate()
方法,返回的是同一个ViewModel
实例,通过查看源码可以知道,ViewModel
保存在Activity
内部的ViewModelStore
对象中,ViewModelStore
内部有一个HashMap
来维护ViewModel
列表,虽然执行ViewModelProviders.of(this)
会返回不同的ViewModelProvider
对象,但是由于每次都是从同一个ViewModelStore
中获取ViewModel
对象,因此可以保证多次执行onCreate()
方法获取到的是同一个ViewModel
实例。