LiveData&ViewModel初次體會(huì)

Android Architecture

Android Architecture 推出了LiveData&ViewModel已經(jīng)有些日子了,在大佬推動(dòng)下,項(xiàng)目逐漸引入了該模塊,下面依據(jù)個(gè)人愚見分析用MVVM模式的好處。

MVVM優(yōu)點(diǎn)
  1. 先從LiveData分析。LiveData是一種持有可被觀察數(shù)據(jù)的類。(其實(shí)還是觀察者模式)LiveData具有生命周期感知能力。(Lifecycle,Android Architecture提供的另一個(gè)feature )。優(yōu)點(diǎn)如下:
  • 實(shí)時(shí)數(shù)據(jù)保持一致,LiveData遵守觀察者模式,只要數(shù)據(jù)改變就會(huì)收到相應(yīng)通知。
  • 不會(huì)內(nèi)存泄漏。LiveData被綁定到組建的生命周期上,當(dāng)被綁定的組件銷毀時(shí),在Lifecycle的監(jiān)控下,觀察者會(huì)被自動(dòng)移除。如果觀察者的生命周期處于 不活躍狀態(tài),例如當(dāng)Activity處于后臺(tái)狀態(tài)時(shí),是不會(huì)收到LiveData的任何事件。
  • 刷新實(shí)時(shí)數(shù)據(jù)。不管LiveData是否處于活躍狀態(tài),它的數(shù)據(jù)都是最新數(shù)據(jù)。
  • 數(shù)據(jù)可以共享。如果對(duì)應(yīng)的LiveData是單例,在模塊間就可以共享了。當(dāng)屏幕發(fā)生旋轉(zhuǎn)時(shí)候,會(huì)立即收到最新的數(shù)據(jù)。

小結(jié): LiveData要本身發(fā)生改變,如果LiveData<List<T>>,其中的List某個(gè)數(shù)據(jù)改變了是不會(huì)收到回調(diào)的,要List<T>整個(gè)變化,才會(huì)收到回調(diào)的。不信的小伙伴可以動(dòng)手試試哦。

  1. ViewModel。
  • ViewModel用來存儲(chǔ)和管理UI相關(guān)的數(shù)據(jù),可于將一個(gè)Activity或Fragment組件相關(guān)的數(shù)據(jù)邏輯抽象出來,并能適配組件的生命周期,如當(dāng)屏幕旋轉(zhuǎn)Activity重建后,ViewModel中的數(shù)據(jù)依然有效。
  • ViewModel通常是搭配LiveData使用。
  • ViewModel是個(gè)單例,需要過 ViewModelProviders 創(chuàng)建的。
什么時(shí)候用MVVM

個(gè)人見解:如果當(dāng)前頁面,有很多了操作依賴于一個(gè)變量值的時(shí)候,可以考慮下用LiveData。可以概括為只要是共享變量,都可以用LiveData是修飾。我下面做的例子是監(jiān)聽一個(gè)List數(shù)據(jù)實(shí)時(shí)變化的例子,有點(diǎn)多余但值得自己動(dòng)手試試,畢竟實(shí)踐才是檢驗(yàn)真理的唯一標(biāo)準(zhǔn)。

代碼體驗(yàn)

ViewModel
  1. 定義了一個(gè)Bean類,用來記錄List數(shù)組內(nèi)數(shù)據(jù)變化的類。忘記說了,最近被大佬催著用Kotlin寫代碼,可能Java程序猿們會(huì)看著不習(xí)慣。大家還是學(xué)下Kotlin,在判空方面真的非常好用。
class ListNotifyEvent {
    var start: Int = 0
    var childCount: Int = 0
    var type: Notify = Notify.INIT
    //start最小值為1
    constructor(start: Int, childCount: Int, type: Notify = Notify.INIT){
        this.start = start
        this.childCount = childCount
        this.type = type
    }

