Android开发中MVC、MVP到MVVM演化
文章目录
- 一般模式
- activity_normal.xml
- NormalActivity
- Account
- MCallback
- MVC
- MVC简介
- MVC各层功能
- MVCModel
- MVCActivity
- 优缺点
- MVP
- 简介
- V层
- IMVPView
- MVPActivity
- M层
- P层
- 优缺点
- 使用建议
- MVVM
- 简介
- MVVMModel
- MVVMViewModel
- 布局文件
- MVVMActivity
- 优缺点
- 总结
有一个需求:需要查询用户账号信息,用户输入账号,点击按钮可进行查询账号信息,如果查询数据成功,则将数据展示在界面上;如果查询数据失败,则在界面上提升获取数据失败。
一般模式
使用一个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"><EditTextandroid:id="@+id/userName"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_margin="10dp"android:hint="请输入用户名"android:textSize="14sp" /><Buttonandroid:id="@+id/submit"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_margin="30dp"android:text="提交" /><TextViewandroid: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: MVCModeloverride 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(): Stringfun showSuccessPage(account: Account)fun showFailedPage()
}
MVPActivity
class MVPActivity : AppCompatActivity(), IMVPView {private lateinit var mMVPPresenter: MVPPresenteroverride 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: IMVPViewprivate val mModel: MVPModelconstructor(mView: IMVPView) {this.mView = mViewmModel = 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:Bindablevar userInput: String? = nullset(userInput) {field = userInputnotifyPropertyChanged(BR.userInput)}@get:Bindablevar result: String? = nullset(result) {field = resultnotifyPropertyChanged(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><variablename="viewModel"type="com.juny.mmm.mvvm.MVVMViewModel" /></data><LinearLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><EditTextandroid: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" /><Buttonandroid:id="@+id/submit"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_margin="30dp"android:onClick="@{viewModel.getData}"android:text="提交" /><TextViewandroid: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.ActivityMvvmBindingclass 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 | 代码逻辑简洁,但是学习成本较大 |
Android开发中MVC、MVP到MVVM演化相关推荐
- Android 开发架构-MVC MVP MVVM详解
何为架构 架构,即程序的逻辑组织结构,是指导开发过程中划分程序逻辑模块的关键,好的架构要使程序达到高内聚低耦合的设计目标.例如一个人,身体的骨骼即为身体的架构,有了基本骨架之后,才可以决定在头颅里开发 ...
- android中的mvp模式怎么定义,详解MVP模式在Android开发中的应用
一.MVP介绍 随着UI创建技术的功能日益增强,UI层也履行着越来越多的职责.为了更好地细分视图(View)与模型(Model)的功能,让View专注于处理数据的可视化以及与用户的交互,同时让Mode ...
- MVC, MVP 与 MVVM
前几天读了阮一峰老师的文章<MVC,MVP 和 MVVM 的图示>,觉得讲得十分清晰,所以在这里做一波复习和总结. MVC, MVP 与 MVVM 是三种常见的软件架构,它们之间的特点与区 ...
- Android开发中dip,dpi,density,px等详解
Android开发中的各种单位的解释 Px (Pixel像素) 也称为图像元素,是作为图像构成的基本单元,单个像素的大小并不固定,跟随屏幕大小和像素数量的关系变化(屏幕越大,像素越低,单个像素越大,反 ...
- Android开发中应避免的重大错误
by Varun Barad 由Varun Barad Android开发中应避免的重大错误 (Critical mistakes to avoid in Android development) A ...
- android 4.4 禁止下拉,Android开发中禁止下拉式的实现技巧
我们开发项目的时候,经常会看到禁止的情况,而Android开发中并没有直接调用的接口,下面是爱站技术频道小编就给大家介绍的Android开发中禁止下拉式的实现技巧,希望网友们喜欢! 分享给大家供大家参 ...
- 史上最全Android开发中100%会用到的开源框架整理(1/5)
其实这个开源框架整理很久了,只是一直放在有道云笔记里面,笔者还有很多写得文章都放在有道云笔记里面,有时间都好好整理一下放出来,本篇文章也会不定期更新,由于整理的开源框架分类都有200多个,所有这次只将 ...
- Android开发中使用七牛云存储进行图片上传下载
Android开发中的图片存储本来就是比较耗时耗地的事情,而使用第三方的七牛云,便可以很好的解决这些后顾之忧,最近我也是在学习七牛的SDK,将使用过程在这记录下来,方便以后使用. 先说一下七牛云的存储 ...
- 5 个 Android 开发中比较常见的内存泄漏问题及解决办法
Android开发中,内存泄漏是比较常见的问题,有过一些Android编程经历的童鞋应该都遇到过,但为什么会出现内存泄漏呢?内存泄漏又有什么影响呢? 在Android程序开发中,当一个对象已经不需要再 ...
最新文章
- Android开发神器:OkHttp框架源码解析
- 07/11/20 资料整理
- excel中如何et vb根据数据自动生成表格_如何实现excel与PPT互联互通(动态生成PPT)...
- Oracle 排序中使用nulls first 或者nulls last 语法
- 通过 .htaccess 实现缓存策略
- extjs之TypeError: d.read is not a function解决方案
- Live meeting+OCS 二次开发手记
- 麦克风设计指导与选型参考
- leetcode 279. 完全平方数(dp)
- 前端学习(2026)vue之电商管理系统电商系统之实现分页功能
- 2020班徽设计图案高中计算机,高铁工程学院举办2020级班徽设计大赛
- 兼容IE和FF:获取Referer的JS和PHP方法 及 PHP利用curl伪造IP和来路
- 深度克隆对象【前端每日一题-19】
- 使用注解方式搭建SpringMVC
- 鸿蒙系统u盘制作,WINDOWS系列 篇二:【保姆级】Windows 10安装版原版系统U盘制作及系统安装教程...
- 计算机用户目录是什么,c盘里面users是什么文件夹
- 有关H5第八章的页面布局与规划介绍
- c语言如何画简单图形,如何用C语言画基本图形
- 实例分析!如何快速搭建OA办公系统
- 20条技巧,让Chrome超越Firefox (2010-11-16更新)
热门文章
- 德源希望教育救助中心第十所希望小学落户河北省涉县
- 根据出生日期,得出年龄
- 2022-2023第一学期微信小程序期末实训报告
- FFmpeg之ffprobe
- Ubuntu 18.04 安装最新版本wine(支持微信、企业微信)
- ORA-01034:oracle不可用 的解决方法
- python3.6爬虫源代码_基于Python3.6爬虫 采集知网文献(更新)
- 七分养护三分维修 关注车辆的生命周期保养
- MISCONF Redis is configured to save RDB snapshots~
- C语言趣味小游戏——猜数字(1到100之间)