谷歌在今年的I/O大会上推出了Jetpack的概念,意图在于统一框架与UI组件。所以我也将项目架构往这一概念上靠齐。
Jetpack
今天我们来研究架构层最重要的两个类LiveData和ViewModel之一的LiveData
本文涉及到的代码已上传到Github,欢迎star、fork
LiveData简介
LiveData是一个可以感知Activity、Fragment生命周期的数据容器。当LiveData所持有的数据发生改变时,它会通知相应的观察者进行数据更新。由于Activity、Fragment都已实现LifecycleOwner接口,所以持有LifecycleOwner引用的LiveData在LifecycleOwner的生命周期处于started或resumed时可以作出相应更新,而在LifecycleOwner处于被销毁时停止更新。
AppCompatActivity继承自ComponentActivity FragmentLiveData的功能
从官方文档来看,LiveData的使用有以下几大好处
-
保证UI状态和数据的统一
LiveData采用了观察者设计模式。当生命周期状态改变时,LiveData会通知Observer对象。每次应用程序数据更改时,都会通知观察者对象,从而更新UI。 -
减少内存泄漏
LiveData能够感知到组件的生命周期,观察者绑定到生命周期对象,并在其相关生命周期被破坏后自行清理。 -
当Activity停止时不会引起崩溃
这是因为组件处于非激活状态时,不会收到LiveData中数据变化的通知 -
不需要额外的手动处理来响应生命周期的变化
这一点同样是因为LiveData能够感知组件的生命周期,所以就完全不需要在代码中告诉LiveData组件的生命周期状态。 -
组件和数据相关的内容能实时更新
组件在前台的时候能够实时收到数据改变的通知,这是可以理解的。当组件从后台到前台来时,LiveData能够将最新的数据通知组件,这两点就保证了组件中和数据相关的内容能够实时更新。 -
资源共享
通过继承LiveData类,然后将该类定义成单例模式,在该类封装监听一些系统属性变化,然后通知LiveData的观察者
实例
LiveData已经被添加到原生androidx.lifecycle
模块中,所以不需要我们再单独的添加任何库了
LiveData的代码比较简单,我们根据代码来了解其工作原理。我定义一个NetWorkState
类,这个类的功能是对网络状态的广播进行监听。在onActive
方法中注册广播,在onInactive
取消广播,收到广播通知之后执行setValue
将实例的值修改为当前网络状态并通知观察者
class NetWorkState(val context: Context) : LiveData<Boolean>() {
private val broadcastReceiver: BroadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(p0: Context?, p1: Intent?) {
value = NetworkUtils.isConnected()
}
}
override fun onInactive() {
super.onInactive()
context.unregisterReceiver(broadcastReceiver)
}
override fun onActive() {
super.onActive()
val intentFilter = IntentFilter()
intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION)
context.registerReceiver(broadcastReceiver, intentFilter)
}
}
LiveData包含3个重要的方法:
onActive()
:当LiveData被激活时调用
onInactive()
:当LiveData被取消时调用
setValue(T)
:更新LiveData所持有的数据并通知观察者
LiveData使用起来很简单,直接初始化好LiveData对象之后,调用observe
方法将其绑定在当前Activity的LifecycleOwner上
class LiveDataActivity : AppCompatActivity() {
val state: NetWorkState by lazy {
NetWorkState(applicationContext)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
DataBindingUtil.setContentView<ActivityLivedataBinding>(this, R.layout.activity_livedata)
state.observe(this, Observer<Boolean> {
Toast.makeText(this, if (it) "网络连接成功" else "网络连接失败", Toast.LENGTH_LONG).show()
})
}
}
我们要通过源码来了解一下LiveData到底是如何绑定到Observer上并与生命周期发生关联的,先从observe()
方法入手
如果owner
的声明周期处在DESTROYED
的环节,则直接忽略本次添加的观察者。在其他生命周期环节下,LiveData根据owner
和observer
构造LifecycleBoundObserver
对象wrapper
,然后将其添加到SafeIterableMap
。随后通过owner.getLifecycle().addObserver(wrapper)
将其与Lifecycle
进行绑定
实际上调用的是Activity/Fragment
中的mLifecycleRegistry
来添加观察者,最终会调用到LifecycleRegistry
中的addObserver(@NonNull LifecycleObserver observer)
。这样Activity/Fragment
的生命周期变化会被LifecycleBoundObserver
观察到,同理removeObserver(@NonNull final Observer< T> observer)
会在LifecycleRegistry
中移除对Activity/Fragment
的观察
再来顺藤摸瓜看看LifecycleBoundObserver
,LiveData就是在这个类里感知到组件的生命周期。LifecycleBoundObserver
继承自ObserverWrapper
并实现了GenericLifecycleObserver
接口。owner
生命周期发生改变之后会回调GenericLifecycleObserver
接口的onStateChanged()
方法。如果owner
处于DESTROYED
,那么就移除当前组件Lifecycle
绑定关系。反之则调用activeStateChanged(boolean newActive)
。之前我们实现的onActive()
与onInactive()
全凭它控制
ObserverWrapper
是一个抽象类,其中shouldBeActive()
方法用来判断当前owner
生命周期下LiveData通知是否应该被激活
isAttachedTo(LifecycleOwner owner)
方法用来判断当前owner
与传入的对象是否为同一个对象
detachObserver()
是移除当前组件Lifecycle
绑定关系
进入activeStateChanged()
,首先判断当前的状态是否和上一次一致,一致则方法不执行。其次计算当前mActiveCount的数量,如果LiveData从未被激活过,则调用onActive()
方法。因为多个activity有可能共享同一个LiveData,所以这里加入了mActiveCount
计数。只要没有一个LiveData被激活,则回调onInactive()
。当LiveData被重新激活后,这里就会再执行一遍dispatchingValue()
来检查并处理之前没有发出去的通知
来看看通知部分,postValue()
与setValue()
的差别就在前者是通过子线程来调用dispatchingValue
而后者是直接在UI线程调用。postValue()
内部切换线程就一定会增加延迟,所以就算先执行postValue()
后执行setValue()
,也是setValue()
的结果先返回回来
postValue()
其实是在子线程中执行setValue()
setValue()
会调用dispatchingValue()
完成通知的发送。dispatchingValue()
里循环遍历mObservers中保存的ObserverWrapper
对象,然后执行considerNotify()
调用观察者的onChanged()
将数据发送出去
进入considerNotify(ObserverWrapper observer)
我们来看下通知观察者的时机,这个也相当重要。
来看具体的代码首先要满足mActive
为true
,这个值改变渠道有2个地方,1是LiveData中组件绑定Lifecycle的时候,此时会将该值通过activeStateChanged()
设置为true,解绑的时候设置为false;2是在owner
的生命周期发生改变的情况下,生命周期至少在onStart
之后onPause
之前的时候设置为true,其他情况下为false。这就意味着只有在onStart
之后onPause
之前发出的通知才可以立即收到。那onPause
之后且未发生onDestory
这段时期的通知到哪里去了呢?会被存在mData
,但是也只能存最后一条通知。
由于每次执行setValue()
或者postValue
的时候,mVersion
都会自增1,所以当你发出多个通知的时候,只有最后一条才会满足observer.mLastVersion >= mVersion
,因此也不用担心多条通知并发而产生的时序性问题
LiveData衍生类
MutableLiveData
LiveData中的setValue()
与postValue
是封装在其内部使用的,MutableLiveData
只是将它拿出来,方便我们在外界使用
MutableLiveData
MutableLiveData可以同时监听多个LiveData,它提供了addSource()
与removeSource()
添加和删除LiveData,我们稍后在具体例子中学习到
removeSource
LiveData转换
您可能想对存储在LiveData对象中的值进行更改后再分配给观察者,或者您可能需要根据另一个LiveData实例返回不同的LiveData实例,这个时候你需要添加extensions扩展包
implementation "android.arch.lifecycle:extensions:2.0.0-beta01"
扩展包提供Transformations
类,它包括支持这些场景
来看这么一个例子,假设我要通过用户ID来查找用户信息,你可以使用Transformations.switchMap
,它可以通过ID异步获取到用户信息,数据加工完成之后通过postValue
发出来。使用LiveData的网络请求写法基本上就是这样的
val id: MutableLiveData<String> = MutableLiveData()
val data1 = Transformations.switchMap(id) {
val temp: MutableLiveData<Data1> = MutableLiveData()
Thread(Runnable {
Thread.sleep(4000)
temp.postValue(Data1(it))
}).start()
temp
}
data1.observe(this, Observer<Data1> { t -> Log.d(LiveDataActivity::class.java.simpleName, t.name) })
id.value = "测试姓名"
还有一种是Transformations.map
,它其实是将String
进行解包重新组成一个新的LiveData对象Data1
val data11 = Transformations.map(id, Function<String, Data1> { input ->
if (input != null) {
return@Function Data1(input)
}
Data1("")
})
data11.observe(this, Observer<Data1> {
Log.d(LiveDataActivity::class.java.simpleName, it.name)
})
LiveData多监听
MediatorLiveData比较任性的地方在于可以给它随时增加或删除一个LiveData,你可以让其中任何一个LiveData发送通知给MediatorLiveData
有这么一种场景,页面加载数据的流程,首先在缓存中查找,如果没有数据的话再去网络加载。
val cacheSource = loadCache()
realResult.addSource(cacheSource) {
realResult.removeSource(cacheSource)
if (TextUtils.isEmpty(it.name)) {
val apiResponse = loadFromNetwork()
realResult.addSource(apiResponse) {
realResult.removeSource(apiResponse)
if (TextUtils.isEmpty(it.name)) {
// 网络请求失败
realResult.value = null
}
else {
realResult.value = it
}
}
}
else {
realResult.value = it
}
}
realResult.observe(this, Observer<Data1> { t -> Log.d(LiveDataActivity::class.java.simpleName, "onChanged ${t?.name}") })
这种写法就是嵌套层次太深,我不太习惯这样,还不如用RxJava那么方便呢
LiveData基本上就介绍完了,后面我们再说ViewModel
参考文章
Android Architecture Components应用架构组件源码详解(基于1.0以上)(第二篇ViewModel和LiveData)
Android架构组件四 Android Architecture Components LiveData组件解析
网友评论