    enum class Notify {
        //對(duì)應(yīng)初始化,addALL,add,remove,set操作
        INIT, ADDALL, ADD, DELETE, UPDATE, INSERT
    }

    override fun toString(): String {
        return "ListNotifyEvent(start=$start, childCount=$childCount, type=$type)"
    }
}
  1. 看下寫的ViewModel是如何實(shí)現(xiàn)的。
  • 首先定義需要被共享的變量。這里要介紹下SingleLiveEvent這個(gè)類,是github上的國外大佬寫的,在監(jiān)聽點(diǎn)擊實(shí)踐上我個(gè)人覺得很好用,就搬過來用了。SingleLiveEvent
  • mListNotify變量用于List數(shù)據(jù)源發(fā)生改變時(shí)候通知到對(duì)應(yīng)的UI上。
class UserNameViewModel : ViewModel() {

    val mClickUpdateMsg = SingleLiveEvent<Any>()

    var mListNotify = MutableLiveData<ListNotifyEvent>()

    companion object {

        @JvmStatic
        fun getListUser(activity: Activity?): MutableLiveData<ListNotifyEvent>? {
            return getCurrentModel(activity)?.mListNotify
        }

        @JvmStatic
        fun updateListNotifyEvent(activity: Activity?, newEvent: ListNotifyEvent) {
            getCurrentModel(activity)?.mListNotify?.value = newEvent
        }

        /**
         * 點(diǎn)擊事件
         */
        @JvmStatic
        fun clickUpdateMsg(activity: Activity?, position: Int) {
            getCurrentModel(activity)?.mClickUpdateMsg?.call()
        }

        @JvmStatic
        fun observeClickUpdate(activity: Activity?, observer: Observer<Any>) {
            if (activity is FragmentActivity) {
                getCurrentModel(activity)?.mClickUpdateMsg?.observe(activity, observer)
            }
        }

        @JvmStatic
        fun observeListDataSource(activity: Activity?, observer: Observer<ListNotifyEvent>) {
            if (activity is FragmentActivity) {
                getCurrentModel(activity)?.mListNotify?.observe(activity, observer)
            }
        }

        @JvmStatic
        fun getCurrentModel(activity: Activity?): UserNameViewModel? {
            if (activity is FragmentActivity) {
                return ViewModelProviders.of(activity).get(UserNameViewModel::class.java)
            }
            return null
        }
    }
}
UI與ViewModel的中間人

在UI與ViewModel之間我寫了一個(gè)Helper類,主要作用是修改List的數(shù)據(jù)源和更新ViewModel上的LiveData<ListNotifyEvent>變量,以便可以通知到對(duì)應(yīng)的訂閱者。

object ListDataNotifyHelper {

    @JvmStatic
    fun <T> addAll(activity: Activity?, sourceList: List<T>, target: List<T>) {
        if (activity !is FragmentActivity) {
            return
        }
        if (sourceList is ArrayList<T>) {
            val insertIndex = sourceList.size
            sourceList.addAll(target)
            val position = UserNameViewModel.getCurrentModel(activity)?.mListNotify
            position?.value = ListNotifyEvent(insertIndex + 1,
                target.size, ListNotifyEvent.Notify.ADDALL)
        }
    }

    @JvmStatic
    fun <T> add(activity: Activity?, sourceList: List<T>?, value: T) {
        if (activity !is FragmentActivity) {
            return
        }
        if (sourceList is ArrayList<T>) {
            val insertIndex = sourceList.size
            sourceList.add(value)
            val position = UserNameViewModel.getCurrentModel(activity)?.mListNotify
            position?.value = ListNotifyEvent(insertIndex + 1, 1
                , ListNotifyEvent.Notify.ADD)
        }
    }

    @JvmStatic
    fun <T> remove(activity: Activity?, sourceList: List<T>, value: T) {
        if (activity !is FragmentActivity) {
            return
        }
        if (sourceList is ArrayList<T>) {
            val insertIndex = sourceList.indexOf(value)
            val position = UserNameViewModel.getCurrentModel(activity)?.mListNotify
            position?.value = ListNotifyEvent(insertIndex + 1, 1
                , ListNotifyEvent.Notify.DELETE)
            sourceList.remove(value)
        }
    }

