Android开发中MVC、MVP到MVVM演化

有一个需求:需要查询用户账号信息,用户输入账号,点击按钮可进行查询账号信息,如果查询数据成功,则将数据展示在界面上;如果查询数据失败,则在界面上提升获取数据失败。

一般模式

使用一个Activity完成所有功能
NormalActivity

  • 获取用户输入信息
  • 展示获取信息成功页面
  • 展示获取信息失败页面
  • 查询用户数据
  • 业务逻辑

实现的效果如下,输入用户名,点击提交,查询用户信息,获取信息成功展示成功页,获取信息失败展示失败页。
在这里插入图片描述

activity_normal.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <EditText
        android:id="@+id/userName"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="10dp"
        android:hint="请输入用户名"
        android:textSize="14sp" />

    <Button
        android:id="@+id/submit"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="30dp"
        android:text="提交" />

    <TextView
        android:id="@+id/result"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="10dp"
        android:textSize="14sp" />
</LinearLayout>

NormalActivity

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.juny.mmm.bean.Account
import com.juny.mmm.callback.MCallback
import kotlinx.android.synthetic.main.activity_normal.*
import java.util.*

class NormalActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_normal)
        initEvent()
    }

    private fun initEvent() {
        submit.setOnClickListener {
            getAccountData(getUserInput(), object : MCallback {
                override fun onSuccess(account: Account) {
                    showSuccessPage(account)
                }

                override fun onFailed() {
                    showFailedPage()
                }
            })
        }
    }

    // 获取用户输入信息
    private fun getUserInput(): String {
        return userName.text.toString()
    }

    // 展示获取数据成功的界面
    private fun showSuccessPage(account: Account) {
        result.text = "用户账号:" + account.name + " | " + "用户等级:" + account.level
    }

    // 展示获取数据失败的界面
    private fun showFailedPage() {
        result.text = "获取数据失败"
    }

    // 模拟查询账号数据
    private fun getAccountData(accountName: String, callback: MCallback) {
        val random = Random()
        val isSuccess = random.nextBoolean()
        if (isSuccess) {
            val account = Account(accountName, 100)
            callback.onSuccess(account)
        } else {
            callback.onFailed()
        }
    }
}

Account

用户信息实体类:使用了 Kotlin 中的数据类来进行实现

data class Account(var name: String, var level: Int)

MCallback

回调接口:对于数据请求的成功和失败提供相关接口

interface MCallback {
    fun onSuccess(account: Account)
    fun onFailed()
}

一般模式中,所有的功能都是堆积在一个Activity中的,导致Activity中代码的可复用性降低,Activity过于累赘。


MVC

MVC简介

MVC的全名是Model View Controller,即模型(model)- 视图(view)- 控制器(controller)。

在这里插入图片描述
Android的MVC模式中,各个层表示如下:

  • Controller :Activity、Fragment 等;
  • View : layout, view 等控件;
  • Model :数据处理的逻辑,比如:网络请求,数据库,文件查询等。

流程:

  • 对View 进行操作会传递到 Controller 中;
  • Controller 通知 Model后,Model 对数据进行处理,比如网络请求;
  • Model对数据处理完成后,将结果返回给 View 进行展现。

让 Controller 持有 Model的引用;而 Model 要向 View 传递数据一般不会让 Model 持有 View 的引用,而是类似 CallBack 的注册监听的方式进行数据的传递。

MVC各层功能

MVC 主要是把数据处理这块的逻辑给放到 Model 层中进行处理。

在这里插入图片描述
代码实现如下:

MVCModel

class MVCModel {

    // 模拟查询账号数据
    fun getAccountData(accountName: String, callback: MCallback) {
        val random = Random()
        val isSuccess = random.nextBoolean()
        if (isSuccess) {
            val account = Account(accountName, 100)
            callback.onSuccess(account)
        } else {
            callback.onFailed()
        }
    }
}

MVCActivity

class MVCActivity : AppCompatActivity() {

	//Activity 中持有 Model 的引用
    private lateinit var mMVCModel: MVCModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_normal)
        mMVCModel = MVCModel()
        initEvent()
    }

    private fun initEvent() {
        submit.setOnClickListener {
            mMVCModel.getAccountData(getUserInput(), object : MCallback {
                override fun onSuccess(account: Account) {
                    showSuccessPage(account)
                }

                override fun onFailed() {
                    showFailedPage()
                }
            })
        }
    }

    // 获取用户输入信息
    private fun getUserInput(): String {
        return userName.text.toString()
    }

    // 展示获取数据成功的界面
    private fun showSuccessPage(account: Account) {
        result.text = "用户账号:" + account.name + " | " + "用户等级:" + account.level
    }

    // 展示获取数据失败的界面
    private fun showFailedPage() {
        result.text = "获取数据失败"
    }
}

