[toc]
1. 前言
官网地址:https://developer.android.google.cn/topic/libraries/architecture/livedata
其实在使用ViewModel和LifeCycle的时候,我们已经看见了LiveData这个组件。在这篇博客中将继续来了解这个组件。其特点:
LiveData是一种可观察的数据存储器类;- 与常规的可观察类不同,
LiveData具有生命周期感知能力。其感知能力遵循其他组件(如Activity、Fragment或Service)的生命周期; - 感知能力可确保
LiveData仅更新处于活跃生命周期状态的应用组件观察者;活跃的观察者对象声明周期处于STARTED或RESUMED状态; LiveData只会将更新通知给活跃的观察者,非活跃观察者不会收到更改通知;- 当观察者组件的
Lifecycle对象的状态变为DESTROYED时,便可移除此观察者,不必担心泄露(当Activity和Fragment的生命周期被销毁时,系统会立即退订它们)。
不妨再次看下这个图:

1.1 关于观察者模式
我们知道观察者模式,在Java中提供了Observable和Observer来定义被观察者和观察者对象。然后对于可观察对象可以设置观察者,来监听其变化。而在Android的Lifecycle包中提供了对应的LifecycleOwner和LifecycleObserver,而在Activity、Fragment 或 Service等组件中已经实现了LifecycleOwner接口,我们在使用的时候,只需要实现LifecycleObserver来标识类是一个观察者,以注册观察。在之前的博客:【Android Jetpack】LifeCycle一文中也曾提到过通过:
lifecycle.addObserver(myLocationObserver)
来为lifecycle来设置一个观察者对象。类似的,前面提到了:LiveData也是一种可观察的数据存储器类。故而也可以设置观察对象,比如:
// viewModel.get() 获取的为LiveData对象
viewModel.get().observe(this, object : androidx.lifecycle.Observer<Int>{
override fun onChanged(value: Int?) {
livedataText.text = viewModel.getValue().toString()
}
})
2. LiveData 的优势
- 确保界面符合数据状态;
LiveData遵循观察者模式。当底层数据发生变化时,LiveData会通知Observer对象。您可以整合代码以在这些Observer对象中更新界面。 - 不会发生内存泄漏;观察者会绑定到
Lifecycle对象,并在其关联的生命周期遭到销毁后进行自我清理。 - 不会因
Activity停止而导致崩溃;如果观察者的生命周期处于非活跃状态,则它不会接收任何LiveData事件。 - 不再需要手动处理生命周期;
- 共享资源;可以使用单例模式扩展
LiveData对象以封装系统服务,以便在应用中共享它们。LiveData对象连接到系统服务一次,然后需要相应资源的任何观察者只需观察LiveData对象。
更加通俗来说,LiveData是一个持有Activity、Fragment生命周期的数据容器。当数据源发生改变的时候,可以通知观察者。
2.1 案例一:计时器效果
在Activity中定义一个文本,在该控件上每秒自动加一操作。且屏蔽设备配置发生改变后不影响其状态。比如布局文件为ConstraintLayout下放置一个居中的TextView。
2.1.1 实现方式一:ViewModel+LiveData
- 因为需要屏蔽设备配置发生改变的影响,故而这里考虑直接使用
ViewModel; - 考虑代码解耦,这里使用
LiveData来设置观察者,也即是我们需要在ViewModel中设置数据类型为LiveData;
自定义的ViewModel如下:
class MyNumberViewModel : ViewModel() {
private var number: MutableLiveData<Int> = MutableLiveData(0)
fun set(value: Int) {
number.value = value
}
// 工作线程需要使用Post
fun post(value: Int) {
number.postValue(value)
}
fun get(): LiveData<Int> {
return number
}
fun getValue(): Int{
return number.value?:0
}
}
因为我们需要使用LiveData对象来注册观察者对象,故而这里get()方法返回的是LiveData对象。且因为需要开启定时任务,故而可以在工作线程完成,所以提供了post方法,然后在Activity中进行设置:
class LiveDataActivity : AppCompatActivity() {
private lateinit var viewModel: MyNumberViewModel
private val livedataText: TextView by lazy { findViewById(R.id.livedata_text)}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_live_data)
// 获取到ViewModel
viewModel = ViewModelProvider(
this,
ViewModelProvider.NewInstanceFactory()
).get(MyNumberViewModel::class.java)
// 为TextView设置数据
livedataText.text = viewModel.get().toString()
// 开始定时任务
startTimer()
// 注册一个观察者,监听到ViewModel中数据的变化,如果变化执行onChanged()方法
// viewModel.get() 获取的为LiveData对象
viewModel.get().observe(this, object : androidx.lifecycle.Observer<Int>{
override fun onChanged(value: Int?) {
livedataText.text = viewModel.getValue().toString()
}
})
}
// 定时任务
private fun startTimer(){
Timer().schedule(
object : TimerTask() {
override fun run() {
viewModel.post(viewModel.getValue() + 1)
}
},
1000, 1000,
)
}
}
运行即可看见预想的效果。看完上述案例代码之后是否会有疑惑?肯定是有的,因为这里是LiveData的案例,却引入了ViewModel,且将LiveData放入了自定义的ViewModel类中,为什么需要这么做?LiveData对象本身就可以被观察,为什么还需要放置到ViewModel中?
答:其实在刚开始的要求部分我已经做了引导,需要做到屏蔽设备配置发生改变后不影响其状态,而很方便的ViewModel就可以轻松做到这一点。如果LiveData实例与特定的 Activity 或 Fragment 实例并没有分离开,那么在 Activity 或 Fragment 经历onDestory的时候,LiveData中的数据也就没了。
且上述程序存在一个致命Bug,由于每次屏幕旋转都会执行onCreate,故而会开启多个定时任务,故而这里是不符合题意的。当然,可以加上标志处理,这里不再解决。
2.1.2 实现方式二:configChange
当然,上述使用ViewModel只是一种方式,也可以配置清单文件,设置onConfigChange属性,这里来尝试写一下:
<activity
android:name=".LiveDataActivity"
android:configChanges="orientation|screenSize"
android:exported="true">
然后将LiveData直接定义在Activity中:
class LiveDataActivity : AppCompatActivity() {
private val livedataText: TextView by lazy { findViewById(R.id.livedata_text) }
private val liveData: MutableLiveData<Int> by lazy { MutableLiveData<Int>(0) }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_live_data)
// 为TextView设置数据
livedataText.text = liveData.value.toString()
// 开始定时任务
startTimer()
// 注册一个观察者,监听到ViewModel中数据的变化,如果变化执行onChanged()方法
// viewModel.get() 获取的为LiveData对象
liveData.observe(this) {
livedataText.text = liveData.value.toString()
}
}
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
Log.e("TAG", "onConfigurationChanged: ")
}
// 定时任务
private fun startTimer() {
Timer().schedule(
object : TimerTask() {
override fun run() {
liveData.postValue((liveData.value ?: 0) + 1)
Log.e("TAG", "run: ${liveData.value ?: 0}", )
}
},
1000, 1000,
)
}
}