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;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
initView();
}
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 MVVMLoginBean) {
tv_showData.setText("登录成功:" + MVVMLoginBean.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 MVVMLoginBean, OnLoginCallback loginCallback) {
if (SQL_Utils.userName.equals(MVVMLoginBean.getUserName()) && SQL_Utils.passWord.equals(MVVMLoginBean.getPassWord())) {
//登录成功
loginCallback.onSuccess(MVVMLoginBean);
} else {
//登录失败
loginCallback.onError();
}
}
}
Footer
用以上代码就可以实现一个登录实例,我们将这个代码图解一下:
界面代码-视图层:界面绘画、界面初始化、界面事件监听、界面数据更新
业务代码-控制层:登录需求、注册需求...
逻辑代码-模型层:需求具体的实现逻辑
目前我们可以看到 界面代码、业务代码、逻辑代码 这些代码均在同一个界面,当我们这个界面功能变多了,那这个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:
**/
public class MVC_Model {
//登录请求
public void loginRequest(LoginBean loginBean, OnLoginCallback loginCallback) {
if (SQL_Utils.userName.equals(loginBean.getUserName()) && SQL_Utils.passWord.equals(loginBean.getPassWord())) {
//登录成功
loginCallback.onSuccess(loginBean);
} else {
//登录失败
loginCallback.onError();
}
}
}
MVC_Activity
public class MVC_Activity extends AppCompatActivity implements View.OnClickListener {
private TextView tv_showData;
public EditText et_userName;
public 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();
}
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() {
@SuppressLint("SetTextI18n")
@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 没有变化
/**
* author:hello
* time:2020/7/31
* CSDN: qq_39799899
* explain:
**/
public class MVP_Model {
//登录请求
public void loginRequest(LoginBean loginBean, OnLoginCallback loginCallback) {
if (SQL_Utils.userName.equals(loginBean.getUserName()) && SQL_Utils.passWord.equals(loginBean.getPassWord())) {
//登录成功
loginCallback.onSuccess(loginBean);
} else {
//登录失败
loginCallback.onError();
}
}
}
MVP_View
/**
* author:hello
* time:2020/8/1
* CSDN: qq_39799899
* explain:主要负责View数据的 获取 与 赋值
**/
public interface MVP_View extends OnLoginCallback{
LoginBean getLoginData();
}
接下来我们来看看Presenter如何来协调 MVP_View 与 MVP_Model 的
/**
* author:hello
* time:2020/8/1
* CSDN: qq_39799899
* explain:
**/
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;
this.mvp_model = new MVP_Model();
}
public void login(LoginBean loginBean) {
mvp_model.loginRequest(loginBean, new OnLoginCallback() {
@Override
public void onSuccess(LoginBean MVVMLoginBean) {
mvp_view.onSuccess(MVVMLoginBean);
}
@Override
public void onError() {
mvp_view.onError();
}
});
}
}
我们来看看图解:
接下来我们来看看 业务代码与逻辑代码都被解耦了,那Activity还有什么事呢?
/**
* 主要负责初始化数据
*/
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);
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);
mvp_presenter = new MVP_Presenter(this);
}
@Override
public void onClick(View v) {
mvp_presenter.login(getLoginData());
}
@Override
public LoginBean getLoginData() {
String userName = et_userName.getText().toString();
String passWord = et_passWord.getText().toString();
return new LoginBean(userName, passWord);
}
@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