这样我们就将数据请求的部分代码抽离了出去,通过引用进行调用,让代码更加灵活、干净。

优缺点

  • 优点:
    一定程度上的实现了 Model 与 View 的分离,降低了代码的耦合性。

  • 缺点:
    Controller 与 View 难以完全解耦,并且随着项目复杂度的提升,Controller 将会越来越臃肿,Activity 承担了控制器的功能,又要承担部分视图层的工作。

其实 MVC 模式的实现也可以这样进行表示:

在这里插入图片描述
这样就体现出来了 View 和 Controller 的耦合关系。


MVP

简介

MVP 的全称为 Model - View - Presenter 模型,他是将 Model 和 View 隔离开来,两者之间不相互作用,而是通过 Presenter 作为一个中间件进行通信;Presenter负责逻辑的处理,Model提供数据,View负责显示。
在这里插入图片描述

  • Model: 和 MVC 模式中的 Model 的功能是相同的,主要是做一些数据处理的功能,
  • View:就是直接对应了 Activity,Fragment 以及 layout 和 view 等控件,Presenter 就是他们俩之间沟通的桥梁。这样 Activity 的功能就被简化了,不再充当控制器,主要就是负责 View 层面的工作。
  • 这样我们就可以将业务逻辑处理部分代码从 MVC 中的 Controller 中取出,放入 Presenter 中。

在这里插入图片描述

V层

IMVPView

interface IMVPView {
    fun getUserInput(): String
    fun showSuccessPage(account: Account)
    fun showFailedPage()
}

MVPActivity

class MVPActivity : AppCompatActivity(), IMVPView {

    private lateinit var mMVPPresenter: MVPPresenter

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_normal)
        mMVPPresenter = MVPPresenter(this)
        initEvent()
    }

    private fun initEvent() {
        submit.setOnClickListener {
            mMVPPresenter.getData()
        }
    }

    // 获取用户输入信息
    override fun getUserInput(): String {
        return userName.text.toString()
    }

    // 展示获取数据成功的界面
    override fun showSuccessPage(account: Account) {
        result.text = "用户账号:" + account.name + " | " + "用户等级:" + account.level
    }

    // 展示获取数据失败的界面
    override fun showFailedPage() {
        result.text = "获取数据失败"
    }
}

M层

与MVC 中的 Model 的作用相同

class MVPModel {
    // 模拟查询账号数据
    fun getAccountData(accountName: String, callback: MCallback) {
        val random = Random()
        val isSuccess = random.nextBoolean()
        if (isSuccess) {
            val account = Account(accountName, 100)
            callback.onSuccess(account)
        } else {
            callback.onFailed()
        }
    }
}

P层

Presenter 持有 View 和 Model 的引用,通过 Presenter 我们向 Model 请求相关数据,并根据判断将结果返回 View 上面显示出来。

class MVPPresenter {

    private val mView: IMVPView
    private val mModel: MVPModel

    constructor(mView: IMVPView) {
        this.mView = mView
        mModel = MVPModel()
    }

    fun getData() {
        mModel.getAccountData(mView.getUserInput(), object : MCallback {
            override fun onSuccess(account: Account) {
                mView.showSuccessPage(account)
            }

            override fun onFailed() {
                mView.showFailedPage()
            }
        })
    }
}

优缺点

  • 优点:
    解决了 MVC 中 Controller 与 View 过度耦合的缺点,职责划分明显,更加易于维护。

  • 缺点:
    接口数量过多,项目复杂的升高。随着项目的复杂度升高, Presenter 层将会越来越臃肿

使用建议

结合上面所说的优缺点,有几条关于 MVP 的使用建议:

  • 接口规范化(封装父类接口以减少接口的使用量);
  • 使用第三方插件自动生成 MVP 代码;
  • 对于一些简单的页面,可以选择不使用框架;
  • 根据项目的复杂程度,部分模块可以选择不使用接口;
  • 其实这些操作都是为了一个目的:减少接口的数量。

MVVM

简介

MVVM 的全称是 Model - View - ViewModel 模型,他的模型结构和 MVP 很相像,但是在代码逻辑上 MVVM 会显得更加简洁,其实他就是将 Presenter 换为了 ViewModel。
在这里插入图片描述

