文章目录
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,
)
}
}