使用生命周期感知型组件

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第25天,点击查看活动详情

使用生命周期感知型组件

生命周期感知型组件可执行操作来响应另一个组件(如ActivityFragment)的生命周期状态的变化。androidx.lifecycle软件包提供了可用于构建生命周期感知型组件的类和接口,这些组件可以根据ActivityFragment的当前生命周期状态自动调整其行为。

由于在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方法中)放置大量的代码,这就会使得它们难以维护。

此外,无法保证组件会在ActivityFragment停止之前启动。在我们需要长时间运行的操作中(如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是一个类,用于存储有关组件(如ActivityFragment)的生命周期信息,并允许其它对象观察此状态。

Lifecycle使用两种主要枚举跟踪其关联组件的生命周期状态:

Event

从框架和Lifecycle类分派的生命周期事件,这些事件映射到ActivityFragment中的回调事件。

State

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

构成Activity生命周期的状态和事件

可以将状态看作途中的节点,将事件看作这些节点之间的边。

类可以通过向其方法添加注解来监控组件的生命周期的状态,然后,可以通过调用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

此接口从各个类(如FragmentAppcompatActivity)抽象化Lifecycle的所有权,并允许编写与这些类搭配使用的组件。任何自定义应用类均可实现LifecycleOwner接口。

实现LifecycleObserver的组件可与实现LifecycleOwner的组件无缝协同工作,因为所有者可以提供生命周期,而观察者可以注册以观察声明周期。

对于上面的位置跟踪示例,可以让LocationUtils类实现LifecycleObserber接口,然后在ActivityonCreate()方法中对其进行初始化。这样,LocationUtils类便可以自动根据Activity的生命周期执行相应的操作。同时,这样做意味着对生命周期状态的变化做出响应的逻辑会在LocationUtils类内部。让各个组件存储自己的逻辑,可使ActivityFragment的逻辑更易于管理。

为了实现上面的效果,主要分成以下三步:

  1. 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("停止请求")
    }
}
  1. Activity中添加需要实现的LifecycleOwner接口,这里由于Support Library 26.1.0及更高版本的ActivityFragment中已经默认实现了这个接口,所以可以省略,主要是要看一下编译的版本。

  2. ActivityonCreate()方法中添加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允许我们将ActivityFragment等组件的声明周期暴露给其它组件,这样可以将Activity和其它组件进行解耦,其它组件在自己的类中就可以根据不同的生命周期执行不同的操作,不用再去到Activity的声明周期的方法中执行操作了。

LiveData

LiveData是一种可观察的数据存储类,与常规的可观察类不同,LiveData具有生命周期感知能力,也就是说它遵循其它组件(如Activity,Fragment,Service)的生命周期。这种感知能力可确保LiveData仅更新处于活跃生命周期状态的应用组件观察者。

如果观察者(由Observer类表示)的声明周期处于STARTEDRESUMED状态,则LiveData会认为该观察者处于活跃状态。LiveData只会将更新通知给活跃的观察者。为观察LiveData对象而注册的非活跃观察者不会收到更改通知。

可以注册与实现LifecycleOwner接口的对象配对的观察者。有了这种关系,当相应的Lifecycle对象的状态变为DESTROYED时,便可移除此观察者。这对于ActivityFragment特别有用,因为它们可以放心地观察LiveData对象而不必担心泄露(当ActivityFragment的生命周期被销毁时,系统会立即退订它们)

使用LiveData对象

可以按照以下步骤使用LiveData对象:

  1. 创建LiveData的实例以存储某种类型的数据,这通常在ViewModel中完成
  2. 创建可定义onChanged()方法的Observer对象,该方法可以控制当LiveData对象存储的数据更改时会发生什么。通常情况下,可以在界面控制器(ActivityFragment)中创建Observer对象
  3. 使用observer()方法将Observer对象附加到LiveData对象。observer()方法会采用LifecycleOwner对象。这样会使Observer对象订阅LiveData对象,以使其收到有关更改的通知。通常情况下,可以在界面控制器(ActivityFragment)中附加Observer对象。

注意:可以通过使用observerForever(Observer)方法来注册未关联LifecycleOwner对象的观察者。在这种情况下,观察者会被视为始终处于活跃状态,因此它始终会收到关于更改的通知。同时,可以调用removeObserver(Observer)方法来移除观察者。

当更新存储在LiveData对象中的值时,它会触发所有已注册的观察者(只要附加的LifecycleOwner处于活跃状态)。

