Jetpack架构组件库:LifecycleLiveDataViewModel

Posted 川峰

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Jetpack架构组件库:LifecycleLiveDataViewModel相关的知识,希望对你有一定的参考价值。

Lifecycle

添加依赖

dependencies 
     def lifecycle_version = "2.5.1" 

     // ViewModel
     implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
     // ViewModel utilities for Compose
     implementation "androidx.lifecycle:lifecycle-viewmodel-compose:$lifecycle_version"
     // LiveData
     implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
     // Lifecycles only (without ViewModel or LiveData)
     implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycle_version"

     // Saved state module for ViewModel
     implementation "androidx.lifecycle:lifecycle-viewmodel-savedstate:$lifecycle_version" 
 

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

  • 事件:从框架和 Lifecycle 类分派的生命周期事件。这些事件映射到 activityfragment 中的回调事件。
  • 状态:由 Lifecycle 对象跟踪的组件的当前状态。


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

实现 LifecycleObserver 观察者

实现 LifecycleObserver 观察者,有两种方法,一种是实现 LifecycleEventObserver 接口,覆写 onStateChanged() 方法,在该方法中监听不同的生命周期事件:

import android.util.Log
import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.Lifecycle.Event

class MyLifecycleObserver: LifecycleEventObserver 

    override fun onStateChanged(source: LifecycleOwner, event: Event) 
        Log.e(TAG, "onStateChanged 事件来源: $source.javaClass.name")
        when (event) 
            Event.ON_CREATE -> 
                Log.e(TAG, "onStateChanged: ON_CREATE")
            
            Event.ON_START -> 
                Log.e(TAG, "onStateChanged: ON_START")
            
            Event.ON_RESUME -> 
                Log.e(TAG, "onStateChanged: ON_RESUME")
            
            Event.ON_PAUSE -> 
                Log.e(TAG, "onStateChanged: ON_PAUSE")
            
            Event.ON_STOP -> 
                Log.e(TAG, "onStateChanged: ON_STOP")
            
            Event.ON_DESTROY -> 
                Log.e(TAG, "onStateChanged: ON_DESTROY")
            
            Event.ON_ANY -> 
                Log.e(TAG, "onStateChanged: ON_ANY")
            
            else -> 
        
    

    companion object 
        private val TAG = MyLifecycleObserver::class.java.simpleName
    

另一种方法是实现 DefaultLifecycleObserver 接口,该接口是支持默认方法实现的接口类(java8),然后选择你要监听的生命周期方法进行覆写即可:

import android.util.Log
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner

class MyLifecycleObserver2 : DefaultLifecycleObserver 
    override fun onCreate(owner: LifecycleOwner) 
        Log.e(TAG, "onCreate: ")
    

    override fun onStart(owner: LifecycleOwner) 
        Log.e(TAG, "onStart: ")
    

    override fun onResume(owner: LifecycleOwner) 
        Log.e(TAG, "onResume: ")
    

    override fun onPause(owner: LifecycleOwner) 
        Log.e(TAG, "onPause: ")
    

    override fun onStop(owner: LifecycleOwner) 
        Log.e(TAG, "onStop: ")
    

    override fun onDestroy(owner: LifecycleOwner) 
        Log.e(TAG, "onDestroy: ")
    

    companion object 
        private val TAG = MyLifecycleObserver2::class.java.simpleName
    

实现 LifecycleObserver 观察者对象之后,通过调用 Lifecycle 类的 addObserver() 方法来添加观察者对象:

class MainActivity : ComponentActivity()  

    override fun onCreate(savedInstanceState: Bundle?) 
        super.onCreate(savedInstanceState)
        lifecycle.addObserver(MyLifecycleObserver())
        lifecycle.addObserver(MyLifecycleObserver2())
    

添加观察者对象通常是在 Activity 或者 Fragment 中进行,因为这二者都是LifecycleOwner接口实现者,它们天生自带 Lifecycle实例。

LifecycleOwner

