Markdown版本笔记 | 我的GitHub首页 | 我的博客 | 我的微信 | 我的邮箱 |
---|---|---|---|---|
MyAndroidBlogs | baiqiantao | baiqiantao | bqt20094 | baiqiantao@sina.com |
目录
MVC 架构
MVC各层的作用
Android中的实际情况
演示案例
BaseModel
Callback
SampleModel
SampleActivity
案例总结
MVP 架构
基本概念
特点
MVP各层的作用
用MVP架构编写登录模块完整版
定义Presenter接口(可选)
定义Model接口(可选)及MP回调接口(必选)
定义View接口(必选)
定义Presenter的实现类
定义Model的实现类
让Activity实现View接口
用MVP架构编写登录模块简洁版
View层接口
Activity
Presenter
Model
MVVM 架构
如何选择
MVC 架构
MVC各层的作用
- M层:Model,SQL、XML、JSON,数据模型。负责与数据处理相关的业务逻辑的处理,比如数据库读写操作,网络请求操作,复杂的算法,耗时的任务等。Model是一个应用系统的核心部分,代表了该系统实际要实现的所有功能。当M层完成数据处理后,会通知Controller更新View。
- V层:View,XML布局、自定义View,Java编写的View。负责在屏幕上渲染出相应的图形信息展示给用户看。
- C层:Controlle,Activity或者Fragmentr,控制器。负责接收如点击、触摸、电话呼入、网络改变等外部事件,并向Model层发送数据请求。同时负责接收Model层处理完数据后发的通知,并更新View。Controller是View和Model之间通信的桥梁。
Android中的实际情况
其实Android中只有MV
Android下MVC中的控制层是由Activity来承担的,Activity本来主要是作为初始化页面,展示数据的操作,但是因为XML视图功能太弱,所以Activity既要负责视图的显示又要加入控制逻辑,承担的功能过多。
比如对于登录页面,MVC的基本流程为:
用户与View交互,View接收并反馈用户的动作,View把用户的请求传给相应的控制器,由控制器决定调用哪个模型,然后由模型调用相应的业务逻辑对用户请求进行加工处理,如果需要返回数据,模型会把相应的数据返回给控制器,由控制器调用相应的视图,最终由视图渲染返回的数据。
在Android开发中,Activity并不是一个标准的MVC模式中的Controller,它的首要职责是加载应用的布局和初始化用户界面,接受并处理来自用户的操作请求,进而作出响应。随着界面及其逻辑的复杂度不断提升,Activity类的职责不断增加,以致变得庞大臃肿。
比如在Android中,对于登录页面,典型的交互过程是这样的:
用户点击登录按钮 → Activity中注册的监听器检测到点击事件 → Activity通过转动View中的ProgressBar来响应用户点击事件 → Activity通过View中的EditText获取用户输入的账户密码 → Activity将数据交由业务逻辑层(Model层)处理 → Model层处理完成后通过回调将数据返回给Activity → Activity更新UI反馈给用户
由上面的案例可以看出,其实这个View对应于布局文件能做的事情特别少,实际上关于该布局文件中的数据绑定、事件处理的代码都在Activity中,造成了Activity既像View又像Controller,这可能也就是为何:
Most of the modern Android applications just use View-Model
architecture,everything is connected with Activity.
演示案例
BaseModel
BaseModel顾名思义就是所有业务逻辑model的父类,这里的onDestroy()
方法用于跟activity或者fragment生命周期同步,在destroy做一些销毁操作
public interface BaseModel { void onDestroy(); }
Callback
Callback是根据View或者Controller调用Model时回调的参数个数选择使用
public interface Callback1<T> { void onCallBack(T t); }
public interface Callback2<T, P> { void onCallBack(T t, P p); }
SampleModel
SampleModel是我们业务逻辑的具体实现
public class SampleModel implements BaseModel { public void getUserInfo(String uid, Callback1<UserInfo> callback) { UserInfo userInfo = new UserInfo(); //...从网络或数据库等获取数据 callback.onCallBack(userInfo); } public void getUserInfo2(String uid, Callback2<UserInfo, String> callback) { UserInfo userInfo = new UserInfo(); //...从网络或数据库等获取数据 callback.onCallBack(userInfo, "其他数据"); } @Override public void onDestroy() { } public class UserInfo { private int age; private String name; //... } }
SampleActivity
public class SampleActivity extends AppCompatActivity { private SampleModel sampleModel; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_sample); sampleModel = new SampleModel(); String uid = "123456"; findViewById(R.id.button).setOnClickListener(view -> getUserInfo(uid)); } @Override protected void onDestroy() { super.onDestroy(); sampleModel.onDestroy(); } //获取用户信息 private void getUserInfo(String uid) { sampleModel.getUserInfo(uid, userInfo -> { //...设置用户信息到view }); } }
案例总结
事件的流向
- button点击事件的触发:View → Controller
- 获取用户信息事件的触发:Controller → Model → Controller
- 绑定用户信息到View:Controller → View
总结
- 具有一定的分层,model彻底解耦,controller和view并没有解耦
- 层与层之间的交互尽量使用回调或者去使用消息机制去完成,尽量避免直接持有
- controller和view在android中无法做到彻底分离,但在代码逻辑层面一定要分清
- 业务逻辑被放置在model层,能够更好的复用和修改增加业务
MVP 架构
MVP的核心就是:让M和V完全解耦,通过Presenter统一调度管理
MVP的基本流程:Presenter从View中获取需要的参数,交给Model去处理,Model执行过程中的反馈以及结果告诉Presenter,Presenter再让View做对应的显示。
基本概念
MVP跟MVC很相像,根据MVC的发展来看,我们把MVP当成MVC来看也不为过,因为MVP也是三层,唯一的差别是Model和View之间不进行通讯
,都是通过Presenter完成。
前面介绍MVC的时候提到了算是致命缺点吧,在android中由于activity(god object)的存在,Controller和View很难做到完全解耦
。但在MVP中就可以很好的解决这个问题
特点
- 是MVC的演化版本
让Model和View完全解耦,由Presenter负责完成View与Model的交互
将Actvity视为View层
,减少了Activity的职责,将复杂的逻辑代码提取到了Presenter中
Presenter与View之间的交互完全是通过接口的
- 代码很清晰,不过增加了很多类;耦合度更低,更方便的进行测试;有助于协同开发,降低维护成本
MVP各层的作用
- Model:数据模型,和MVC中的Model一样
- View:对应UI界面(包括Activity、Fragment、以及所有视图),负责View的绘制以及与用户交互
- Presenter:调度者,负责完成View和Model间的交互
用MVP架构编写登录模块完整版
定义Presenter接口(可选)
分析这个模块需要哪些业务逻辑,或者说有哪些复杂的功能,以此定义Presenter接口。
对于登录模块,主要的就是登录功能。为了增加接口的复杂度,这里我又添加了一个退出前清理功能。
public interface Login_Presenter_I { void login(String username, String password);//登录过程可能涉及到很工作,所以把它抽出来。此过程需要与View交互 void onFinishActivity();//退出前可能要做很多清理工作,所以也把它抽出来。此过程不需要与View交互 }
定义Model接口(可选)及MP回调接口(必选)
分析上述Presenter层中的功能在被Model层处理过程中,Model需要通知Presenter哪些内容,以此定义Model接口。
Model层在执行过程中,是通过接口通知Presenter执行过程中的状态以及执行完毕后的结果的,为了逻辑更清晰,建议此此接口定义为Model层接口的内部接口。
public interface Login_Model_I { //参数 listener:Model层通过此接口通知Presenter执行过程中的状态以及执行完毕后的结果 void login(String username, String password, OnLoginListener listener);//执行过程中需要通知Presenter void clearBeforeFinishActivity(boolean clearSp, boolean deleteCache);//执行过程中不需要通知Presenter //将Model层需要通知Presenter的内容,按照类别,定义在不同的接口中 interface OnLoginListener { void onUsernameError(); void onPasswordError(); void onSuccess(); } }
定义View接口(必选)
分析所有可能会操作UI的最基础逻辑,以此定义View接口。
Presenter层是通过接口来操作View层的。
public interface Login_Activity_I { void showProgress(); void hideProgress(); void setUsernameError(); void setPasswordError(); void showToast(String msg, int duration); }
定义Presenter的实现类
public class Login_Presenter_Impl implements Login_Presenter_I, Login_Model_I.OnLoginListener { private Login_Activity_I loginActivityI; //拿到的是接口,整个Presenter中没有导入任何View和Activity private Login_Model_I loginIModel; //拿到的是Model层的实现类 public Login_Presenter_Impl(Login_Activity_I loginActivityI) { this.loginActivityI = loginActivityI;//View层的实现类,由Activity传过来 this.loginIModel = new Login_Model_Impl();//Model层的实现类,由Activity传过来或自己创建均可 } @Override public void login(String username, String password) { if (loginActivityI != null) loginActivityI.showProgress();//通知View更新UI loginIModel.login(username, password, this);//交给Model层处理 } @Override public void onFinishActivity() { if (loginActivityI != null) loginActivityI.showProgress();//通知View更新UI boolean clearSp = new Random().nextBoolean(); boolean deleteCache = new Random().nextBoolean(); loginIModel.clearBeforeFinishActivity(clearSp, deleteCache);//交给Model层处理 if (loginActivityI != null) loginActivityI.hideProgress();//通知View更新UI } @Override public void onUsernameError() { if (loginActivityI != null) { loginActivityI.setUsernameError(); loginActivityI.hideProgress(); } } @Override public void onPasswordError() { if (loginActivityI != null) { loginActivityI.setPasswordError(); loginActivityI.hideProgress(); } } @Override public void onSuccess() { if (loginActivityI != null) loginActivityI.showToast("登录成功", Toast.LENGTH_SHORT); } }
定义Model的实现类
Model层的实现类,只处理逻辑,完全不操作View,对逻辑处理的状态反馈给Presenter
public class Login_Model_Impl implements Login_Model_I { @Override public void login(String username, String password, OnLoginListener listener) { if (TextUtils.isEmpty(username)) listener.onUsernameError(); else if (TextUtils.isEmpty(password)) listener.onPasswordError(); else listener.onSuccess(); } @Override public void clearBeforeFinishActivity(boolean clearSp, boolean deleteCache) { if (clearSp) Log.i("bqt", "【清理SP】"); if (deleteCache) Log.i("bqt", "【清理缓存】"); } }
让Activity实现View接口
public class Login_Activity extends Activity implements Login_Activity_I, View.OnClickListener { private ProgressBar progressBar; private EditText username; private EditText password; private Login_Presenter_I presenter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_login); progressBar = (ProgressBar) findViewById(R.id.progress); username = (EditText) findViewById(R.id.username); password = (EditText) findViewById(R.id.password); findViewById(R.id.button).setOnClickListener(this); presenter = new Login_Presenter_Impl(this);//new一个Presenter的实现类,把自己传过去。实际上接收的只是LoginView接口的实例 } @Override public void finish() { presenter.onFinishActivity(); super.finish(); } @Override public void showProgress() { progressBar.setVisibility(View.VISIBLE); } @Override public void hideProgress() { progressBar.setVisibility(View.GONE); } @Override public void setUsernameError() { username.setError(getString(R.string.username_error)); } @Override public void setPasswordError() { password.setError(getString(R.string.password_error)); } @Override public void showToast(String msg, int duration) { Toast.makeText(this, msg, duration).show(); } @Override public void onClick(View v) { //点击登录时,View把数据传给presenter,presenter处理完数据后通知View处理事件 presenter.login(username.getText().toString(), password.getText().toString()); } }
用MVP架构编写登录模块简洁版
View层接口
public interface LoginView { void showProgress(); void hideProgress(); void setUsernameError(); void setPasswordError(); void navigateToHome(); }
Activity
public class LoginActivity extends AppCompatActivity implements LoginView { private ProgressBar progressBar; private EditText username; private EditText password; private LoginPresenter presenter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_login); progressBar = findViewById(R.id.progress); username = findViewById(R.id.username); password = findViewById(R.id.password); findViewById(R.id.button).setOnClickListener(v -> login()); presenter = new LoginPresenter(this, new LoginModel()); } @Override protected void onDestroy() { presenter.onDestroy(); super.onDestroy(); } @Override public void showProgress() { progressBar.setVisibility(View.VISIBLE); } @Override public void hideProgress() { progressBar.setVisibility(View.GONE); } @Override public void setUsernameError() { username.setError("用户名错误"); } @Override public void setPasswordError() { password.setError("密码错误"); } @Override public void navigateToHome() { startActivity(new Intent(this, MainActivity.class)); finish(); } private void login() { presenter.login(username.getText().toString(), password.getText().toString()); } }
Presenter
public class LoginPresenter implements LoginModel.OnLoginFinishedListener { private LoginView LoginView; private LoginModel loginModel; LoginPresenter(LoginView LoginView, LoginModel loginModel) { this.LoginView = LoginView; this.loginModel = loginModel; } public void login(String username, String password) { if (LoginView != null) { LoginView.showProgress(); } loginModel.login(username, password, this); } public void onDestroy() { LoginView = null; } @Override public void onUsernameError() { if (LoginView != null) { LoginView.setUsernameError(); LoginView.hideProgress(); } } @Override public void onPasswordError() { if (LoginView != null) { LoginView.setPasswordError(); LoginView.hideProgress(); } } @Override public void onSuccess() { if (LoginView != null) { LoginView.navigateToHome(); } } }
Model
public class LoginModel { interface OnLoginFinishedListener { void onUsernameError(); void onPasswordError(); void onSuccess(); } public void login(final String username, final String password, final OnLoginFinishedListener listener) { new Handler().postDelayed(() -> { if (TextUtils.isEmpty(username)) { listener.onUsernameError(); return; } if (TextUtils.isEmpty(password)) { listener.onPasswordError(); return; } listener.onSuccess(); }, 2000); } }
MVVM 架构
MVP中,随着业务逻辑的增加,UI的改变多的情况下,会有非常多的跟UI相关的case,这样就会造成View的接口会很庞大。而MVVM就解决了这个问题,通过双向绑定
的机制,实现数据和UI内容,只要想改其中一方,另一方都能够及时更新的一种设计理念,这样就省去了很多在View层中写很多case的情况,只需要改变数据就行。
先看下MVVM设计图:
这看起来跟MVP好像没啥差别,其实区别还是挺大的,在MVP中,View和presenter要相互持有,方便调用对方,而在MVP中,View和ViewModel通过Binding进行关联,他们之前的关联处理通过DataBinding
完成。
MVVM与DataBinding的关系用一句话表述就是,MVVM是一种思想,DataBinding是谷歌推出的方便实现MVVM的工具
。
在google推出DataBinding之前,因为xml layout功能较弱,想实现MVVM非常困难。而DataBinding的出现可以让我们很方便的实现MVVM。
看起来MVVM很好的解决了MVC和MVP的不足,但是由于数据和视图的双向绑定,导致出现问题时不太好定位来源
,有可能数据问题导致,也有可能业务逻辑中对视图属性的修改导致。如果项目中打算用MVVM的话可以考虑使用官方的架构组件ViewModel、LiveData、DataBinding
去实现MVVM。
关于ViewModel、LiveData、DataBindin这些类或框架的使用,因为涉及到的内容比较多,这里不详细介绍。
如何选择
前面在介绍MVC、MVP、MVVM时并没有去详细列出他们的优缺点,主要原因是:关于架构,设计,模块化等等,它们的优缺点没有绝对的,主要看实现者如何去做
比如在mvp中我们要实现根据业务逻辑和页面逻辑做很多Present和View的具体实现,如果这些case太多,会导致代码的可读性变差。但是通过引入contract契约类,会让业务逻辑变得清晰许多。因此不管是用哪种设计模式,只要运用得当,都可以达到想要的结果。
如果非要说怎么选的话,一般的建议如下:
- 如果
项目简单
,没什么复杂性,未来改动也不大的话,那就不要用设计模式或者架构方法,只需要将每个模块封装好,方便调用即可,不要为了使用设计模式或架构方法而使用。 - 对于偏向
展示型
的app,绝大多数业务逻辑都在后端,app主要功能就是展示数据,交互等,建议使用mvvm
。 - 对于工具类或者需要写很多
业务逻辑
app,使用mvp或者mvvm都可。 - 如果想通过一个项目去学习架构和设计模式,建议用
MVC
然后在此基础上慢慢挖掘改进。最后你可能发现,改进的最终结果可能就变成了mvp,mvvm。
2019-5-9
来源:https://www.cnblogs.com/baiqiantao/p/10840064.html