    @JvmStatic
    fun <T> updateValue(activity: Activity?, sourceList: List<T>, value: T, po: Int) {
        if (activity !is FragmentActivity) {
            return
        }
        if (sourceList is ArrayList<T>) {
            val position = UserNameViewModel.getCurrentModel(activity)?.mListNotify
            position?.value = ListNotifyEvent(po + 1, 1
                , ListNotifyEvent.Notify.UPDATE)
            sourceList.set(po, value)
        }
    }

    @JvmStatic
    fun <T> insertValue(activity: Activity?, sourceList: List<T>, value: T, index: Int){
        if (activity !is FragmentActivity) {
            return
        }
        if (sourceList is ArrayList<T>) {
            val position = UserNameViewModel.getCurrentModel(activity)?.mListNotify
            position?.value = ListNotifyEvent(index + 1, 1
                , ListNotifyEvent.Notify.INSERT)
            sourceList.add(index, value)
        }
    }
}
UI
  1. 我的UI界面就是一個(gè)RecyclerView,item布局中的Button點(diǎn)擊會(huì)修改對(duì)應(yīng)itemData的值。最后會(huì)將布局的圖貼出來。
    Adapter上的關(guān)于ViewModel的核心邏輯,其中就是點(diǎn)擊Button會(huì)修改到ViewModel上兩個(gè)LiveData變量的值。
override fun onBindViewHolder(holder: UserNameHolder?, position: Int) {
        holder?.txtName?.text = list[position].name
        holder?.txtAdress?.text = list[position].address
        holder?.txtNumber?.text = list[position].number.toString()
        holder?.btnUpdate?.setOnClickListener {
            ListDataNotifyHelper.updateValue(holder.itemView.context as? Activity, list,UserName("成功修改", "", 0),position)
            UserNameViewModel.clickUpdateMsg(holder.itemView.context as? Activity, position)
        }
    }
  1. Activity為ViewModel的LiveData添加訂閱者
    initClick是我自己封裝基類的方法,大家可以選擇忽略。主要看里面的代碼。
override fun initClick() {
        UserNameViewModel.observeListDataSource(this, Observer {
            MyLog.i("MvActivity","數(shù)據(jù)源改變了${it.toString()}")
            binding.recycler.adapter.notifyDataSetChanged()
        })


        UserNameViewModel.observeClickUpdate(this, Observer {
            Toast.makeText(this,"item 按鈕點(diǎn)擊",Toast.LENGTH_SHORT).show()
        })
    }
點(diǎn)擊后相關(guān)的Log輸出

當(dāng)然我這里的按鈕只是更新數(shù)據(jù),當(dāng)你想刪除或者插入的時(shí)候,可以調(diào)用Helper的相關(guān)方法remove,add方法就可以了。

2019-03-01 20:36:37.809 16875-16875/cn.bili.linsixu.bitmaprecycler I/magic: 數(shù)據(jù)源改變了ListNotifyEvent(start=1, childCount=1, type=UPDATE)
2019-03-01 20:36:43.044 16875-16875/cn.bili.linsixu.bitmaprecycler I/magic: 數(shù)據(jù)源改變了ListNotifyEvent(start=3, childCount=1, type=UPDATE)
2019-03-01 20:37:17.509 16875-16875/cn.bili.linsixu.bitmaprecycler I/magic: 數(shù)據(jù)源改變了ListNotifyEvent(start=2, childCount=1, type=UPDATE)
2019-03-01 20:37:29.405 16875-16875/? I/magic: 數(shù)據(jù)源改變了ListNotifyEvent(start=4, childCount=1, type=UPDATE)
UI整體布局(特別丑陋)
image.png
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容