Android——超简单 MVCMVPMVVM入门系列
Posted PlayfulKing
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android——超简单 MVCMVPMVVM入门系列相关的知识,希望对你有一定的参考价值。
2022年,新年第一篇文章,本篇文章将用非常简单的言语来描述各框架,尽量让大家一看即会。
前言:
相信不少伙伴在进行android开发的时候,肯定遇见过 Activity 代码上千行的,这种代码非常难以维护,牵一发而动全身,像极了某印#国的电线杆的电线一样,网上调侃程序员修水管,越修水越多估计也是这么来的。
而框架意在将这Activity中上千行代码进行功能分类,并提高相同功能的重复使用率,我们大体可将功能简单分为三种 界面代码、业务代码、逻辑代码。让各自专一的完成各自任务。
各自负责区域
界面代码-视图层:界面绘画、界面初始化、界面事件监听、界面数据更新
业务代码-控制层:登录需求、注册需求...
逻辑代码-模型层:需求具体的实现逻辑
接下来我们来按照 Demo无框架 -> MVC -> MVP -> MVVM 的顺序来讲解一下各自的实现与区别,本章附带源码:https://github.com/1079374315/Dome_MVC_MVP_MVVM
实现的案例:
单击按钮进行单机登录,这个例子够简单了吧,我们来看看不用框架实现时的情况。
(本代码均简易实现,不进行复杂的逻辑判断,意在将代码简化让读者更好的理解框架)
我们先看看总体结构:
公共类介绍:
实体类
数据库封装类
反馈接口
Demo无框架
public class Demo_Activity extends AppCompatActivity implements View.OnClickListener
private TextView tv_showData;
private EditText et_userName;
private EditText et_passWord;
private SQLUtils sqlUtils;
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
initView();//初始化UI组件
sqlUtils = new SQLUtils();//初始化数据库操作类
private void initView()
tv_showData = findViewById(R.id.tv_showData);
et_userName = findViewById(R.id.et_userName);
et_passWord = findViewById(R.id.et_passWord);
findViewById(R.id.btn_login).setOnClickListener(this);//注册单击事件
@Override
public void onClick(View v)
//单击进行登录
loginRequest(getLoginData(), new OnLoginCallback()
@SuppressLint("SetTextI18n")
@Override
public void onSuccess(LoginBean loginBean)
tv_showData.setText("登录成功:" + loginBean.toString());
@Override
public void onError()
tv_showData.setText("登录失败,账号密码错误!");
);
//获取登录数据
private LoginBean getLoginData()
String userName = et_userName.getText().toString();
String passWord = et_passWord.getText().toString();
return new LoginBean(userName, passWord);
//登录请求,将数据与数据库的数据进行对比
private void loginRequest(LoginBean loginBean, OnLoginCallback loginCallback)
if (sqlUtils.queryUserName().equals(loginBean.getUserName()) &&
sqlUtils.queryPassWord().equals(loginBean.getPassWord()))
//登录成功
loginCallback.onSuccess(loginBean);//调用接口反馈成功数据
else
//登录失败
loginCallback.onError();//调用接口反馈失败数据
用以上代码就可以实现一个登录实例,我们将这个代码图解一下:
界面代码-视图层:界面绘画、界面初始化、界面事件监听、界面数据更新
业务代码-控制层:登录需求、注册需求...
逻辑代码-模型层:需求具体的实现逻辑
目前我们可以看到 界面代码、业务代码、逻辑代码 这些代码均在同一个界面,当我们这个界面功能变多了,那这个Activity将会越来越臃肿,变得难以维护。
现在我们用 MVC 来看看如何把他进行解耦优化。
MVC模式:
(先解释一下箭头)
View -> Controller 表示:Controller持有 View类的引用,单向则表示,Controller可调用View类中方法,但View无法调用Contriller, 后面的Controller -> Model 也一样。
界面代码-视图层-View:界面绘画、界面初始化、界面事件监听、界面数据更新
业务代码-控制层-Controller:登录需求、注册需求...
逻辑代码-模型层-Model:需求具体的实现逻辑
我们先看看总体结构:
公共类还是用之前的,MVC就新增了两个类,Activity 与 Model
MVC_Model
/**
* author:hello
* time:2020/7/31
* CSDN: qq_39799899
* explain:其实就是将与UI无关的事情移至到 Mvc_Model中
* 如:网络请求、数据获取、查询等
**/
public class MVC_Model
private SQLUtils sqlUtils;
public MVC_Model()
sqlUtils = new SQLUtils();//初始化数据库封装类
//登录请求,将数据与数据库的数据进行对比
public void loginRequest(LoginBean loginBean, OnLoginCallback loginCallback)
if (sqlUtils.queryUserName().equals(loginBean.getUserName()) &&
sqlUtils.queryPassWord().equals(loginBean.getPassWord()))
//登录成功
loginCallback.onSuccess(loginBean);
else
//登录失败
loginCallback.onError();
MVC_Activity
public class MVC_Activity extends AppCompatActivity implements View.OnClickListener
private TextView tv_showData;
private EditText et_userName;
private EditText et_passWord;
private MVC_Model mvc_model;
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
initView();
mvc_model = new MVC_Model();//初始化Model
private void initView()
tv_showData = findViewById(R.id.tv_showData);
et_userName = findViewById(R.id.et_userName);
et_passWord = findViewById(R.id.et_passWord);
findViewById(R.id.btn_login).setOnClickListener(this);//注册事件
@Override
public void onClick(View v)
mvc_model.loginRequest(getLoginData(), new OnLoginCallback() //业务代码
@Override
public void onSuccess(LoginBean loginBean)
tv_showData.setText("登录成功:" + loginBean.toString());//设置数据
@Override
public void onError()
tv_showData.setText("登录失败,账号密码错误!");//设置数据
);
//获取登录数据
public LoginBean getLoginData()
String userName = et_userName.getText().toString();
String passWord = et_passWord.getText().toString();
return new LoginBean(userName, passWord);
我们MVC图解一下:
界面代码-视图层:界面绘画、界面初始化、界面事件监听、界面数据更新
业务代码-控制层:登录需求、注册需求...
逻辑代码-模型层:需求具体的实现逻辑
在MVC的体系上是不是发现,逻辑层代码已经被搬离出去了,这里就做到了逻辑代码的解耦,接下来我们再分析一下逻辑层MVC_Model里的代码
只需要调用者提供数据源与反馈数据接口,不管是那个Activity,都可以继续重用该业务逻辑代码,这样是不是一下就提高了代码的重复利用性,降低了代码的耦合性,而且今后改逻辑也只需要改一处就好。
我们再来看看项目中自带的MVC文档
MVC缺点也确实很明显,逻辑代码确实已经被搬离出去了,但控制层的代码的代码依旧还在Activity中,接下来我们来看看如何将这控制层的代码也搬离出去,进行更优的解耦。
MVP模式:
(先解释一下箭头)
View →← Presenter表示:Presenter与View类 相互持有彼此的引用,Controller可调用View类中方法,且View同样也可调用Presenter方法, 后面的Controller -> Model 也一样。
Presenter意在将MVC中 View 持有Model引用给完全隔离掉,View 与 Model之间交互完全由Presenter来帮助与协调。
MVC示意图
我们先看看总体结构
公共类上面已介绍过了,我们继续来看看MVP这些接口
Model 与 上面MVC里介绍的Model 没有变化
public class MVP_Model
private SQLUtils sqlUtils;
public MVP_Model()
sqlUtils = new SQLUtils();
//登录请求
public void loginRequest(LoginBean loginBean, OnLoginCallback loginCallback)
if (sqlUtils.queryUserName().equals(loginBean.getUserName()) &&
sqlUtils.queryPassWord().equals(loginBean.getPassWord()))
//登录成功
loginCallback.onSuccess(loginBean);
else
//登录失败
loginCallback.onError();
MVP_View
public interface MVP_View extends OnLoginCallback
LoginBean getLoginData();
接下来我们来看看Presenter如何来协调 MVP_View 与 MVP_Model 的
public class MVP_Presenter
private MVP_View mvp_view;
private MVP_Model mvp_model;
public MVP_Presenter(MVP_View mvp_view)
this.mvp_view = mvp_view;//view接口由Activity那边传递
this.mvp_model = new MVP_Model();//model在Presenter内创建
public void loginRequest(LoginBean loginBean)
mvp_model.loginRequest(loginBean, new OnLoginCallback()
public void onSuccess(LoginBean loginBean1)
mvp_view.onSuccess(loginBean1);//反馈成功数据
public void onError()
mvp_view.onError();//反馈失败
);
我们来看看图解:
接下来我们来看看 业务代码与逻辑代码都被解耦了,那Activity还有什么事呢?
//实现 MVP_View 接口重写 getLoginData、onSuccess、onError、进行获取数据与数据更新
public class MVP_Activity extends AppCompatActivity
implements View.OnClickListener, MVP_View
private TextView tv_showData;
public EditText et_userName;
public EditText et_passWord;
private MVP_Presenter mvp_presenter;
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
initView();
mvp_presenter = new MVP_Presenter(this);
private void initView()
tv_showData = findViewById(R.id.tv_showData);
et_userName = findViewById(R.id.et_userName);
et_passWord = findViewById(R.id.et_passWord);
findViewById(R.id.btn_login).setOnClickListener(this);
@Override
public void onClick(View v)
mvp_presenter.loginRequest(getLoginData());//请求登录
@Override
public LoginBean getLoginData()
String userName = et_userName.getText().toString();
String passWord = et_passWord.getText().toString();
return new LoginBean(userName, passWord);
@SuppressLint("SetTextI18n")
@Override
public void onSuccess(LoginBean MVVMLoginBean)
tv_showData.setText("登录成功:" + MVVMLoginBean.toString());
@Override
public void onError()
tv_showData.setText("登录失败,账号密码错误!");
这张图解是不是发现大多数都是界面代码没发现有逻辑代码了,因为逻辑代码已经被解耦到 Presenter里去了,View没有Model的引用了。
而且你细心一点就会发觉,MVP_View 这个接口也是可以多次实现的,也就是说,目前的Presenter也是可以复用,Model也是可以复用的。
界面代码-视图层:界面绘画、界面初始化、界面事件监听、界面数据更新
业务代码-控制层:登录需求、注册需求...
逻辑代码-模型层:需求具体的实现逻辑
MVP示意图
MVP不愧是MVC的升级版,更加提升了框架代码的复用性
我们来看看MVP的文档
其实MVP已经很优解了,解耦性也比较不错了,但我们还有更优解的 MVVM模式且不用写那么多接口,代码量也更少
MVVM模式:
(先解释一下箭头)
View ↔ ViewModel表示:View与ViewModel相互绑定,然后ViewModel与Model又相互持有彼此引用,相互持有引用相信大家已经非常熟悉了,但这相互绑定是怎样的,没了解过的伙伴估计不太理解。
相互绑定的话,View可以通过 ViewModel 从 Model 中获取数据;当获取到了数据之后,会通过自动绑定相互绑定的作用,设置到View界面展示。
相互绑定的作用:可以省去 组件获取、事件注册、数据获取、数据更新这些代码。我们来看看之前MVP中Activity代码,除去Presenter代码与Model代码,View代码里剩下的全是组件获取、事件注册、数据获取、数据更新了,而MVVM就是省去这些代码。
MVP Activity代码
我们先看看总体结构:
想要实现MVVM就得 打开DataBindingUtil :找到.app
在android下添加开启:
然后我们再来看看MVVM 的 Model,铁打的Model 流水的View, Model层与MVC、MVP的Model一样。
Model
public class MVVM_Model
private SQLUtils sqlUtils;
public MVVM_Model()
sqlUtils = new SQLUtils();
//登录请求
public void loginRequest(LoginBean loginBean, OnLoginCallback loginCallback)
if (sqlUtils.queryUserName().equals(loginBean.getUserName()) &&
sqlUtils.queryPassWord().equals(loginBean.getPassWord()))
//登录成功
loginCallback.onSuccess(loginBean);
else
//登录失败
loginCallback.onError();
我们再来看看 ViewModel 是如何与View互相绑定的
ViewModel
public class MVVM_ViewModel extends BaseObservable
private MVVM_Model mvvm_model;
private String result;
private ActivityMvvmBinding binding;
//该方法由View界面自动调用
@Bindable
public String getResult()
return result;
//设置结果到View界面并刷新
public void setResult(String result)
this.result = result;
notifyPropertyChanged(BR.result);
//构造方法初始化
public MVVM_ViewModel(ActivityMvvmBinding binding)
mvvm_model = new MVVM_Model();
this.binding = binding;
//通过binding获取数据
public LoginBean getLoginBean()
return new LoginBean(
binding.etUserName.getText().toString()
, binding.etPassWord.getText().toString()
);
//View界面注册的单击事件
public void loginRequest(View view)
mvvm_model.loginRequest(getLoginBean(), new OnLoginCallback()
@Override
public void onSuccess(LoginBean loginBean)
setResult("获取成功:" + loginBean.toString());
@Override
public void onError()
setResult("获取失败:账号或密码错误!");
);
我们来图解一下:图有点大,但放大看还是看的很透彻的
看完这张图你应该可以明白,View与ViewModel是如何绑定的了吧。
那我们再来看看此时的Activity还负责那些代码
仅负责 View 与 ViewModel 之间的绑定后,就做甩手掌柜了。
现在是不是感觉代码少了很多很多。
小伙伴门可以将Model 静态化试试,将会有新的发现。
博主将 MVC MVP MVVM 的辅助方法加入了GT库中,在MVVM的基础上更优解,感兴趣的小伙伴们可 下载MVVM-GT版 进行查看
本章演示源码:GitHub - 1079374315/Dome_MVC_MVP_MVVM
MVVM-GT库版源码:GitHub - 1079374315/MVC_MVP_MVVM_GT
以上是关于Android——超简单 MVCMVPMVVM入门系列的主要内容,如果未能解决你的问题,请参考以下文章