Skip to content

How to code the MVVM in Android

MVVM

  • 一种通过观察者模式实现数据回调的单一数据源结构
  • 核心结构(这个图是结合我的理解用ProcessOn画的,每个人理解可能会有不同的偏差,如果有不同看法,结合你的业务去理解会更好) ..mvvm-architecture-framework

ViewModel的创建方式很简单,按照官方的推荐,注入相应的生命即可以获取到相应的ViewModel,当然它这里还涉及到工厂模式,这部分详细参见ViewModelProvider.Factory:

  • 自己生命周期的
kotlin
private val viewModel: MyViewModel by lazy {
    ViewModelProvider(
        this,
        MyViewModelFactory()
    )[MyViewModel::class.java]
}
  • 父容器生命周期的
kotlin
private val parentViewModel: RouteViewModel? by lazy {
    parentFragment?.let {
        ViewModelProvider(
            it,
            MyViewModelFactory()
        )[RouteViewModel::class.java]
    }
}

private val activityViewModel: ActivityViewModel? by lazy {
    activity?.let {
        ViewModelProvider(
            it,
            MyViewModelFactory()
        )[ActivityViewModel::class.java]
    }
}

ViewModel的实际编写和LiveData的使用

kotlin
class MyViewModel(
    private val repository: Repository,
): ViewModel() {
    private val _data: MutableLiveData<String> = MutableLiveData()
    val data: LiveData<String> = _data
    
    private val _errorMessage: MutableLiveData<Throwable> = MutableLiveData()
    val errorMessage: LiveData<Throwable> = _errorMessage
    
    private val _isRefresh: MutableLiveData<Boolean> = MutableLiveData()
    val isRefresh: LiveData<Boolean> = _isRefresh
    
    fun fetchData() {
        _isRefresh.value = true
        viewModelScope.launch(Dispatchers.Main + CoroutineExceptionHandler { _, throwable -> {
            _errorMessage.value = throwable
            _isRefresh.value = false
        }}) {
            // Image repository.fetchData() run in background thread to fetch data.
            // Warning: when you want to post something to livedata, you need to stay at UI Thread.
            _data.value = repository.fetchData()
            _isRefresh.value = false
        }
    }
}

class MyFragment: Fragment() {
    private val viewModel: MyViewModel by lazy {
        ViewModelProvider(this)[MyViewModel::class.java]
    }
    
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        viewModel.isRefresh.observe(this) {
            showHideSpinner(it)
        }
        
        viewModel.errorMessage.observe(this) {
            Toast.makeText(context, it.message, Toast.LENGTH_SHORT).show()
        }
        
        viewModel.data.observe(this) {
            updateUI(it)
        }
        
        viewModel.fetchData()
    }
    
    private fun updateUI(data: String) {
        // updateUI
        println(data)
    }
}
  • MVVM与MVP的不同,就是MVVM并不一定要采用双向绑定,受限于DataBinding双向绑定的能力实在是太弱了,在使用MVVM的时候,我个人更加喜欢使用单一数据源,这也是Google最推荐的方式。结合着LiveData这一个拥有生命周期注入的监听工具,ViewModel中的数据可以很方便返回给到View。
  • 值得提及的一点是,我个人常用的单Activity多Fragment模式,使用parentFragment的生命周期去实现路由跳转是一个很好解决路由问题的方式。
  • 当然不可避免的一点,当我们使用LiveData的时候,按照以往既定的想法,每一个LiveData会对应一个状态。像上面的示例,它就存在了刷新中、数据请求成功、数据请求失败着3个状态。 这就会导致在ViewModel中,LiveData变得异常多,我们俗称为LiveData爆炸。

DIPENG-XU

DIPENG-XU

Creator

随便写写的,喜欢就好。 使用VitePress构建