Android开发笔记——MVVM模式&Jetpack实现MVVM模式

Android开发笔记——MVVM模式&Jetpack实现的MVVM模式

  • MVVM模式
    • MVC、MVP和MVVM
      • MVC
        • 概述
        • 特点
      • MVP
        • 概述
        • MVC和MVP的区别
        • 特点
      • MVVM
        • 概述
        • 特点
  • Jetpack实现MVVM
    • Model
    • View
    • ViewModel

在之前的学习中,我将Jetpack中使用最为频繁的几个架构组件都做了相关的学习和了解,但是,之前只是对每个组件分别进行学习,而如何在实际开发中学会熟练使用Jetpack的组件才是最重要的,正好打算再重新学习和了解下Android的MVVM模式,所以这周我打算基于Jetpack的几个组件写一个简单的MVVM模式的Demo,多加练习以下,以便在之后的开发中能够熟练运用。

MVVM模式

在写demo之前,我们首先先来学习下MVVM模式。

MVC、MVP和MVVM

在我们实际开发中,从项目的可维护性、以及可扩展性等方面考虑,通常我们对于项目的架构会做出合理设计,而目前最为常用的Android开发架构模式就是MVC、MVP和MVVM模式。

MVC

概述

MVC模式全称Model-View-Controller,将整个项目结构划分为三个部分模型层(Model),视图层(View)和控制层(Controller),三层分别承担不同任务,如下:

  • 模型层(Model):是应用程序中用于处理应用程序数据逻辑的部分。负责根据Controller来从数据库或网络请求等途径获取相关数据。在Android项目中通常会创建一些相关的Bean类,或是与数据请求相关的类这就是Model层。
  • 视图层(View):是应用程序中处理数据显示的部分。负责展示数据,并响应用户的交互从而通过Controller来获取和更新数据。在Android项目中通常是以XML形式出现,通常Activity也需要承担部分View层工作。
  • 控制层(Controller):是应用程序中处理用户交互的部分。通常控制器负责从视图读取数据,控制用户输入,并向模型发送数据。在Android项目中,就是以Activity和Fragment来充当控制器,处理相关业务逻辑。
    如下图所示:
    Android开发笔记——MVVM模式&Jetpack实现MVVM模式_第1张图片

特点

MVC架构相对于将所有业务逻辑都放入Activity中,多了一个Model层,将部分请求数据相关逻辑划分到了Model层,将视图层和业务层进行了一定程度上的分离,降低了耦合性,提高了项目的可维护性。
但是,在Android实际的项目开发中,MVC存在着很大的缺陷。假设,我们需要创建一个登录页面,需要实现登录、自动登录和保存密码等相关功能。如果我们采用MVC模式,那么,我们可能会利用XML完成界面设计作为View层,然后创建一个LoginModel来完成登录和网络请求这就是Model层了,而Activity则复杂相关业务逻辑,界面交互等工作,为Controller层。
这么做是存在着一定的问题的。首先,View与Model之间还存在依赖关系,Controller很重很复杂。由上面的MVC架构图可知,view层和model层是相互可知的,这意味着两层之间存在耦合,耦合对于一个大型程序来说是非常致命的,因为这表示开发,测试,维护都需要花大量的精力。其次,xml作为view层,控制能力实在太弱了,因此,View层很多工作都需要在Activity中进行,这就导致了Activity在实际开发中承担功能过多,即需要承担Controller层的功能,也需要承担View层功能,如果页面功能较为复杂,那么Activity中就会存在大量代码,维护成本很高。
因此,MVC模式只适用于功能简单,业务逻辑相对较少的项目和页面。

MVP

概述

MVP模式全称Model-View-Presenter,将项目划分为模型层(Model)、视图层(View)和中间层(Presenter),MVP模式是由MVC模式发展演化而成的,我们从其名称就可以发现,其不同的就只是Controller和Presenter。其各层功能也基本相同,Model层复杂数据处理和获取,View层负责数据展示和交互,而Presenter层则负责业务逻辑处理,联系View层和Model。但是,实际上其View层和Model层的实现也有所不同。其架构图如下:
Android开发笔记——MVVM模式&Jetpack实现MVVM模式_第2张图片

MVC和MVP的区别

