谈谈我理解基于jetpack的MVVM

711 阅读2分钟

img

如上图,我理解的MVVMLiveData处理ViewViewModel之间的通讯,协程/Flow处理ViewModelRepository之间的数据通讯 。Repository执行数据相关的处理(包括接口数据与本地数据)

也可以使用Databinding的双向绑定来完成数据驱动UI的更新,数据的变化能自动响应到UI、UI的输入能自动更新到数据。但是Databinding的坑太多,不建议使用。

一个简单的demo示例如下:

Model层:

class DataRepository {
    private val model by lazy { SplashModel() }

    @ExperimentalCoroutinesApi
    suspend fun getNameData(): Flow<String> {
        return flow { emit(model.sendNetworkRequestSuspend()) }
                .map { it.name }
                .onEach { Log.i(NameViewModel.TAG, "onEach: $it") }
                .catch { Log.i(NameViewModel.TAG, "catch: $it") }
                .onCompletion { Log.i(NameViewModel.TAG, "onCompletion: ") }
                .flowOn(Dispatchers.IO)
    }

    suspend fun getData() = model.sendNetworkRequestSuspend()
}

VM层:

lass NameViewModel : ViewModel() {
    companion object {
        const val TAG = "NameViewModel"
    }

    private val repository by lazy { DataRepository() }

    @ExperimentalCoroutinesApi
    val nameLiveData = liveData<String> {
        emitSource(repository.getNameData().asLiveData())
    }

}

View层:

class MVVMDemoActivity : AppCompatActivity() {

    private val viewModel by lazy {
        ViewModelProvider(this,
                ViewModelProvider.AndroidViewModelFactory.getInstance(application))
                .get(NameViewModel::class.java)
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val binding = DataBindingUtil.setContentView<ActivityMvvmDemoBinding>(this, R.layout.activity_mvvm_demo)
        binding.viewModel = viewModel
        binding.setLifecycleOwner(this)
    }
}
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>
        <variable
            name="viewModel"
            type="com.liujian.kotlindemo.mvvmdemo.NameViewModel"/>
    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center_horizontal"
        android:orientation="vertical">

        <Button
            android:id="@+id/btnData"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="20dp"
            android:background="#CCCCCC"
            android:text="Get Data" />

        <TextView
            android:id="@+id/tvName"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="100dp"
            android:text="@{viewModel.nameLiveData}"/>

    </LinearLayout>
</layout>


总结

MVP模式虽然很好用,但是也有它的缺点:

  • 虽然P层不会直接引用View层,但是它会持有View层的代理接口的引用,不适合作单元测试。

  • 如果使用不当的话,P层可以传入ActivityView 的,造成内存泄漏。

  • P层与V层有一定的耦合度,一旦V层某个UI元素更改,那么对应的接口就必须得改,数据如何映射到UI上、事件监听接口这些都需要转变,牵一发而动全身

  • Presenter层与View层是通过接口进行交互的,接口粒度不好控制。粒度太小,就会存在大量接口的情况,使代码太过碎版化;粒度太大,解耦效果不好

  • Presenter可复用性不强

MVVM模式很好的解决了MVP的问题:它具有下面几个优点:

  • 它不会持有View层的任何引用,在业务逻辑处理中只要关心数据,不需要直接和UI打交道,数据独立于UI相对于MVP完美解耦。

  • 可复用性强,一个ViewModel可以复用到多个View中。

  • 适合单元测试。

  • 另外它使用ViewModel来作为VM,数据可在发生屏幕旋转等配置更改后继续留存。