LifecycleOwner 是单一方法接口,表示类具有 Lifecycle。它具有一种方法(即 getLifecycle()),该方法必须由类实现。 此接口从各个类(如 FragmentAppCompatActivity)抽象化 Lifecycle 的所有权,并允许编写与这些类搭配使用的组件。任何自定义应用类均可实现 LifecycleOwner 接口。

实现 DefaultLifecycleObserver 的组件可与实现 LifecycleOwner 的组件完美配合,因为所有者可以提供生命周期,而观察者可以注册以观察生命周期。

对于位置跟踪示例,我们可以让 MyLocationListener 类实现 DefaultLifecycleObserver,然后在 onCreate() 方法中使用 activityLifecycle 对其进行初始化。这样,MyLocationListener 类便可以“自给自足”,这意味着,对生命周期状态的变化做出响应的逻辑会在 MyLocationListener(而不是在 activity)中进行声明。让各个组件存储自己的逻辑可使 activityfragment 逻辑更易于管理。

internal class MyLocationListener(
        private val context: Context,
        private val lifecycle: Lifecycle,
        private val callback: (Location) -> Unit
): DefaultLifecycleObserver 

    private var enabled = false

    override fun onStart(owner: LifecycleOwner) 
        if (enabled) 
            // connect
        
    

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

    override fun onStop(owner: LifecycleOwner) 
        // disconnect if connected
    

对于此实现,LocationListener 类可以完全感知生命周期。如果我们需要从另一个 activity 或 fragment 使用 LocationListener,只需对其进行初始化。所有设置和拆解操作都由类本身管理。

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

    override fun onCreate(...) 
        myLocationListener = MyLocationListener(this, lifecycle)  location ->
            // update UI
        
        Util.checkUserStatus  result ->
            if (result) 
                myLocationListener.enable()
            
        
    

实现自定义 LifecycleOwner

支持库 26.1.0 及更高版本中的 FragmentActivity 均已实现 LifecycleOwner 接口。

如果你有一个自定义类并希望使其成为 LifecycleOwner,可以使用 LifecycleRegistry 类,但需要将事件转发到该类,如以下代码示例中所示:

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的实现原理

请参考我的另一篇博文:【深入理解Kotlin协程】lifecycleScope源码追踪扒皮 ,其中对Lifecycle的源码流程进行了简要分析。

简要总结:

  • ActivitygetLifecycle 方法返回的是一个 LifecycleRegistry 对象,LifecycleRegistry类正是Lifecycle接口的实现者。
  • LifecycleRegistry通过弱引用持有了LifecycleOwner的对象,也就是Activity对象 。
  • ActivityonCreate()方法中添加了一个透明的 ReportFragment 来专门处理生命周期。
  • ReportFragment 中, API >= 29 时的处理逻辑是,调用 LifecycleCallbacks.registerIn(activity) 方法, 其内容是:为Activity注册一个Application.ActivityLifecycleCallbacks的回调接口实现类,这个CallbackActvity内部保存了起来。在 ATMS 跨进程调用ActivityThread中的 handleStartActivity方法时,回调ActivityperformStart()方法,进而回调其保存的Callback的回调方法,在其中拿到Activity持有的LifecycleRegistry对象进行分发处理,最终调用到LifecycleEventObserver观察者对象的onStateChanged()接口方法。
  • ReportFragment 中, API < 29 时的处理逻辑是, 在ReportFragment的生命周期方法里,进行事件分发,进而回调LifecycleEventObserveronStateChanged()接口方法。
  • 普通的Fragment中生命周期的处理流程类似,也是交给LifecycleRegistry来分发,但是生命周期方法的触发是通过宿主 Activity 实现的。

监听 Application 的前后台切换

监听 Application 的前后台切换在以前是通过 registerActivityLifecycleCallbacks() 判断当前处于栈顶的 Activity 的状态来判断的,现在Jetpack提供了一个简单的API ProcessLifecycleOwner 来直接注册监听:

class MyApp: Application()   
    override fun onCreate() 
        super.onCreate()
        ProcessLifecycleOwner.get().lifecycle.addObserver(AppObserver())
    
    class AppObserver : DefaultLifecycleObserver 

        override fun onCreate(owner: LifecycleOwner) 
            Analytics.report("MyApp --> onCreate")
        

        override fun onStart(owner: LifecycleOwner) 
            Analytics.report("MyApp --> onStart")
        

        override fun onResume(owner: LifecycleOwner) 
            Analytics.report("MyApp --> onResume")
        

        override fun onPause(owner: LifecycleOwner) 
            Analytics.report("MyApp --> onPause")
        

        override fun onStop(owner: LifecycleOwner) 
            Analytics.report("MyApp --> onStop")
        

        override fun onDestroy(owner: LifecycleOwner) 
            Analytics.report("MyApp --> onDestroy")
        
    
	
	object Analytics 
	    fun report(state: String) = Log.e("Analytics", state)
	

这里的Observer是复用了Activity的LifecycleObserver,并没有提供一个专门为Application使用的Observer,不过不影响我们监听Application的前后台切换。

当应用启动时会依次调用:

  1. MyApp --> onCreate
  2. MyApp --> onStart
  3. MyApp --> onResume

当应用按Home键返回桌面或者切换到最近应用列表界面时会依次调用:

  1. MyApp --> onPause
  2. MyApp --> onStop

当从桌面返回到应用时会依次调用:

  1. MyApp --> onStart
  2. MyApp --> onResume

可以看到只有应用第一次创建时会回调 onCreate 方法,后面应用前后台切换时会在 onPause/onStoponStart/onResume 两组回调方法之间切换,可以根据需要选择对应的回调方法进行监听业务处理。

LiveData

LiveData 是一种可观察的数据存储器类。与常规的可观察类不同,LiveData 具有生命周期感知能力,意指它遵循其他应用组件(如 activity、fragment 或 service)的生命周期。这种感知能力可确保 LiveData 仅更新处于活跃生命周期状态的应用组件观察者

如果观察者(由 Observer 类表示)的生命周期处于 STARTEDRESUMED 状态,则 LiveData 会认为该观察者处于活跃状态。LiveData 只会将更新通知给活跃的观察者。为观察 LiveData 对象而注册的非活跃观察者不会收到更改通知。 (这一点非常重要,也就是说在页面不可见时,观察者对象中的处理逻辑不会被执行,从而节约宝贵的内存和CPU资源。)

使用 LiveData 具有以下优势:

  • 确保界面符合数据状态: LiveData 遵循观察者模式。当底层数据发生变化时,LiveData 会通知 Observer 对象。你可以整合代码以在这些 Observer 对象中更新界面。这样一来,您无需在每次应用数据发生变化时更新界面,因为观察者会替您完成更新。
  • 不会发生内存泄漏: 观察者会绑定到 Lifecycle 对象,并在其关联的生命周期遭到销毁后进行自我清理。
  • 不会因 Activity 停止而导致崩溃: 如果观察者的生命周期处于非活跃状态(如返回堆栈中的 activity),它便不会接收任何 LiveData 事件。
  • 不再需要手动处理生命周期: 界面组件只是观察相关数据,不会停止或恢复观察。LiveData 将自动管理所有这些操作,因为它在观察时可以感知相关的生命周期状态变化。
  • 数据始终保持最新状态: 如果生命周期变为非活跃状态,它会在再次变为活跃状态时接收最新的数据。例如,曾经在后台的 Activity 会在返回前台后立即接收最新的数据。
  • 适当的配置更改: 如果由于配置更改(如设备旋转)而重新创建了 activity 或 fragment,它会立即接收最新的可用数据。
  • 共享资源 你可以使用单例模式扩展 LiveData 对象以封装系统服务,以便在应用中共享它们。LiveData 对象连接到系统服务一次,然后需要相应资源的任何观察者只需观察 LiveData 对象。

创建 LiveData

LiveData 对象通常存储在 ViewModel 对象中,并可通过 getter 方法进行访问,如以下示例中所示:

class MyViewModel : ViewModel() 

    private val _currentName: MutableLiveData<String> by lazy  MutableLiveData<String>("Tom") 

    val currentName: LiveData<String> = _currentName

    fun updateName(name: String) 
        _currentName.value = name // 只能在主线程调用
        // _currentName.postValue(name) // 主线程、子线程中都可以调用
    

LiveData 没有公开可用的方法来更新存储的数据。MutableLiveData 类公开 setValue(T)postValue(T) 方法来修改存储在 LiveData 对象中的值。通常情况下会在 ViewModel 中使用 MutableLiveData,然后 ViewModel 只会向观察者公开不可变的 LiveData 对象

setValue(T)postValue(T) 方法的区别:

  • liveData.postValue():可以在任意的线程下执行, 内部会post主线程调用setValue
  • liveData.setValue():只能在主线程中执行

LiveData 的 postValue() 方法有一定的延时,如果想在 postValue()之后移除观察者,马上调用移除方法,可能会导致部分观察者收不到前面postValue的数据,此时应该使用LiveData的setValue(),它是即时的进行分发,在下一行代码进行移除观察者也没问题。

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

  • 避免 Activity 和 Fragment 过于庞大。现在,这些界面控制器负责显示数据,但不负责存储数据状态。
  • 将 LiveData 实例与特定的 Activity 或 Fragment 实例分离开,并使 LiveData 对象在配置更改后继续存在。

观察 LiveData 对象

在大多数情况下,应用组件的 onCreate() 方法是开始观察 LiveData 对象的正确着手点,原因如下:

  • 确保系统不会从 Activity 或 Fragment 的 onResume() 方法进行冗余调用。
  • 确保 activity 或 fragment 变为活跃状态后具有可以立即显示的数据。一旦应用组件处于 STARTED 状态,就会从它正在观察的 LiveData 对象接收最新值。只有在设置了要观察的 LiveData 对象时,才会发生这种情况。

通常,LiveData 仅在数据发生更改时才发送更新,并且仅发送给活跃观察者。此行为的一种例外情况是,观察者从非活跃状态更改为活跃状态时也会收到更新。此外,如果观察者第二次从非活跃状态更改为活跃状态,则只有在自上次变为活跃状态以来值发生了更改时,它才会收到更新

以下示例代码说明了如何开始观察 LiveData 对象:

class MyActivity: ComponentActivity() 

    private val myViewModel: MyViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) 
        super.onCreate(savedInstanceState)
        setContentView(R.layout.livedata_test)
        val nameTextView = findViewById<TextView>(R.id.tv_user_name)
        val updateBtn = findViewById<Button>(R.id.btn_update)

        myViewModel.currentName.observe(this)  newName ->
            nameTextView.text = newName
        

        updateBtn.setOnClickListener 
            myViewModel.updateName("张三")
        
    

该示例中演示的是按下按钮调用的 setValue() 方法,但也可以出于各种各样的原因调用 setValue()postValue() 来更新 name,这些原因可能包括响应网络请求或数据库加载完成等等。在所有情况下,调用 setValue()postValue() 都会触发观察者并更新界面。

注意:你必须保证调用 setValue(T) 方法时是从主线程更新 LiveData 对象。如果在子线程中执行代码,可以改用 postValue(T) 方法来更新 LiveData 对象。

应用架构中的 LiveData

LiveData 具有生命周期感知能力,遵循 activity 和 fragment 等实体的生命周期。你可以使用 LiveData 在这些生命周期所有者和生命周期不同的其他对象(例如 ViewModel 对象)之间传递数据。ViewModel 的主要责任是加载和管理与界面相关的数据,因此非常适合作为用于保留 LiveData 对象的备选方法。你可以在 ViewModel 中创建 LiveData 对象,然后使用这些对象向界面层公开状态。

activity 和 fragment 不应保留 LiveData 实例,因为它们的用途是显示数据,而不是保持状态。此外,如果 activity 和 fragment 无需保留数据,还可以简化单元测试的编写。