创建LiveData对象

LiveData是一种可用于任何数据的封装容器,其中包括可实现Collections的对象,如ListLiveData对象通常存储在ViewModel对象中,并可通过getter方法进行访问,如:

class NameViewModel: ViewModel() {
    //创建一个包含String类型的LiveData
    val currentName: MutableLiveData<String> by lazy {
        MutableLiveData<String>()
    }
}

最初,LiveData中的数据并未经过设置。

需要注意的是:确保将用于更新界面的LiveData对象存储在ViewModel对象中,而不是将其存储在ActivityFragment中,原因如下:

  • 避免ActivityFragment过于庞大。现在,这些界面控制器负责显示数据,但不负责存储数据状态

  • LiveData实例与特定的ActivityFragment实例分开,并使对象在配置更改后继续存在。

ViewModel

ViewModel类旨在以注重生命周期的方式存储和管理界面相关的数据。ViewModel类让数据可在发生屏幕旋转等配置更改后继续存在。

Android框架可以管理界面控制器(如ActivityFragment)的生命周期。Android框架可能会决定销毁或重新创建界面控制器,以响应完全不受用户控制的某些用户操作或设备事件。

如果系统销毁或重新创建界面控制器,则存储在其中的任何临时性界面相关数据都会丢失。例如,应用的某个Activity中可能包含用户列表,因配置更改而重新创建Activity后,新Activity必须重新提取用户列表。对于简单的数据,Activity可以使用onSaveInstanceState()方法从onCreate()中的Bundle中恢复其数据,但是此方法仅适合可以序列化再反序列化的少量数据,而不适合数量可能较大的数据,如用户列表或位图。

另一个问题是,界面控制器经常需要进行异步调用,这些调用可能需要一些时间才能返回结果。界面控制器需要管理这些调用,并确保系统再其销毁后清理这些调用以避免潜在的内存泄露。此项管理需要大量的维护操作,并且在因配置更改而重新创建对象的情况下,会造成资源的浪费,因为对象可能需要重新发出已经发出过的调用。

诸如ActivityFragment之类的界面控制器主要用于显示界面数据,对用户操作做出响应或处理操作系统通信(如权限请求)。如果要求界面控制器也负责从网络或数据库加载数据,那么会使类越发膨胀。为界面控制器分配过多的责任可能会导致单个类尝试自己处理应用的所有工作,而不是将工作委托给其它类。以这种方式为界面控制器分配过多的责任也会大大增加的是的难度。

总的来说,其实就是将界面控制器中有关数据的操作分离出去,使得界面控制器仅仅负责和用户的交互,显示数据给用户,接收用户的操作,至于数据怎么来的,怎么保存的,这些都不是界面控制器需要考虑的问题。

实现ViewModel

架构组件为界面控制器提供了ViewModel辅助程序类,该类负责为界面准备数据。在配置更改期间会自动保留ViewModel对象,以便它们存储的数据立即可供下一个ActivityFragment实例使用。例如:如果需要在应用中显示用户列表,则需要确保将获取和保留该用户列表的责任分配给ViewModel,而不是ActivityFragment

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时传递给ViewModelProviderLifecyleViewModel将一直存在于内存中,直到限定其存在时间范围的Lifecycle永久消失:对于Activity是在Activity完成时,而对于Fragment,是在Fragment分离时。

下图说明了ViewModel的生命周期和Activity生命周期之间的关系,图片来源于官方文档,点击这里查看原文

ViewModel和Activity生命周期的关系

通常在系统首次调用Activity对象的onCreate()方法时请求ViewModel。系统可能会在Activity的整个生命周期内多次调用onCreate()方法,如在旋转设备屏幕时。ViewModel存在的时间范围是从首次请求ViewMoel直到Activity完全销毁。

针对上面所说的多次调用同一个ActivityonCreate()方法,返回的是同一个ViewModel实例,通过查看源码可以知道,ViewModel保存在Activity内部的ViewModelStore对象中,ViewModelStore内部有一个HashMap来维护ViewModel列表,虽然执行ViewModelProviders.of(this)会返回不同的ViewModelProvider对象,但是由于每次都是从同一个ViewModelStore中获取ViewModel对象,因此可以保证多次执行onCreate()方法获取到的是同一个ViewModel实例。

猜你喜欢

转载自juejin.im/post/7113002536361000997