关于 Android MVVM 一些理解与实践

4038次阅读  |  发布于3年以前

前言

什么是MVVM?

先上图

1813550.png

viewmodel可以理解为桥梁,通过viewmodel 将 View和Model双向绑定,数据的变化可以直接作用在View上,就是MVVM,其实MVVM,MVP都是在MVC的基础上演变而来。

MVVM与DataBinding的关系

MVVM是架构思想,没有固定的套路,你可以基于这个思想整个自己的MVVM框架。而DataBinding就是基于MVVM思想实现的一个框架。目前也是android MVVM 开发的主流框架。

JetPack的ViewModel 与MVVM中ViewModel的关系

有位大佬说过它们两个没有关系不是一个东西。其实这句话说要看怎么说. JetPack 的 ViewModel 官方给的定义是旨在存储和管理生命周期的方式与UI相关的数据,而MVVM中的viewmodel是连接View与Model的纽带。

不使用jetPack的ViewModel,我们可以自己实现一个ViewModel

  class Presenter {
    val size = ObservableField<Int>()
    fun numChange() {
        size.set(size.get() + 1)
        size.notifyChange()
    }
}

xml 数据绑定

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">
    <data>
        <variable
            name="presenter"
            type="com.example.myapplication.Presenter" />
    </data>
    <LinearLayout
        android:gravity="center_horizontal"
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">
        <TextView
            android:layout_marginTop="50dp"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{presenter.size.toString()}"
         />
        <Button
            android:onClick="@{()->presenter.numChange()}"
            android:layout_marginTop="20dp"
            android:background="@color/black"
            android:text="点击数字自增"
            android:layout_width="150dp"
            android:layout_height="wrap_content">
        </Button>
    </LinearLayout>
</layout>

如果是这样的情况,MVVM ViewMoel 是我们自己定义的Presenter类实现的,JetPack的ViewModel 和MVVM的ViewModel确实没有关系。但是因为我们自己定义的ViewModel实现类,在实际开发中会有很多问题,最突出就是没有生命周期的管理。

所以你会想到什么?

用JetPack的ViewModel来承担MVVM的ViewModel的职责。

class MainViewModel : ViewModel() {
    val size = MutableLiveData<Int>().apply { this.value = 0 }
    fun numChange() {
        size.postValue((size.value ?: 0) + 1)
    }
}
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">
    <data>
        <variable
            name="mainViewModel"
            type="com.example.myapplication.MainViewModel" />
    </data>
    <LinearLayout
        android:gravity="center_horizontal"
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">
        <TextView
            android:layout_marginTop="50dp"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{mainViewModel.size.toString()}"
           />
        <Button
            android:onClick="@{()->mainViewModel.numChange()}"
            android:layout_marginTop="20dp"
            android:background="@color/black"
            android:text="点击数字自增"
            android:layout_width="150dp"
            android:layout_height="wrap_content">
        </Button>
    </LinearLayout>
</layout>
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val binding = DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
        binding.lifecycleOwner = this
        val viewModel = ViewModelProvider(this).get(MainViewModel::class.java)
        //将viewModel 数据 赋值给  View 声明的变量
        binding.setVariable(BR.mainViewModel,viewModel)
    }
}

gradle 的anroid 配置

 dataBinding {
        enabled = true
    }

gradle 的 dependencies 配置

dependencies {
    implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
    implementation 'androidx.core:core-ktx:1.3.2'
    implementation 'androidx.appcompat:appcompat:1.2.0'
    implementation 'com.google.android.material:material:1.2.1'
    implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
    testImplementation 'junit:junit:4.13.1'
    androidTestImplementation 'androidx.test.ext:junit:1.1.2'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'

    def lifecycle_version = "2.2.0"
    implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version"
    implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
    implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
}

如果是这中情况,那么JetPack的ViewModel 等同于 MVVM的ViewModel,当然JetPack的ViewModel还能干其他事,承担MVVM的ViewModel 仅是一个兼职。

android.gif

对比了一下Vue的MVVM的实现

<template>
  <div>
    <p>{{ size }}</p>
    <button @click="numChange">点击数字自增</button>
  </div>
</template>
<script>
export default {
  methods: {
    numChange: function () {
      this.size = this.size + 1
    }
  },
  data: function () {
    return {
      size: 0
    }
  }
}
</script>
<style lang="less" scoped></style>

vue.gif

两者都是在View层绑定了 size 变量 , 数据声明与方法的都在ViewModel中,(Vue的export default 也承担了MVVM中viewmodel的职责,当然了这也是它的一个兼职)

补充

DataBinding 中的bindAdapter 还可以将每个控件单独处理,使结构更加松散,逻辑不会耦合在Activity中或者ViewModel里,特别在复杂的交互场景下需求迭代频繁的时候,MVVM太香了。相见恨晚,往事回想,泪目( Ĭ ^ Ĭ )

Copyright© 2013-2020

All Rights Reserved 京ICP备2023019179号-8