美文网首页面试待看
LiveData&ViewModel初次体会

LiveData&ViewModel初次体会

作者: Magic旭 | 来源:发表于2019-03-01 18:46 被阅读0次

    Android Architecture

    Android Architecture 推出了LiveData&ViewModel已经有些日子了,在大佬推动下,项目逐渐引入了该模块,下面依据个人愚见分析用MVVM模式的好处。

    MVVM优点
    1. 先从LiveData分析。LiveData是一种持有可被观察数据的类。(其实还是观察者模式)LiveData具有生命周期感知能力。(Lifecycle,Android Architecture提供的另一个feature )。优点如下:
    • 实时数据保持一致,LiveData遵守观察者模式,只要数据改变就会收到相应通知。
    • 不会内存泄漏。LiveData被绑定到组建的生命周期上,当被绑定的组件销毁时,在Lifecycle的监控下,观察者会被自动移除。如果观察者的生命周期处于 不活跃状态,例如当Activity处于后台状态时,是不会收到LiveData的任何事件。
    • 刷新实时数据。不管LiveData是否处于活跃状态,它的数据都是最新数据。
    • 数据可以共享。如果对应的LiveData是单例,在模块间就可以共享了。当屏幕发生旋转时候,会立即收到最新的数据。

    小结: LiveData要本身发生改变,如果LiveData<List<T>>,其中的List某个数据改变了是不会收到回调的,要List<T>整个变化,才会收到回调的。不信的小伙伴可以动手试试哦。

    1. ViewModel。
    • ViewModel用来存储和管理UI相关的数据,可于将一个Activity或Fragment组件相关的数据逻辑抽象出来,并能适配组件的生命周期,如当屏幕旋转Activity重建后,ViewModel中的数据依然有效。
    • ViewModel通常是搭配LiveData使用。
    • ViewModel是个单例,需要过 ViewModelProviders 创建的。
    什么时候用MVVM

    个人见解:如果当前页面,有很多了操作依赖于一个变量值的时候,可以考虑下用LiveData。可以概括为只要是共享变量,都可以用LiveData是修饰。我下面做的例子是监听一个List数据实时变化的例子,有点多余但值得自己动手试试,毕竟实践才是检验真理的唯一标准。

    代码体验

    ViewModel
    1. 定义了一个Bean类,用来记录List数组内数据变化的类。忘记说了,最近被大佬催着用Kotlin写代码,可能Java程序猿们会看着不习惯。大家还是学下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 {
            //对应初始化,addALL,add,remove,set操作
            INIT, ADDALL, ADD, DELETE, UPDATE, INSERT
        }
    
        override fun toString(): String {
            return "ListNotifyEvent(start=$start, childCount=$childCount, type=$type)"
        }
    }
    
    1. 看下写的ViewModel是如何实现的。
    • 首先定义需要被共享的变量。这里要介绍下SingleLiveEvent这个类,是github上的国外大佬写的,在监听点击实践上我个人觉得很好用,就搬过来用了。SingleLiveEvent
    • mListNotify变量用于List数据源发生改变时候通知到对应的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
            }
    
            /**
             * 点击事件
             */
            @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之间我写了一个Helper类,主要作用是修改List的数据源和更新ViewModel上的LiveData<ListNotifyEvent>变量,以便可以通知到对应的订阅者。

    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界面就是一个RecyclerView,item布局中的Button点击会修改对应itemData的值。最后会将布局的图贴出来。
      Adapter上的关于ViewModel的核心逻辑,其中就是点击Button会修改到ViewModel上两个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","数据源改变了${it.toString()}")
                binding.recycler.adapter.notifyDataSetChanged()
            })
    
    
            UserNameViewModel.observeClickUpdate(this, Observer {
                Toast.makeText(this,"item 按钮点击",Toast.LENGTH_SHORT).show()
            })
        }
    
    点击后相关的Log输出

    当然我这里的按钮只是更新数据,当你想删除或者插入的时候,可以调用Helper的相关方法remove,add方法就可以了。

    2019-03-01 20:36:37.809 16875-16875/cn.bili.linsixu.bitmaprecycler I/magic: 数据源改变了ListNotifyEvent(start=1, childCount=1, type=UPDATE)
    2019-03-01 20:36:43.044 16875-16875/cn.bili.linsixu.bitmaprecycler I/magic: 数据源改变了ListNotifyEvent(start=3, childCount=1, type=UPDATE)
    2019-03-01 20:37:17.509 16875-16875/cn.bili.linsixu.bitmaprecycler I/magic: 数据源改变了ListNotifyEvent(start=2, childCount=1, type=UPDATE)
    2019-03-01 20:37:29.405 16875-16875/? I/magic: 数据源改变了ListNotifyEvent(start=4, childCount=1, type=UPDATE)
    
    UI整体布局(特别丑陋)
    image.png

    相关文章

      网友评论

        本文标题:LiveData&ViewModel初次体会

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