<>1. 框架初识

首先讲框架之前,要问自己一个问题,为什么要用到框架?框架的使用能给自己的开发带来什么好处?


在撰写代码时,会发现随着代码越来越多,理清代码的逻辑越来越困难,且想尽可能的少些Activity。且随着迭代轮次的增多,功能也会随之增删,这时候如果没有一个好的架构,在迭代过程中程序将会被破坏,工作展开极其困难。

MVP框架又是什么?
在撰写代码时,要避免创建神类
。即避免创建无所不知,无所不能的上帝类。如果一个类需要花费时间从其他类中通过Get()和Set()检索数据(也就是说,需要深入业务并且告诉它们如何去做),所以是否应该把这些功能函数更好的组织到其它类而不是上帝类中。
Activity就是上帝类
,随着Activity代码量随时间不断增加,最后会成为一个无法重用的组件的集合。上帝类的维护成本很高,很难理解正在进行的操作,并且难以测试和扩展。

Activity中同时存在业务逻辑和UI逻辑
,不仅造成Activity臃肿,且导致数据绑定等操作变得复杂,所以我们首先要解决的一个问题就是分离关注点,而MVP架构正是个中翘楚。

MVP模式将Activity中的业务逻辑和UI逻辑分离开来,让Activity只做UI逻辑的处理,其他业务逻辑由Presenter层处理,这带来了如下优点:

* 分离了视图逻辑和业务逻辑,降低了耦合
* Activity只处理生命周期的任务,代码变得简洁
* 视图逻辑和业务逻辑分别抽象到了View和Presenter的接口中,提高代码的阅读性
* Presenter被抽象成接口,可以有多种具体的实现,所以方便进行单元测试
* 把业务逻辑抽到Presenter中去,避免后台线程引用着Activity导致Activity的资源无法被系统回收从而引起内存
泄露和OOM。
<>1.1 MVP概念

MVP代表Model,View和Presenter

*
View层:负责处理用户事件和视图部分的展示,即显示数据的地方
。得到数据后,数据传递到view层,显示数据。同时view层的点击事件等处理也在这里出现,真正的数据处理在model层中处理。
在Android中,它可能是Activity或者Fragment类。

*
Model层:
负责访问数据,即单纯的处理数据,不接手其他任务。数据可以是远端的Server API,本地数据库或者SharedPreference等。

*
Presenter层:

是连接(或适配)View和Model的桥梁。M层获得数据后交给P层,P层再交给View层。同理,View层的点击事件等处理通过P层去通知M层,让其进行数据处理。

在MVP中,View和Presenter是一一对应的(MVVM是一对多的)


MVP分离了view和model层,Presenter层充当了桥梁的角色,View层只负责更新界面即可,这里的View我们要明白只是一个viewinterface,它是视图的接口,这样我们在做单元测试的时候可以非常方便编写Presenter层代码



三层之间的调用顺序为view->presenter->model不可反向调用!不可跨级调用!
model层如何反馈给Presenter层?Presenter如何操控View层?如下图所示


底层不会直接给上一层做反馈,而是通过View,Callback为上级做出了反馈,这样就解决了请求数据与更新界面的异步操作。图中View和Callback都是以接口的形式存在。

View中定义了Activity的具体操作,主要将请求到的数据在界面中更新之类

Callback中定义了请求数据是反馈的各种状态:成功,失败,异常等


上图是我简要搭建的MVP模式下常见的目录结构,其中:

* base :存放app的基类
* common:存放异常常量,接口,公用的东西
* bean:用来存放定义的数据
* contract:连接V层和P层的一个契约包
* parser:存放一些json等字符串的解析
* presenter:P层,负责连接V层和M层,中心管理器。
* ui:这个包下面主要存放一下跟UI相关的部分
* activity:存放Activity类
* fragment:存放Fragment类
* adapter:适配器类
* utils:存放工具类
* widget:存放自定义的一些组件
<>2. 贫困版MVP模式