其实,从上面的架构图来看,我们就能够发现,MVP与MVC最大的区别就是,将View层和Model层完全分离,之间相互不可见,而是以Presenter层作为沟通的桥梁,用于操作view层发出的事件传递到presenter层中,presenter层去操作model层,并且将数据返回给view层,整个过程中view层和model层完全没有联系。
其实,从上面的架构图来看,我们就能够发现,MVP与MVC最大的区别就是,将View层和Model层完全分离,之间相互不可见,而是以Presenter层作为沟通的桥梁,用于操作view层发出的事件传递到presenter层中,presenter层去操作model层,并且将数据返回给view层,整个过程中view层和model层完全没有联系。在MVP模式中,Activity为完成充当View层,只处理视图相关逻辑,其它所有业务逻辑都交由Presenter层处理。
再回看,如果使用MVP模式来完成上述登录界面,我们可能会这么写。首先,创建LoginModel类,该类提供了相关方法,实现了保存、获取密码和登录等相关数据处理的功能,此为Model层;然后,创建LoginActivity,实现相关界面,并且提供了相关接口,用于展示数据,Activity中持有Presenter层引用,通过Presenter层提供的接口完成相关业务逻辑,此为View层;最后,创建LoginPresenter层,在该层中持有LoginModel和LoginActivity引用,通过LoginModel处理数据,并提供了相关接口完成登录、保存密码和自动登录等功能,最后利用LoginActivity的相关接口返回结果,改变界面,这就是Presenter层。当然,实际上,在我们使用MVP模式的时候,我们可能更多的会使用接口的形式来实现,创建Model、View和Presenter层基类,然后在基类中进行注册等工作。

特点

MVP模式主要存在以下几个特点:

  • View 与 Model 不通信,都通过 Presenter 传递。Presenter完全把Model和View进行了分离,主要的程序逻辑在Presenter里实现。视图层和模型层的完全分离方便了单元测试。
  • Activity和Fragment完全充当View层,而不负责业务逻辑的处理,降低了其复杂性。
  • Presenter与具体的View是没有直接关联的,而是通过定义好的接口进行交互,从而使得在变更View时候可以保持Presenter的不变,可复用于多个视图。
  • View 非常薄,不部署任何业务逻辑,称为”被动视图”,即没有任何主动性,而 Presenter非常厚,所有逻辑都部署在那里,因此,当业务逻辑复杂时,Presenter层也会越来越臃肿。
  • MVP通过接口进行交互,接口粒度不太好设计和控制,粒度太小,就会存在大量接口,粒度太大,解耦效果就不好。

MVVM

概述

MVVM模式全称Model-View-ViewModel,MVVM可以看作是MVP的一个改进版,其不同之处也主要存在是ViewModel层和Presenter层。和Presenter相比,其数据和视图是双向绑定的,ViewModel不再持有View的引用,而是通过DataBanding等方式,在数据发生改变的时候自动更新View层,而视图层发生变化时同时也会改变数据。其实现和各种特点,将在下面的实例中一一展示。

特点

MVVM的特点如下:

  • 低耦合,数据和业务逻辑都是处理一个独立的ViewModel中,ViewModel只需要关注数据和业务逻辑,不需要View层打交道。
  • 可重用性,你可以把一些视图逻辑放在一个ViewModel里面,让很多View重用这段视图逻辑。
  • 相对于MVP而言,MVVM不需要手动处理大量的View和Model相关操作,完美的解耦了View层和ViewModel。
  • 各个模块完全分离,便于单元测试。
  • 数据的双向绑定对于庞大的项目可能会消耗更多性能.

Jetpack实现MVVM

接下来,我将使用Jetpack中相关组件ViewModel和LiveData基于MVVM模式,来实现上述的登录界面。当然,实际上使用Jetpack来实现MVVM模式的最佳选择是以DataBinding组件来实现数据的双向绑定,我们现在只是使用ViewModel和LiveData来尝试实现一个简单的Demo。项目中使用fastmock提供的API,利用okhttp来实现网络请求。

Model

首先,我们根据接口返回的json数据,创建对应的Model,如下所示:

data class LoginDataModel(
    var code: String,
    var data: DataBean,
    var desc: String
)

data class DataBean(
    var verifySuccess: Boolean,
    val userInfo: UserInfoBean
)