MVVM 在 MVP 的基础上实现了数据视图的绑定(DataBinding),这样的话就不用使用接口进行传递了,而是当数据变化的时候,视图会自动更新;反之,当视图发生改变的时候,数据也会进行自动更新。

好处:

  • 减少了接口数量
  • 不用使用 findViewById 去操控 View

在这里插入图片描述
首先我们需要在app的 build.gradle 中声明使用 DataBinding

在 android 中添加下面代码

android {
    dataBinding {
        enabled = true
    }
}

使用 Kotlin 的话还需要添加下面代码

apply plugin: 'kotlin-kapt'//需要使用kapt作为注解处理器

kapt {
    generateStubs = true
}

MVVMModel

Model 和 MVP 中的一样

class MVVMModel {

    // 模拟查询账号数据
    fun getAccountData(accountName: String, callback: MCallback) {
        val random = Random()
        val isSuccess = random.nextBoolean()
        if (isSuccess) {
            val account = Account(accountName, 100)
            callback.onSuccess(account)
        } else {
            callback.onFailed()
        }
    }
}

MVVMViewModel

分析ViewModel中需要哪些变量以及方法,比如:getData(),需要调用 Model 中的 getAccountData 方法去获取数据,因为我们是使用 DataBinding 方法,所以在 ViewModel 中还需要记录对应的返回值用于展现,因为希望和数据变更进行绑定,防止每次都要更新所有的数据,所以类需要继承 BaseObservable。然后在元素的 get 和 set 方法做出修改,get 方法需要添加注解 @Bindable 而 set 方法中需要加入更新该元素显示的代码 notifyPropertyChanged(BR.XXX); ,BR 是什么呢,其实就是相当于 R 文件,用来确定是那个控件需要更新,只不过这里可以直接用变量名。

class MVVMViewModel : BaseObservable() {

    private val mvvmModel: MVVMModel = MVVMModel()

    @get:Bindable
    var userInput: String? = null
        set(userInput) {
            field = userInput
            notifyPropertyChanged(BR.userInput)
        }

    @get:Bindable
    var result: String? = null
        set(result) {
            field = result
            notifyPropertyChanged(BR.result)
        }

    fun getData(view: View) {
        this.userInput?.let {
            mvvmModel.getAccountData(it, object : MCallback {
                override fun onSuccess(account: Account) {
                    result = "用户账号:" + account.name + " | " + "用户等级:" + account.level
                }

                override fun onFailed() {
                    result = "获取数据失败"
                }
            })
        }
    }
}

布局文件

将 ViewModel 作为布局文件的参数,在布局文件中进行调用。

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>
        <variable
            name="viewModel"
            type="com.juny.mmm.mvvm.MVVMViewModel" />
    </data>

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

        <EditText
            android:id="@+id/userName"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_margin="10dp"
            android:hint="请输入用户名"
            android:text="@={viewModel.userInput}"
            android:textSize="14sp" />

        <Button
            android:id="@+id/submit"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_margin="30dp"
            android:onClick="@{viewModel.getData}"
            android:text="提交" />

        <TextView
            android:id="@+id/result"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_margin="10dp"
            android:text="@{viewModel.result}"
            android:textSize="14sp" />
    </LinearLayout>
</layout>

MVVMActivity

绑定数据,修改原有的 setContentView 方法,改为使用 DataBindingUtil.setContentView 方法进行绑定,然后我们将我们需要的 ViewModel 传入进去。
DataBinding 有单向绑定和双向绑定的区别,如果是单向绑定的话就是直接用 @{XXX} 的形式,如果是需要双向绑定的话就是需要改为 @={XXX} 的形式。
双向绑定的好处就是不仅在数据变化的时候进行刷新 View,也会在 View 主动修改数据的时候对数据进行更新,多用于编辑框等控件。

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.databinding.DataBindingUtil
import com.juny.mmm.R
import com.juny.mmm.databinding.ActivityMvvmBinding

class MVVMActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        var binding = DataBindingUtil.setContentView<ActivityMvvmBinding>(this, R.layout.activity_mvvm)
        var mvvmViewModel = MVVMViewModel()
        binding.viewModel = mvvmViewModel
    }
}

优缺点

  • 优点:
    实现了数据和视图的双向绑定,极大的简化代码

  • 缺点:
    Bug 难以调试,学习成本较大。

总结

框架名总结
MVC学习简单,但是解耦不够彻底
MVP解耦更加彻底,学习起来较为简单,但是代码相对较为繁琐
MVVM代码逻辑简洁,但是学习成本较大
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值