MVP架构的中心思想是面向接口编程,调用层使用接口对象,去调用接口方法,实现层去实现接口方法,并在调用层实例化。


在MVP架构中:我们要把MVP层的接口方法抽象出来,同时分别写出三个接口的实现类(V层一般是activity或者fragment继承view层接口),
其中V层持有P层的接口对象,P层持有V层与M层的接口对象,M层为P层提供数据。三者之间形成了MVP架构,三者之间通过P层相互连接。

这里我们先实现一个简单的MVP架构

* 先定义一个接口,在该接口中实现MVP中的所有接口: interface View { //显示数据 void showData(String str);
} interface Presenter { //通知model要获取数据并把model返回的数据交给view层 void getData(); }
interface Model { //获取数据 String doData(); }
* 创建一个类,实现P接口,这里即P层,P层持有V层和M层接口对象 public class TestPresenter implements
TestContract.Presenter { private TestContract.Model model; //持有M层接口对象 private
TestContract.View view; // 持有V层接口对象 public TestPresenter (TestContract.Model
model, TestContract.View view) { this.model = model; this.view = view; }
@Override public void getData() { view.showData(model.doData()); } }
* 创建一个类,实现M接口对象,即M层,M层为P层提供数据 public class TestModel implements TestContract.
Model { private static TestModel model = new TestModel(); public static
TestModelgetInstance() { if (model== null) { model= new TestModel (); } return
model; } @Override public String doData() { return "mvp架构"; } }
* 创建一个类,让Activity实现V接口对象,初始化P public class TestActivity extends
AppCompatActivity implements TestContract.View { private TestContract.Presenter
mPresenter; private TextView mDownload; private TextView mShowData; @Override
protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(
savedInstanceState); setContentView(R.layout.activity_main); mPresenter = new
TestPresenter(TestModel.getInstance(),this); mDownload = (TextView) findViewById
(R.id.download_data); mShowData = (TextView) findViewById(R.id.show_text);
mDownload.setOnClickListener(new View.OnClickListener() { @Override public void
onClick(View v) { mPresenter.getData(); } }); } @Override public void showData(
String str) { mShowData.setText("幺幺最爱宝宝"); } }
这里就实现了一个简易的MVP架构,三层之间的调用顺序为view->presenter->model

<>3. 温饱版MVP模式

前述的MVP模式会存在一些问题,其代码冗余量大,且存在内存泄漏的问题,
应用请求网络数据时需要等待后台反馈数据后更新界面,但是请求过程中Activity因为某种原因被销毁了,Presenter收到后台反馈并调用View接口处理UI逻辑时,因为Activity已经被销毁了,就会引发空指针异常。

解决这个方法,我们可以将P层与V层绑定起来,每次调用View前都知道宿主Activity的生命状态