data class UserInfoBean(
    val username: String,
    val email: String,
    val address: String
)

View

在MVVM模式中,Activity充当View层,如下所示:

class MvvmActivity : AppCompatActivity() {

    private var viewModel : LoginViewModel? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_mvvm)
        MMKV.initialize(this)

        viewModel = ViewModelProvider(this).get(LoginViewModel::class.java)
        bindViewAndData()
        //初始化数据
        viewModel?.initData()
        initView()
    }

    private fun initView(){
        btLogin.setOnClickListener {
            Thread {
                viewModel?.login({
                    Log.e("test_mvvm", "登录失败")
                },{
                    Log.e("test_mvvm", "登录成功")
                })
            }.start()
        }
    }

    /**
     * 双向绑定
     */
    private fun bindViewAndData(){
        //data->view
        viewModel?.account?.observe(this,
            Observer<String> { account -> if (account != etAccount.text.toString()) etAccount.setText(account) })
        viewModel?.password?.observe(this,
            Observer<String> { password -> if (password != etPassword.text.toString()) etPassword.setText(password) })
        viewModel?.rememberFlag?.observe(this,
            Observer<Boolean>{ flag -> cbRemember.isChecked = flag} )
        //view->data
        etAccount.addTextChangedListener {
            it?.apply { viewModel?.account?.value = it.toString() }
        }
        etPassword.addTextChangedListener {
            it?.apply { viewModel?.password?.value = it.toString() }
        }
        cbRemember.setOnCheckedChangeListener { _, isChecked ->
            viewModel?.rememberFlag?.value = isChecked
        }
    }
}

我们可以看到,在Activity中没有多余的业务逻辑,其中内容只是进行了视图和数据的双向绑定,并且将点击事件交由View处理Model。因为使用LiveData,界面会自动根据数据变化而更新,从而使View层和ViewModel层完全解耦。

ViewModel

然后就是最重要,包含了所有业务逻辑的ViewModel了,我们继承View Model创建LoginViewModel,如下:

class LoginViewModel : ViewModel() {

    var account : MutableLiveData<String> = MutableLiveData()
    var password : MutableLiveData<String> = MutableLiveData()
    var rememberFlag : MutableLiveData<Boolean> = MutableLiveData(false)

    /**
     * 登录
     */
    fun login(failed : () -> Unit, success : ()->Unit){
        val client = OkHttpClient.Builder().build()
        val requestBody = FormBody.Builder()
            .add("username", account.value ?: "")
            .add("password", password.value ?: "")
            .build()
        val request = Request.Builder()
            .url("https://www.fastmock.site/mock/46ef588deb8840f2083cefdad4d760b7/test/api/submit")
            .post(requestBody)
            .build()
        client.newCall(request).enqueue(object : Callback {
            override fun onFailure(call: Call, e: IOException) {
            }
            override fun onResponse(call: Call, response: Response) {
                val data = Gson().fromJson(response.body()?.string(), LoginDataModel::class.java)
                if (data.data.verifySuccess){
                    //登录成功
                    success.invoke()
                    saveData()
                } else {
                    //登录失败
                    failed.invoke()
                }
            }
        })
    }

    /**
     * 初始化数据
     */
    fun initData(){
        val mmkv = MMKV.defaultMMKV()
        rememberFlag.value = mmkv.decodeBool("remember")
        account.value = mmkv.decodeString("account")
        if (rememberFlag.value == true){
            password.value = mmkv.decodeString("password")
        }
    }

    /**
     * 保存数据
     */
    fun saveData(){
        val mmkv = MMKV.defaultMMKV()
        mmkv.encode("remember", rememberFlag.value ?: false)
        mmkv.encode("account", account.value ?: "")
        mmkv.encode("password", password.value ?: "")
    }

}

我们可以看到,LoginViewModel中包括了登录页面的所有业务逻辑,包括初始化数据,记住密码,和登录。
如此一来,一个简单的MVVM模式demo就完成了,当然这是一个非常简单的Demo,其中包括网络请求等操作都没有进行相关处理。而且也没有使用DataBinding,在实际开发中有许多可以优化的地方。无论是Jetpack还是MVVM模式的使用,这都需要在之后的开发中多加使用,熟练。

你可能感兴趣的:(Android开发学习笔记,Android,android)