Android JetPack之LiveData

作者: 皮球二二 | 来源:发表于2018-07-12 10:57 被阅读78次

    谷歌在今年的I/O大会上推出了Jetpack的概念,意图在于统一框架与UI组件。所以我也将项目架构往这一概念上靠齐。

    Jetpack
    今天我们来研究架构层最重要的两个类LiveDataViewModel之一的LiveData
    本文涉及到的代码已上传到Github,欢迎star、fork

    LiveData简介

    LiveData是一个可以感知Activity、Fragment生命周期的数据容器。当LiveData所持有的数据发生改变时,它会通知相应的观察者进行数据更新。由于Activity、Fragment都已实现LifecycleOwner接口,所以持有LifecycleOwner引用的LiveData在LifecycleOwner的生命周期处于started或resumed时可以作出相应更新,而在LifecycleOwner处于被销毁时停止更新。

    AppCompatActivity继承自ComponentActivity Fragment

    LiveData的功能

    从官方文档来看,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根据ownerobserver构造LifecycleBoundObserver对象wrapper,然后将其添加到SafeIterableMap。随后通过owner.getLifecycle().addObserver(wrapper)将其与Lifecycle进行绑定

    observe

    实际上调用的是Activity/Fragment中的mLifecycleRegistry来添加观察者,最终会调用到LifecycleRegistry中的addObserver(@NonNull LifecycleObserver observer)。这样Activity/Fragment的生命周期变化会被LifecycleBoundObserver观察到,同理removeObserver(@NonNull final Observer< T> observer)会在LifecycleRegistry中移除对Activity/Fragment的观察

    再来顺藤摸瓜看看LifecycleBoundObserverLiveData就是在这个类里感知到组件的生命周期LifecycleBoundObserver继承自ObserverWrapper并实现了GenericLifecycleObserver接口。owner生命周期发生改变之后会回调GenericLifecycleObserver接口的onStateChanged()方法。如果owner处于DESTROYED,那么就移除当前组件Lifecycle绑定关系。反之则调用activeStateChanged(boolean newActive)。之前我们实现的onActive()onInactive()全凭它控制

    ObserverWrapper是一个抽象类,其中shouldBeActive()方法用来判断当前owner生命周期下LiveData通知是否应该被激活
    isAttachedTo(LifecycleOwner owner)方法用来判断当前owner与传入的对象是否为同一个对象
    detachObserver()是移除当前组件Lifecycle绑定关系

    LifecycleBoundObserver

    进入activeStateChanged(),首先判断当前的状态是否和上一次一致,一致则方法不执行。其次计算当前mActiveCount的数量,如果LiveData从未被激活过,则调用onActive()方法。因为多个activity有可能共享同一个LiveData,所以这里加入了mActiveCount计数。只要没有一个LiveData被激活,则回调onInactive()。当LiveData被重新激活后,这里就会再执行一遍dispatchingValue()来检查并处理之前没有发出去的通知

    activeStateChanged

    来看看通知部分,postValue()setValue()的差别就在前者是通过子线程来调用dispatchingValue而后者是直接在UI线程调用。postValue()内部切换线程就一定会增加延迟,所以就算先执行postValue()后执行setValue(),也是setValue()的结果先返回回来

    postValue setValue

    postValue()其实是在子线程中执行setValue()

    子线程部分

    setValue()会调用dispatchingValue()完成通知的发送。dispatchingValue()里循环遍历mObservers中保存的ObserverWrapper对象,然后执行considerNotify()调用观察者的onChanged()将数据发送出去

    dispatchingValue

    进入considerNotify(ObserverWrapper observer)我们来看下通知观察者的时机,这个也相当重要。
    来看具体的代码首先要满足mActivetrue,这个值改变渠道有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,因此也不用担心多条通知并发而产生的时序性问题

    considerNotify

    LiveData衍生类

    MutableLiveData
    LiveData中的setValue()postValue是封装在其内部使用的,MutableLiveData只是将它拿出来,方便我们在外界使用

    MutableLiveData

    MutableLiveData
    MutableLiveData可以同时监听多个LiveData,它提供了addSource()removeSource()添加和删除LiveData,我们稍后在具体例子中学习到

    addSource
    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组件解析

    相关文章

      网友评论

        本文标题:Android JetPack之LiveData

        本文链接:https://www.haomeiwen.com/subject/vpvcpftx.html