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 类分派的生命周期事件。这些事件映射到
activity
和fragment
中的回调事件。 - 状态:由 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()
),该方法必须由类实现。 此接口从各个类(如 Fragment
和 AppCompatActivity
)抽象化 Lifecycle
的所有权,并允许编写与这些类搭配使用的组件。任何自定义应用类均可实现 LifecycleOwner
接口。
实现 DefaultLifecycleObserver
的组件可与实现 LifecycleOwner
的组件完美配合,因为所有者可以提供生命周期,而观察者可以注册以观察生命周期。
对于位置跟踪示例,我们可以让 MyLocationListener
类实现 DefaultLifecycleObserver
,然后在 onCreate()
方法中使用 activity
的 Lifecycle
对其进行初始化。这样,MyLocationListener
类便可以“自给自足”,这意味着,对生命周期状态的变化做出响应的逻辑会在 MyLocationListener
(而不是在 activity
)中进行声明。让各个组件存储自己的逻辑可使 activity
和 fragment
逻辑更易于管理。
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
及更高版本中的 Fragment
和 Activity
均已实现 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的源码流程进行了简要分析。
简要总结:
Activity
的getLifecycle
方法返回的是一个LifecycleRegistry
对象,LifecycleRegistry
类正是Lifecycle
接口的实现者。LifecycleRegistry
通过弱引用持有了LifecycleOwner
的对象,也就是Activity
对象 。- 在
Activity
的onCreate()
方法中添加了一个透明的ReportFragment
来专门处理生命周期。 - 在
ReportFragment
中,API >= 29
时的处理逻辑是,调用LifecycleCallbacks.registerIn(activity)
方法, 其内容是:为Activity
注册一个Application.ActivityLifecycleCallbacks
的回调接口实现类,这个Callback
被Actvity
内部保存了起来。在 ATMS 跨进程调用ActivityThread
中的handleStartActivity
方法时,回调Activity
的performStart()
方法,进而回调其保存的Callback
的回调方法,在其中拿到Activity持有的LifecycleRegistry
对象进行分发处理,最终调用到LifecycleEventObserver
观察者对象的onStateChanged()
接口方法。 - 在
ReportFragment
中,API < 29
时的处理逻辑是, 在ReportFragment
的生命周期方法里,进行事件分发,进而回调LifecycleEventObserver
的onStateChanged()
接口方法。 - 普通的
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的前后台切换。
当应用启动时会依次调用:
- MyApp --> onCreate
- MyApp --> onStart
- MyApp --> onResume
当应用按Home键返回桌面或者切换到最近应用列表界面时会依次调用:
- MyApp --> onPause
- MyApp --> onStop
当从桌面返回到应用时会依次调用:
- MyApp --> onStart
- MyApp --> onResume
可以看到只有应用第一次创建时会回调 onCreate
方法,后面应用前后台切换时会在 onPause/onStop
和 onStart/onResume
两组回调方法之间切换,可以根据需要选择对应的回调方法进行监听业务处理。
LiveData
LiveData 是一种可观察的数据存储器类。与常规的可观察类不同,LiveData 具有生命周期感知能力,意指它遵循其他应用组件(如 activity、fragment 或 service)的生命周期。这种感知能力可确保 LiveData 仅更新处于活跃生命周期状态的应用组件观察者。
如果观察者(由 Observer 类表示)的生命周期处于 STARTED
或 RESUMED
状态,则 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
如果观察者的生命周期处于 STARTED
或 RESUMED
状态,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
将通过 Fragment
的 getViewLifecycleOwner()
方法来获取 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