* 修改P接口,增加与View解绑的接口方法: public interface TestContract { interface View {
//显示数据 void showData(String str); } interface Presenter {
//通知model要获取数据并把model返回的数据交给view层 void getData(); void detach(); //与view解绑 }
interface Model { //获取数据 String doData(); } }
* 修改P层代码,在调用View接口对象时,增加一个对对象判断处理,判断View是否被销毁 public class TestPresenter
implements TestContract.Presenter { private TestContract.Model model; private
TestContract.View view; public TestPresenter (TestContract.Model model,
TestContract.View view) { this.model = model; this.view = view; } @Override
public void getData() { if (isViewExist(view)) { view.showData(model.doData());
} } @Override public void detach() { view = null; } //判断View是否被销毁 private
boolean isViewExist(TestContract.View view) { return view != null; } }
* 最后,修改V层代码,增加Activity销毁时解绑处理 @Override protected void onDestroy() { super.
onDestroy(); mPresenter.detach(); }

这样子我们就解决了内存泄漏的问题。这种情况下的MVP模式基本上是比较完整的,但是还是不够好。应用中肯定不只一个模块,每个模块都对应着一个V层和P层,这样每个Presenter中都要定义绑定解绑方法,造成代码冗余。因此我们可以抽取一个基类来做这些事情,这就引申出了我们的小康版MVP模式

<>4. 小康版MVP模式

* 创建一个基类View,让所有View接口都必须实现,这个View可以什么都不做只是用来约束类型 public interface BaseView {
}
* 创建一个基类的Presenter,在类上规定泛型,定义绑定与解绑方法,然子类去实现。 public class BasePresenter<T
extends BaseView> { // V层的引用 protected T mView; public BasePresenter(T t) { this
.mView = t; } /** * 判断是否与View建立连接 */ public boolean isViewAttached() { return
mView!= null; } /** * 销毁View 避免P层在View销毁的时候还持有View对象 */ public void destroy() {
mView= null; } }
* 撰写契约类Contract
编写契约类,在契约类中,不会实现任何功能,这里只是写明了Presenter和View分别要实现的接口,这里我们来模拟一个数据下载情况: /** *
数据详情获取 */ public interface DownloadDataContract { interface View extends
BaseView { void hideLoading(); // 隐藏正在加载的进度框 void showData(String data); //
数据请求成功,调用此接口显示数据 void showFailureMessage(String msg); // 数据请求失败,提示失败原因 void
showErrorMessage(); // 数据请求异常,提示异常原因 } abstract class Presenter extends
BasePresenter<View> { public Presenter(View view) { super(view); } public
abstract void onSuccess(String data); // 数据请求成功 public abstract void onFailure(
String msg); // 数据请求失败 public abstract void onError(); // 数据请求错误 public abstract
void onComplete(); // 数据请求结束 } }
* 撰写P层代码,继承P的基类,实现接口方法: public class DownLoadDataPresenter extends
DownloadDataContract.Presenter { public DownLoadDataPresenter(
DownloadDataContract.View view) { super(view); } @Override public void onSuccess
(String data) { if (isViewAttached()) { mView.showData(data); } } @Override
public void onFailure(String msg) { if (isViewAttached()) { mView.
showFailureMessage(msg); } } @Override public void onError() { if (
isViewAttached()) { mView.showErrorMessage(); } } @Override public void
onComplete() { if (isViewAttached()) { mView.hideLoading(); } } }
* 撰写V层类,持有P层对象,实现UI逻辑处理: public class MainActivity extends AppCompatActivity
implements DownloadDataContract.View { private DownLoadDataPresenter
mDownloadDataPresenter; private TextView mDownloadDataTv; private TextView
mShowDataTv; @Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); setContentView(R.layout.activity_main);
mDownloadDataTv= (TextView) findViewById(R.id.download_data); mShowDataTv = (
TextView) findViewById(R.id.show_text); mDownloadDataPresenter = new
DownLoadDataPresenter(this); mDownloadDataTv.setOnClickListener(new View.
OnClickListener() { @Override public void onClick(View v) {
mDownloadDataPresenter.onSuccess("you are the best"); } }); } @Override public
void hideLoading() { } @Override public void showData(String data) { mShowDataTv
.setText(data); } @Override public void showFailureMessage(String msg) { }
@Override public void showErrorMessage() { } }
<>5. 富豪版MVP模式


一般情况下,对于大多数项目来说,小康版的MVP模式都可以很好的处理遇到的问题,且代码结构和复用性良好,当然了,这里还有一些不完美的地方可以优化,这里就可以引申出我们的富豪版MVP模式。

对于富豪版的MVP模式,主要的实现方式
,是高级抽象-使用注解,工厂模式,代理模式,策略模式整体解决代码冗余,内存泄露、Presneter生命周期以及数据存储问题。

关于顶级富豪版的MVP模式待更新。。。。。

友情链接
KaDraw流程图
API参考文档
OK工具箱
云服务器优惠
阿里云优惠券
腾讯云优惠券
华为云优惠券
站点信息
问题反馈
邮箱:ixiaoyang8@qq.com
QQ群:637538335
关注微信