前语

Android Jetpack 架构组件之ViewModel 与 LiveData

ViewModel 和 LiveData 作为JetPack 中架构组件中重要的组件,调配好运用能大大提升开发效率。

ViewModel

ViewModel 具有生命周期认识,会主动存储和管理 UI 相关的数据,即便设备装备发生变化后数据还会存在,ViewModel的呈现会让让Activity专心于视图控制器的人物,业务逻辑交给ViewModel,很好地将视图与逻辑分离开来。不止于此,ViewModel还能在Fragment之间通讯。

ViewModel 在Activity 之间通讯

ViewModel 能在Framgment 通讯,这很好理解,由于他们有一起的载体Activity,就能创立相同的ViewModel实例:

private val  shareViewModel by lazy {
    ViewModelProvider(requireActivity()).get(ShareViewModel::class.java)
} 

每点击一次HomeFragment我都会延迟更新数据。

class ShareViewModel :ViewModel() {
    val data = MutableLiveData<String>()
    var count = 0
    fun getData(){
        Handler(Looper.getMainLooper()).postDelayed({
            count++
            data.value = "from the activity$count"
        },2000)
        data.value = "from the activity$count"
    }
}

然后每个Fragment 监听自己的数据源就能够:

shareViewModel.data.observe(viewLifecycleOwner, Observer {
    textView.text = it
})

Android Jetpack 架构组件之ViewModel 与 LiveData

可是好像实际开发中,跨Activity共享数据的状况更多,这个时候又该怎样处理呢?

ViewModelProvider接纳的是ViewModelStoreOwner子类目标,Activity 和 Fragment都完成了ViewModelStoreOwner接口。想要跨Activity共享数据,咱们让Application完成ViewModelStoreOwner接口,经过Application来创立ViewModel不就能完成跨Activity通讯的问题,代码如下:

class MyApp : Application(), ViewModelStoreOwner {
    private val TAG = "MyApp"
    private val appViewModelStore: ViewModelStore by lazy {
        ViewModelStore()
    }
    override fun onCreate() {
        super.onCreate()
        AppScope.init(this)
    }
    override fun onTerminate() {
        super.onTerminate()
        appViewModelStore.clear()
    }
    override fun getViewModelStore(): ViewModelStore {
        return appViewModelStore
    }
}

其中 AppScope:

object AppScope {
    private lateinit var myApp: MyApp
    fun init(application: MyApp){
        myApp = application
    }
    /**
     * 获取进程共享的ViewModel
     */
    fun <T : ViewModel?> getAppScopeViewModel(modelClass: Class<T>): T {
        return ViewModelProvider(myApp).get(modelClass)
    }
}

这里咱们做个小Demo,SecActivity 发动修改页面ThirdActivity,修改成功后,数据回来 SecActivity。 它们运用一起的EditViewModel,创立方法如下:

private val editViewModel: EditViewModel by lazy {
    AppScope.getAppScopeViewModel(EditViewModel::class.java)
}

监听数据变化

editViewModel.inputData.observe(this, Observer {
    it.let {
        tv?.text = it
    }
})

运转一把:

Android Jetpack 架构组件之ViewModel 与 LiveData

进入修改页面后,修改5689,关闭页面,数据确实传递到了SecActivity,共享数据成功,可是也带来了新的问题,从头进入SecActivity,依然接纳了原来了的数据,这是LiveData支持粘性事情导致的,接下来咱们谈谈LiveData。

LiveData

LiveData 天然生成支持粘性事情,google 规划LiveData 并不是为了粘性而规划,但却有粘性的效果。

LiveData 撤销粘性事情

LiveData支持粘性事情的原因是 observer version 与 LiveData 的version没有保持一致性,Observer 每次的初始值为-1,这样由于

     if (observer.mLastVersion >= mVersion) {
            return;
        }
        observer.mLastVersion = mVersion;
        observer.mObserver.onChanged((T) mData);

只要咱们重写Observer,让它的Version和LiveData的version保持一致,将不会把把历史数据回调新的注册者,也就撤销了粘性事情,从头界说WrapperObserver类:

/**
 * Observer 包装类
 * 经过改变mLastVersion的值就能做到非粘性事情
 *
 */
