LiveData是Architecture Components中的重要一员,本篇将带领大家从源码层面深层次理解它的机制。(此篇不是零基础讲解LiveData和ViewModel的使用,建议往下看的小伙伴先熟悉LiveData和ViewModel的基本使用)
-
使用:
首先在build.gradle中引入:
def livedata_version = '2.3.0-alpha01' def lifecycle_version = '2.2.0' def coroutines_android_version = '1.3.3' // LiveData和ViewModel相关 implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version" implementation "androidx.lifecycle:lifecycle-livedata-ktx:$livedata_version" implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version" // 协程相关 implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_android_version" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_android_version"
LiveData和ViewModel是必须依赖的,而协程相关依赖不是必须的,而我这里主要是用到了协程。
涉及到的类:
- MainActivity:
class MainActivity : AppCompatActivity() { private val mViewModel: MainViewModel by lazy { ViewModelProvider(this, MainViewModelFactory())[MainViewModel::class.java] } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) btn_second.setOnClickListener { Intent(this, SecondActivity::class.java).run { startActivity(this) } } mViewModel.periodTextData.observe(this, Observer { Log.e(TAG, "updated: $it") tvContent.text = it }) mViewModel.dataWithInit.observe(this, Observer { Log.e(TAG, "init received $it") }) mViewModel.dataWithNoVal.observe(this, Observer { Log.e(TAG, "data with no val : $it") }) mViewModel.count() } companion object { private val TAG = MainActivity::class.java.name } }
activity.main.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".MainActivity"> <TextView android:id="@+id/tvContent" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello World!"/> <Button android:id="@+id/btn_second" android:layout_width="wrap_content" android:text="go second" android:layout_height="wrap_content" /> </LinearLayout>
MainViewModel.kt
class MainViewModel : ViewModel() { val periodTextData = MutableLiveData<String>() val dataWithInit = MutableLiveData("hello") val dataWithNoVal = MutableLiveData<String>() fun count() { viewModelScope.launch { repeat(5) { delay(5000) periodTextData.value = "count: $it" } } } } class MainViewModelFactory : ViewModelProvider.Factory { override fun <T : ViewModel?> create(modelClass: Class<T>): T { return MainViewModel() as T } }
SecondActivity
class SecondActivity : AppCompatActivity() { }
-
额外补充:
- viewModelScope,这是lifecycle-viewmodel-ktx依赖中提供给开发者用来启动协程的scope
- 不用着急,这些代码我们一个个来解析。
-
我们假设你已经对LiveData有了基本的了解,我们这里直接从源码入手。我们直接从LiveData类的observe()方法的文档注释入手:
-
Adds the given observer to the observers list within the lifespan of the given owner. The events are dispatched on the main thread.
-
If LiveData already has data set, it will be delivered to the observer. The observer will only receive events if the owner is in Lifecycle.State.STARTED or Lifecycle.State.RESUMED state (active).
-
If the owner moves to the Lifecycle.State.DESTROYED state, the observer will automatically be removed.
-
When data changes while the owner is not active, it will not receive any updates. If it becomes active again, it will receive the last available data automatically.
-
LiveData keeps a strong reference to the observer and the owner as long as the given LifecycleOwner is not destroyed. When it is destroyed, LiveData removes references to the observer & the owner.
-
If the given owner is already in Lifecycle.State.DESTROYED state, LiveData ignores the call.
-
If the given owner, observer tuple is already in the list, the call is ignored.
-
If the observer is already in the list with another owner, LiveData throws an IllegalArgumentException.
我们这里将整段文档注释分成了8个点,下面我们一一从实例运行和源码分析角度上来验证这里提到的特性:
1) 第6点:
2) 第8点:
- 这里出现的LifecycleBoundObserver,它会将LifecycleOwner(Activity、Fragment以及Service等)以及observer(observe这个方法的第二个参数)封装到一起。
- mObservers:observer到LifecycleBoundObserver的映射,它的类型是SafeIterableMap。
3) 第7点:
4) 第2点。
在分析源码之前,我们可以借助于前面已经创建好的工程以及项目代码,我们在MainViewModel里定义了两个MutableLiveData:
class MainViewModel: ViewModel(){ val dataWithInit = MutableLiveData("hello") val dataWithNoVal = MutableLiveData<String>() }
再在MainActivity的onCreate方法里:
mViewModel.dataWithInit.observe(this, Observer { Log.e(TAG, "init received $it") }) mViewModel.dataWithNoVal.observe(this, Observer { Log.e(TAG, "data with no val : $it") })
运行查看日志输出,只有一行输出:
init received hello
也就是说,带初始值的MutableLiveData,一旦被observe,立即会把已有的数据发送给Observer;不带初始值的MutableLiveData则不会这样。这也就验证了这里提到的:If LiveData already has data set, it will be delivered to the observer.
那么为什么会这样呢?我们一起来分析源代码:
执行流程就会来到LifecycleRegistry的addObserver()方法:
那么这个mLifeCycleObserver是谁?看ObserverWithState的构造器
ObserverWithState(LifecycleObserver observer, State initialState) { mLifecycleObserver = Lifecycling.lifecycleEventObserver(observer); mState = initialState; }
执行流程又回到LifecycleBoundObserver的onStateChanged():
那么为什么不带初始值的LiveData在刚开始observe的时候,没有onChanged()通知呢:
不带参数(即不带初始值)的构造器,mVersion = START_VERSION,这样在considerNotify()方法里:
在这个地方就直接return回去了,不会调用下面的onChanged回调。
5) 第3点:If the owner moves to the Lifecycle.State.DESTROYED state, the observer will automatically be removed
要说明这一点,我们需要知道ReportFragment以及ComponentActivity的存在:
-
我们重点关注androidx.core.app.ComponentActivity:
这里我们会看到熟悉的LifecycleOwner 接口以及LifecycleRegisty实现类。然后我们再来看ComponentActivity的onCreate()方法里:
会看到ReportFragment的身影,并且会将ReportFragment添加到Activity中。如果你看过RxPermissions的源码,你就会熟悉这样的套路:在Activity中添加一个Fragment,然后通过该Fragment来感知Activity的相应生命周期回调。由于本小点是在讨论Lifecycle.State.DESTROYED state,所以我们来重点关注ReportFragment的onDestory方法:
不管是进到哪个分支条件,最终都会调用LifecycleRegistry.handleLifecycleEvent()方法:
这样执行流程又会到了我们之前提到的onStateChanged()方法:
看,是不是就把对应Observer移除掉了。
6) 下面我们就来验证第4点文档注释:When data changes while the owner is not active, it will not receive any updates. If it becomes active again, it will receive the last available data automatically
和分析第5点一样,我们先不翻看源码,我们通过一个实例来验证。这个时候,我们前面创建的项目工程以及相关类就可以起作用了。
在MainViewModel类中添加方法:
class MainViewModel : ViewModel() {
val periodTextData = MutableLiveData<String>()
fun count() {
viewModelScope.launch {
repeat(5) {
delay(2000)
periodTextData.value = "count: $it"
}
}
}
}
在MainActivity的onCreate方法中:
override fun onCreate(savedInstanceState: Bundle?) {
btn_second.setOnClickListener { Intent(this, SecondActivity::class.java).run { startActivity(this) } }
mViewModel.periodTextData.observe(this, Observer {
Log.e(TAG, "updated: $it")
tvContent.text = it
})
mViewModel.count()
}
先解析一下viewModelScope.launch{}中的代码吧: 我们通过viewModelScope启动了一个协程,协程中的代码逻辑是每隔2秒,改变 periodTextData这个MutabeLiveData的值,一共5次。接着就是MainActivity中添加的代码:btn_second就是一个Button,它的点击响应逻辑也就仅仅是跳转到SecondActivity;tvContent是一个TextView,它会显示periodTextData的值。将项目运行起来,我们的操作逻辑是这样的:一打开页面,等个5秒或是10秒钟,看看是否会收到periodTextData值更新的通知(可以通过日志),然后点击btn_second按钮,跳转到SecondActivity,由于协程的代码还是在跑着的,也就是说还在持续更新periodTextData的值,我们可以关注这个时候看看是否会收到periodTextData值更新的通知。最后,我们再从SecondActivity返回,看看tvConten的显示。语言描述起来,有点复杂,我为大家准备了一张gif动图:
日志输出如下:
也就是说,在我们跳转并停留在SecondActivity 这段时间里,periodTextData的值一直在更新,但是MainActivity中注册Observer没有接收到值更新的通知。但是一旦我们回到MainActivity(使得MainActivity编程RESUME状态),立马会接收到值更新通知。
通过这个实例,我们已经验证了文档注释中提到的:When data changes while the owner is not active, it will not receive any updates. If it becomes active again, it will receive the last available data automatically。
那么源码层面是怎么体现出来的呢?
和上一条分析一样,我们需要查看ReportFragment的代码实现,找到onResume()方法:
以上这些分析流程,我们在前面多条的分析过程中,都已经走过逻辑。最终回到considerNotify()方法:
而在LiveData的setValue()方法里:
每调用一次setValue方法,mVersion的值都会加1。那么considerNotify()中的observer.mLastVersion >= mVersion的判断条件就不会满足,自然也就会走到下面的onChanged()方法里,从而触发我们的Observer中的逻辑。可见,这个LiveData的mVersion是至关重要的,顾名思义,它就是记录LiveData中value值的版本。
至此,整篇文章对于LiveData的源码分析就结束了。文章中存在大量的截图,感谢您的阅读!
关于ViewModel的深层次源码分析可以参考此篇。