关于Android MvvM的一些体会

关于Android MvvM的一些体会

前言

由于我司项目较老有很多历史包袱代码结构也比较混乱,需求复杂的页面动辄activity中1000多行,看着很是头疼,于是趁着加班提前做完需求余下的时间学习了mvvm对项目部分功能进行了改造,目前已经使用3个版本了,本篇博文分享下我使用的感受。

准备

这里先说说关于mvvm的几个问题(如有不对请轻喷 (╹▽╹))

  1. 首先说说我为啥选择mvvm而不是熟知的mvp。

    主要原因是我觉得mvp接口写起来有点麻烦,针对ui和model都得写接口,然后这个粒度不好控制如果太细了就得写一堆接口,太粗了又没有复用性,并且presenter持有了ui引用在更新ui的时候还得考虑生命周期,还有activity引用的处理防止内存泄露这些问题我都觉得挺麻烦的而MvvM中databinding框架处理好了这些问题,所以我选择了更加方便的mvvm,当然mvvm也不是没有缺点下面会说到。

  2. mvvm优缺点

    • 优点:
      1. 数据源被强化,利用databinding框架实现双向绑定技术,当数据变化的时候ui自动更新,ui上用户操作数据自动更新,很好的做到数据的一致性。
      2. xml和activity处理ui操作、model提供数据、vm处理业务逻辑,各个层级分工明确,activity中代码大大减少项目整体结构更加清晰。
      3. 很方便做ui的a/b测试可以共用同一个vm。
      4. 方便单元测试ui和vm逻辑完全分离。
    • 缺点:
      1. bug很难被调试,数据绑定使得一个bug被传递到别的位置,要找到bug的原始位置不太容易。
      2. 由于要遵守模式的规范调用流程变得复杂。
      3. vm中会有很多被观察者变量如果业务逻辑非常复杂会消耗更多内存。
  3. mvvm一定要用databinding么?

    答案是 否。首先我们要了解到mvvm是数据驱动的架构,所以着眼点是数据的变化,那么我们需要实现一套ui和数据双向绑定的逻辑,当数据修改的时候通知ui改变,ui输入或者点击的时候触发数据修改,而databinding就是帮你实现这个双向绑定过程的框架,在xml中按它的语法去写布局,然后他会根据你在xml中所写的生成对应的类帮你实现这个绑定过程,当然你也可以自己手动实现这个绑定过程,所以databinding是非必须的。

项目结构图

上面是mvvm基本的结构图,act/fra和xml是v处理ui操作、viewmodel是vm处理业务逻辑、repository是m提供数据,他们之间是一种单项的持有关系activity/fragment持有vm,vm持有model。

对于Repository不太理解的可以看看这篇文章Repository模式

实际使用

项目中我使用的是retrofit+rxjava+livedata+viewmodel+databinding+kotlin实现的mvvm

retrofit+rejava用来在model层从网络获取数据通知到vm

livedata是vm通知ui时使用可以感知生命周期防止内存泄漏npe问题(主要用在事件传递上)

viewmodel是vm可以在act/frg因配置修改销毁的情况下复用

databinding实现ui和vm的双向绑定

这里来个具体例子,activity可见通知vm获取数据,vm从model拿到数据然后更新被观察者,ui自动刷新的流程。

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:app="http://schemas.android.com/apk/res-auto">

    <data>

        <variable
            name="vm"
            type="rocketly.mvvmdemo.viewmodel.HotCityListVM" />
    </data>

    <android.support.v4.widget.SwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/srl"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:onRefreshListener="@{()->vm.onRefresh()}"//刷新自动触发vm.onRefresh()方法
        tools:context="rocketly.mvvmdemo.ui.MainActivity">

        <android.support.v7.widget.RecyclerView
            android:id="@+id/rv"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:multiTypeItem="@{vm.cityList}"//这里rv与vm中cityList绑定 />

    </android.support.v4.widget.SwipeRefreshLayout>
</layout>

xml中Recyclerview和vm的cityList绑定

class MainActivity : AppCompatActivity() {
   

    lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
   
        super.onCreate(savedInstanceState)
        binding = DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main).apply {
   
            vm = ViewModelProviders.of(this@MainActivity).get(HotCityListVM::class.java)
        }
    }

    override fun onResume() {
   
        super.onResume()
        binding.vm?.onFirstLoad()//onResume调用vm.onFirstLoad()加载数据
    }

}

activity在onResume通知vm加载数据

class HotCityListVM : BaseVM() {
   
    val cityList = ObservableArrayList<Basic>()
    val hotCityItemEvent = SingleLiveEvent<String>()

    override fun onFirstLoad() {
   
        super.onFirstLoad()
        load()
    }

    override fun onRefresh() {
   
        super.onRefresh()
        load()
    }

    private fun load() {
   
        CityRepository.getHotCityList(num = 50)
                .subscribe(ApiObserver(success = {
   
                    resetLoadStatus()
                    cityList.clear()
                    cityList.addAll(it.HeWeather6[0].basic)
                }, error = {
   
                    resetLoadStatus()
                }))
    }

    fun hotCityItemClick(s: String) {
   
        hotCityItemEvent.value = s
    }
}

vm从model CityRepository获取数据修改被观察者对象cityList,然后ui监听到数据修改执行recyclerview刷新,这一套流程就走完了,具体例子在MvvmDeno

除了正常的请求数据显示逻辑,这里再演示下点击事件的流程,弹dialog或者其他需要context的事件也是同样方式。

recyclerview中item点击事件传递到vm然后vm通知activty执行对应的逻辑。

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <data>

        <variable
            name="vm"
            type="rocketly.mvvmdemo.viewmodel.HotCityListVM" />

        <variable
            name="data"
            type="rocketly.mvvmdemo.model.Basic" />
    </data>

    <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:onClick="@{()->vm.hotCityItemClick(data.location)}"//调用vm的方法通知点击了>

        <TextView
            android:id="@+id/tv_city_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{data.location}"
            android:textColor="@android:color/black"
            android:textSize="18sp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintHorizontal_chainStyle="packed"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toLeftOf="@+id/tv_lon"
            app:layout_constraintTop_toTopOf="parent"
            tools:text="上海" />

        <TextView
            android:id="@+id/tv_lon"
            android:layout_width=
  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值