class WrapperObserver<T>(
    var liveData: NoStickyLiveData<T>,
    var observer: Observer<in T>,
    sticky: Boolean, observerForever: Boolean = false
) : Observer<T> {
    private val TAG = "WrapperObserver"
    //符号该liveData已经发射几回数据了,用以过滤老数据重复接纳
    private var mLastVersion = if (sticky) {
        -1
    } else {
        liveData.getVersion()
    }
    override fun onChanged(t: T) {
        if (mLastVersion >= liveData.getVersion()) {
            return
        }
        mLastVersion = liveData.getVersion()
        observer?.onChanged(t)
    }
}

自界说LiveData:

class NoStickyLiveData<T>(
    private val eventName: String = "default",
    private val map: ConcurrentHashMap<String, NoStickyLiveData<T>>? = null,
    private val sticky: Boolean = false
) : MutableLiveData<T>() {
    private val TAG = "StickyLiveData"
    private var mVersion = 0
    /**
     * 记录 绑定的Observer
     */
    private val mHashMap = ConcurrentHashMap<String, Observer<*>>()
    fun getVersion(): Int {
        return mVersion
    }
    override fun setValue(value: T) {
        mVersion++
        super.setValue(value)
    }
    override fun postValue(value: T) {
        mVersion++
        super.postValue(value)
    }
    override fun observeForever(observer: Observer<in T>) {
        val observerExit = mHashMap[eventName]
        if (observerExit != null) {
            removeObserver(observerExit as Observer<in T>)
        }
        val wrapperObserver = WrapperObserver(this, observer, sticky, true)
        mHashMap[eventName] = wrapperObserver
        super.observeForever(wrapperObserver)
    }
    override fun observe(owner: LifecycleOwner, observer: Observer<in T>) {
        observerSticky(owner, observer, sticky)
    }
    private fun observerSticky(owner: LifecycleOwner, observer: Observer<in T>, sticky: Boolean) {
        super.observe(owner, WrapperObserver(this, observer, sticky))
        owner.lifecycle.addObserver(LifecycleEventObserver { source, event ->
            if (event == Lifecycle.Event.ON_DESTROY) {
                map.let {
                    if (!sticky) {
                        it?.remove(eventName)
                    }
                }
            }
        })
    }
    private fun observerSticky(observer: Observer<in T>, sticky: Boolean) {
        super.observeForever(WrapperObserver(this, observer, sticky))
    }
}

监听:

editViewModel.inputDataNoSticky.observe(this, Observer {
    it.let {
        tv?.text = it
    }
})

效果如下图:

Android Jetpack 架构组件之ViewModel 与 LiveData

LiveData定制事情总线

LiveData强大的事情分发才能,能够依据LiveData来做一个事情总线,用来全局分发事情。 并且支持粘性事情和非粘性事情两种方法。 发送事情:

LiveDataBus.withSticky<String>("edit").setValue("********")

监听:

LiveDataBus.withSticky<String>("edit").observe(this, Observer {
    it.let {
        tv?.text = it
    }
})

LiveDataBus 源码

/**
 * 消息总线
 * 跨 activity
 */
object LiveDataBus {
    private val mHashMap = ConcurrentHashMap<String, NoStickyLiveData<*>>()
    /**
     * 不带粘性事情
     */
    fun <T> with(eventName: String): NoStickyLiveData<T> {
        var liveData = mHashMap[eventName]
        if (liveData == null) {
            liveData =
                NoStickyLiveData(
                    eventName,
                    mHashMap as ConcurrentHashMap<String, NoStickyLiveData<T>>
                )
            mHashMap[eventName] = liveData
        }
        return liveData as NoStickyLiveData<T>
    }
    /**
     * 带粘性事情的
     */
    fun <T> withSticky(eventName: String): NoStickyLiveData<T> {
        var liveData = mHashMap[eventName]
        if (liveData == null) {
            liveData =
                NoStickyLiveData(
                    eventName,
                    mHashMap as ConcurrentHashMap<String, NoStickyLiveData<T>>,
                    true
                )
            mHashMap[eventName] = liveData
        }
        return liveData as NoStickyLiveData<T>
    }
}

LiveDataBus 粘性事情:

Android Jetpack 架构组件之ViewModel 与 LiveData

LiveDataBus 非粘性事情:

Android Jetpack 架构组件之ViewModel 与 LiveData

总结

ViewModel和LiveData是JetPack中重量级组件,运用频率之高,熟练掌握必将大大简化咱们的开发使命。 源码:github.com/ThirdPrince…