你可能会想在数据层类中使用 LiveData 对象,但 LiveData 并不适合用于处理异步数据流。如果您需要在应用的其他层中使用数据流,请考虑使用 Kotlin Flow,然后使用 asLiveData()ViewModel 中将 Kotlin Flow 转换成 LiveData。如需详细了解如何搭配使用 Kotlin Flow 与 LiveData,请学习此 Codelab

扩展 LiveData

如果观察者的生命周期处于 STARTEDRESUMED 状态,LiveData 会认为该观察者处于活跃状态。以下示例代码说明了如何扩展 LiveData 类:

class StockLiveData(symbol: String) : LiveData<BigDecimal>() 
    private val stockManager = StockManager(symbol)

    private val listener =  price: BigDecimal ->
        value = price
    

    override fun onActive() 
        stockManager.requestPriceUpdates(listener)
    

    override fun onInactive() 
        stockManager.removeUpdates(listener)
    

本示例中的价格监听器实现包括以下重要方法:

  • 当 LiveData 对象具有活跃观察者时,会调用 onActive() 方法。这意味着,你需要从此方法开始观察股价更新。
  • 当 LiveData 对象没有任何活跃观察者时,会调用 onInactive() 方法。由于没有观察者在监听,因此没有理由与 StockManager 服务保持连接。

setValue(T) 方法将更新 LiveData 实例的值,并将更改告知活跃观察者。

使用 StockLiveData 类,如下所示:

public class MyFragment : Fragment() 
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) 
        super.onViewCreated(view, savedInstanceState)
        val myPriceListener: LiveData<BigDecimal> = StockLiveData("xxxx")
        myPriceListener.observe(viewLifecycleOwner, Observer<BigDecimal>  price: BigDecimal? ->
            // Update the UI.
        )
    

observe() 方法的第一个参数owner将通过 FragmentgetViewLifecycleOwner() 方法来获取 LifecycleOwner 对象进行传递。这样做表示此观察者已绑定到与所有者关联的 Lifecycle 对象,这意味着:

  • 如果 Lifecycle 对象未处于活跃状态,那么即使值发生更改,也不会调用观察者。
  • 销毁 Lifecycle 对象后,会自动移除观察者。

LiveData 对象具有生命周期感知能力,这一事实意味着你可以在多个 Activity、Fragment 和 Service 之间共享这些对象。为使示例保持简单,你可以将 LiveData 类实现为一个单例,如下所示:

class StockLiveData(symbol: String) : LiveData<BigDecimal>() 
    private val stockManager: StockManager = StockManager(symbol)

    private val listener =  price: BigDecimal ->
        value = price
    

    override fun onActive() 
        stockManager.requestPriceUpdates(listener)
    

    override fun onInactive() 
        stockManager.removeUpdates(listener)
    

    companion object 
        private lateinit var sInstance: StockLiveData

        @MainThread
        fun get(symbol: String): StockLiveData 
            sInstance = if (::sInstance.isInitialized) sInstance else StockLiveData(symbol)
            return sInstance
        
    

在 Fragment 中使用它,如下所示:

class MyFragment : Fragment() 

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) 
        super.onViewCreated(view, savedInstanceState)
        StockLiveData.get(symbol).observe(viewLifecycleOwner, Observer<BigDecimal>  price: BigDecimal? ->
            // Update the UI.
        )

    

多个 Fragment 和 Activity 可以观察 MyPriceListener 实例。仅当一个或多项系统服务可见且处于活跃状态时,LiveData 才会连接到该服务。

转换 LiveData

当需要根据另一个LiveData实例的值返回不同的 LiveData 实例时,可以通过 Transformations.map() 或者 Transformations.switchMap() 方法来实现:

val userLiveData: LiveData<User> = UserLiveData()
val userName: LiveData<String> = Transformations.map(userLiveData) 
    user -> "$user.nameJetpack架构组件库:LifecycleLiveDataViewModel

Jetpack架构组件库:DataStore

Jetpack架构组件库:DataStore

Jetpack架构组件库:WorkManager

Jetpack架构组件库:WorkManager

Jetpack架构组件库:Hilt