如上图,我理解的MVVM
:LiveData
处理View
与ViewModel
之间的通讯,协程/Flow
处理ViewModel
与Repository
之间的数据通讯 。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层可以传入
Activity
和View
的,造成内存泄漏。 -
P
层与V
层有一定的耦合度,一旦V层某个UI元素更改,那么对应的接口就必须得改,数据如何映射到UI上、事件监听接口这些都需要转变,牵一发而动全身 -
Presenter
层与View
层是通过接口进行交互的,接口粒度不好控制。粒度太小,就会存在大量接口的情况,使代码太过碎版化;粒度太大,解耦效果不好 -
Presenter
可复用性不强
而MVVM
模式很好的解决了MVP
的问题:它具有下面几个优点:
-
它不会持有
View
层的任何引用,在业务逻辑处理中只要关心数据,不需要直接和UI打交道,数据独立于UI
相对于MVP
完美解耦。 -
可复用性强,一个ViewModel可以复用到多个View中。
-
适合单元测试。
-
另外它使用
ViewModel
来作为VM
,数据可在发生屏幕旋转等配置更改后继续留存。