Mvvm探索集成与使用

作者: Coair_Scarlet | 来源:发表于2019-01-04 11:19 被阅读6次

    Mvvm探索集成与使用

    前言

    Android开发中常常会有activity中数据操作过多导致代码臃肿且杂乱的问题,完全不符合面向对象编程的"责任单一性"原则,通常会有很多种解决方案,MVP,MVVM等(android本身的模式可以看做一种MVC)
    Google官方17年开始提供了一系列开发框架帮助开发者专注于业务,快速开发android程序,其中对于解决这个问题就有ViewModelLiveData两种(实际上还有解决生命周期导致的泄漏问题,工作在后台的Lifecycles )

    ViewModel 将视图的数据和逻辑从具有生命周期特性的实体(如 Activity 和 Fragment)中剥离开来。直到关联的 Activity 或 Fragment 完全销毁时,ViewModel 才会随之消失,也就是说,即使在旋转屏幕导致 Fragment 被重新创建等事件中,视图数据依旧会被保留。ViewModels 不仅消除了常见的生命周期问题,而且可以帮助构建更为模块化、更方便测试的用户界面。

    ViewModel的优点很明显,为Activity 、Fragment存储数据,直到完全销毁。尤其是屏幕旋转的场景,常用的方法都是通过onSaveInstanceState()保存数据,再在onCreate()中恢复,真的是很麻烦。因为ViewModel存储了数据,所以ViewModel可以在当前Activity的Fragment中实现数据共享。

    LiveData 是一个可以感知 Activity 、Fragment生命周期的数据容器。当 LiveData 所持有的数据改变时,它会通知相应的界面代码进行更新。同时,LiveData 持有界面代码 Lifecycle 的引用,这意味着它会在界面代码(LifecycleOwner)的生命周期处于 started 或 resumed 时作出相应更新,而在 LifecycleOwner 被销毁时停止更新。

    LiveData的优点:不用手动控制生命周期,不用担心内存泄露,数据变化时会收到通知。
    那么LiveDataViewModel的组合使用可以说是双剑合璧,而Lifecycles贯穿其中。

    以上是相关基础知识点,本文着重介绍集成方式与使用集成后的代码,不介绍上面三者详细使用,下图是集成后相关的UML图

    无标题.png

    准备

    导入

    主要要导入google的LiveData,LifecyclesViewModel

    implementation 'androidx.appcompat:appcompat:1.0.2'
    implementation "android.arch.lifecycle:extensions:1.1.1"
    

    配置

    已经集成到框架雏形
    https://git.dev.tencent.com/zhoulei26/android-scarlet.git

    代码解释

    ViewModel的获取和activity相关
    在Activity的基类中加入

        /**
         * 获取自定义的VM得方法
         */
        inline fun <reified T : MyLiveModel<T>> initMyLiveModel(init: T): T {
            return ViewModelProviders.of(this).get(T::class.java).apply {
                mutableLiveData.postValue(init)
            }
        }
    
        /**
         * 简单的ViewModel
         */
        inline fun <reified T> simpleVm(init: T): MyLiveModel<T> {
            return ViewModelProviders.of(this).get(SimpleVm<T>().javaClass).apply {
                mutableLiveData.postValue(init)
            }
        }
    

    在Fragment的基类中,此处的上下文填的是activity

       
        /**
         * 获取自定义的VM得方法
         */
        inline fun <reified T : MyLiveModel<T>> initMyLiveModel(init: T): T {
            return ViewModelProviders.of(activity!!).get(T::class.java).apply {
                mutableLiveData.postValue(init)
            }
        }
    
        /**
         * 简单的ViewModel
         */
        inline fun <reified T> simpleVm(init: T): MyLiveModel<T> {
            return ViewModelProviders.of(activity!!).get(SimpleVm<T>().javaClass).apply {
                mutableLiveData.postValue(init)
            }
        }
    

    common目录下新建文件mvvm.kt

    interface Mvvm {
        val vm: MyLiveModel<*>
    }
    
    abstract class MyLiveModel<T> : ViewModel() {
        val mutableLiveData = MutableLiveData<T>()
    
        /**
         * 使用edit将自动发送数据到订阅的地方,否则应该手动调用notifyDataSetChang(),任何地方可以调用
         */
        fun notifyDataSetChang() {
            if (mutableLiveData.value == null)
                throw RuntimeException("MutableLiveData Null!!!")
            else {
                mutableLiveData.postValue(this as T)
            }
        }
    
        /**
         * 修改MutableLiveData中的值并且发送数据,订阅者将更新,任何地方可以调用
         */
        fun edit(block: T.() -> Unit) {
            if (mutableLiveData.value == null)
                throw RuntimeException("MutableLiveData Null!!!")
            else {
                val cur: T = mutableLiveData.value!!
                cur.block()
                mutableLiveData.postValue(cur)
            }
        }
    
        /**
         * 将vm的数据和view进行绑定
         * 在[block]中设置view
         */
        fun subscribe(@NonNull owner: LifecycleOwner, block: (T) -> Unit) {
            mutableLiveData.observe(owner, Observer {
                block(it as T)
            })
        }
    
    }
    
    class SimpleVm<T> : MyLiveModel<T>()
    

    配置完成

    开始使用

    1.定义model

    data class User(
        var name: String = "123",
        var age: Int = 1,
        var count: Int = 0
    )
    

    2.在Activity和Fragment中...

    • Activity
    class ViewModelActivity : BaseActivity(), Mvvm {
                override val vm by lazy { simpleVm(User()) }
                ...
     }
    
    • Fragment
    class Blank1Fragment : BaseFragment(),Mvvm {
       override val vm by lazy { simpleVm(User()) }
    ...
    }
    
    • 订阅数据
    //activity和fragment操作相同
      vm.subscribe(this){
                tx1.text="我是fragment1 年龄${it.age}"
            }
    
    • 更新数据
    //任何地方
    vm.edit {
      age++
    }
    //或者修改数据之后
    vm.notifyDataSetChang()
    

    3.自定义用法

    上面是最简单的情况,viewModle应该是解耦界面和数据的利器,即应该包含各种复杂的数据操作
    自定义一个

    fun User.crazy() {
        name = "我TM疯了"
        age = 111
    }
    
    class Fuza : MyLiveModel<Fuza>() {
        var count: Int = 0
        var user = User()
        
        //使用edit将自动发送数据到订阅的地方,否则应该手动调用notifyDataSetChang()
        fun add(i: Int) {
            edit {
                user.age *= i
                count += i
            }
        }
        //可以放网络操作或者数据库操作等等
        fun updataUser() {
            user.crazy()
            notifyDataSetChang()
        }
    }
    
    //自定义的vm获取方式,在上下文中
     override val vm by lazy { initMyLiveModel(Fuza()) }
    //在界面可以触发,比如
    bt.click{
        vm.updataUser()
    }
    

    5.其他

    整个封装旨在使用更简单,更容易专注于业务,若需要更复杂的用法还需要自行学习Google的相关库

    结语

    使用mvvm之后使得界面和数据完全解耦
    实际上完全的,更进一步的解耦,vm和界面应该由接口沟通,即界面只管使用vm的接口,而无需管vm的实现,考虑到我们的开发团队与程序规模,我没有加入相关的内容
    相关demo git库
    https://git.dev.tencent.com/zhoulei26/android-scarlet.git

    相关文章

      网友评论

        本文标题:Mvvm探索集成与使用

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