Android MVP架构从入门到精通-真枪实弹
Android MVP架构从入门到精通-真枪实弹
- 一. 前言
- 二. MVC架构
- 1. MVC架构优缺点
- A. 缺点
- B. 优点
- 三. MVP架构
- 1. MVP架构优缺点
- A. 缺点
- B. 优点
- 四. MVP架构实战(真枪实弹)
- 1. MVP三层代码简单书写
- 2. P层V层沟通桥梁
- 3. 生命周期适配
- 4. 优化MVP架构
- 5. 单页面多网络请求以及P层复用
- 6. 完整项目地址
- 五. 参考资料
- 六. 后续
一. 前言
你是否遇到过Activity/Fragment中成百上千行代码,完全无法维护,看着头疼?
你是否遇到过因后台接口还未写而你不能先写代码逻辑的情况?
你是否遇到过用MVC架构写的项目进行单元测试时的深深无奈?
如果你现在还是用MVC架构模式在写项目,请先转到MVP模式!
二. MVC架构
MVC架构模式最初生根于服务器端的Web开发,后来渐渐能够胜任客户端Web开发,再后来因Android项目由XML和Activity/Fragment组成,慢慢的Android开发者开始使用类似MVC的架构模式开发应用.
M层:模型层(model),主要是实体类,数据库,网络等存在的层面,model将新的数据发送到view层,用户得到数据响应.
V层:视图层(view),一般指XML为代表的视图界面.显示来源于model层的数据.用户的点击操作等事件从view层传递到controller层.
C层:控制层(controller),一般以Activity/Fragment为代表.C层主要是连接V层和M层的,C层收到V层发送过来的事件请求,从M层获取数据,展示给V层.
从上图可以看出M层和V层有连接关系,而Activity有时候既充当了控制层又充当了视图层,导致项目维护比较麻烦.
1. MVC架构优缺点
A. 缺点
M层和V层有连接关系,没有解耦,导致维护困难.
Activity/Fragment中的代码过多,难以维护.
Activity中有很多关于视图UI的显示代码,因此View视图和Activity控制器并不是完全分离的,当Activity类业务过多的时候,会变得难以管理和维护.尤其是当UI的状态数据,跟持久化的数据混杂在一起,变得极为混乱.
B. 优点
控制层和View层都在Activity中进行操作,数据操作方便.
模块职责划分明确.主要划分层M,V,C三个模块.
三. MVP架构
MVP,即是Model,View,Presenter架构模式.看起来类似MVC,其实不然.从上图能看到Model层和View层没有相连接,完全解耦.
用户触碰界面触发事件,View层把事件通知Presenter层,Presenter层通知Model层处理这个事件,Model层处理后把结果发送到Presenter层,Presenter层再通知View层,最后View层做出改变.这是一整套流程.
M层:模型层(Model),此层和MVC中的M层作用类似.
V层:视图层(View),在MVC中V层只包含XML文件,而MVP中V层包含XML,Activity和Fragment三者.理论上V层不涉及任何逻辑,只负责界面的改变,尽量把逻辑处理放到M层.
P层:通知层(Presenter),P层的主要作用就是连接V层和M层,起到一个通知传递数据的作用.
1. MVP架构优缺点
A. 缺点
MVP中接口过多.
每一个功能,相比于MVC要多写好几个文件.
如果某一个界面中需要请求多个服务器接口,这个界面文件中会实现很多的回调接口,导致代码繁杂.
如果更改了数据源和请求中参数,会导致更多的代码修改.
额外的代码复杂度及学习成本.
B. 优点
模块职责划分明显,层次清晰,接口功能清晰.
Model层和View层分离,解耦.修改View而不影响Model.
功能复用度高,方便.一个Presenter可以复用于多个View,而不用更改Presenter的逻辑.
有利于测试驱动开发,以前的Android开发是难以进行单元测试.
如果后台接口还未写好,但已知返回数据类型的情况下,完全可以写出此接口完整的功能.
四. MVP架构实战(真枪实弹)
1. MVP三层代码简单书写
接下来笔者从简到繁,一点一点的堆砌MVP的整个架构.先看一下XML布局,布局中一个Button按钮和一个TextView控件,用户点击按钮后,Presenter层通知Model层请求处理网络数据,处理后Model层把结果数据发送给Presenter层,Presenter层再通知View层,然后View层改变TextView显示的内容.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:layout_gravity="center"android:gravity="center"android:orientation="vertical"tools:context=".view.SingleInterfaceActivity"><Buttonandroid:id="@+id/button"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="点击" /><TextViewandroid:id="@+id/textView"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginTop="100px"android:text="请点击上方按钮获取数据" />
</LinearLayout>
接下来是Activity代码,里面就是获取Button和TextView控件,然后对Button做监听,先简单的这样写,一会慢慢的增加代码.
public class SingleInterfaceActivity extends AppCompatActivity {private Button button;private TextView textView;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_single_interface);button = findViewById(R.id.button);textView = findViewById(R.id.textView);button.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {}});}
}
下面是Model层代码.本次网络请求用的是wanandroid网站的开放api,其中的文章首页列表接口.SingleInterfaceModel文件里面有一个方法getData,第一个参数curPage意思是获取第几页的数据,第二个参数callback是Model层通知Presenter层的回调.
public class SingleInterfaceModel {public void getData(int curPage, final Callback callback) {NetUtils.getRetrofit().create(Api.class).getData(curPage).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(new Subscriber<ArticleListBean>() {@Overridepublic void onCompleted() {LP.w("completed");}@Overridepublic void onError(Throwable e) {callback.onFail("出现错误");}@Overridepublic void onNext(ArticleListBean bean) {if (null == bean) {callback.onFail("出现错误");} else if (bean.errorCode != 0) {callback.onFail(bean.errorMsg);} else {callback.onSuccess(bean);}}});}
}
Callback文件内容如下.里面一个成功一个失败的回调接口,参数全是泛型,为啥使用泛型笔者就不用说了吧.
public interface Callback<K, V> {void onSuccess(K data);void onFail(V data);
}
再接下来是Presenter层的代码.SingleInterfacePresenter类构造函数中直接new了一个Model层对象,用于Presenter层对Model层的调用.然后SingleInterfacePresenter类的方法getData用于与Model的互相连接.
public class SingleInterfacePresenter {private final SingleInterfaceModel singleInterfaceModel;public SingleInterfacePresenter() {this.singleInterfaceModel = new SingleInterfaceModel();}public void getData(int curPage) {singleInterfaceModel.getData(curPage, new Callback<ArticleListBean, String>() {@Overridepublic void onSuccess(ArticleListBean loginResultBean) {//如果Model层请求数据成功,则此处应执行通知View层的代码}@Overridepublic void onFail(String errorMsg) {//如果Model层请求数据失败,则此处应执行通知View层的代码}});}
}
至此,MVP三层简单的部分代码算是完成.那么怎样进行整个流程的相互调用呢.我们把刚开始的SingleInterfaceActivity代码改一下,让SingleInterfaceActivity持有Presenter层的对象,这样View层就可以调用Presenter层了.修改后代码如下.
public class SingleInterfaceActivity extends AppCompatActivity {private Button button;private TextView textView;private SingleInterfacePresenter singleInterfacePresenter;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_single_interface);button = findViewById(R.id.button);textView = findViewById(R.id.textView);singleInterfacePresenter = new SingleInterfacePresenter();button.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {singleInterfacePresenter.getData(0);}});}
}
从以上所有代码可以看出,当用户点击按钮后,View层按钮的监听事件执行调用了Presenter层对象的getData方法,此时,Presenter层对象的getData方法调用了Model层对象的getData方法,Model层对象的getData方法中执行了网络请求和逻辑处理,把成功或失败的结果通过Callback接口回调给了Presenter层,然后Presenter层再通知View层改变界面.但此时SingleInterfacePresenter类中收到Model层的结果后无法通知View层,因为SingleInterfacePresenter未持有View层的对象.如下代码的注释中有说明.(如果此时点击按钮,下方代码LP.w()处会打印出网络请求成功的log)
public class SingleInterfacePresenter {private final SingleInterfaceModel singleInterfaceModel;public SingleInterfacePresenter() {this.singleInterfaceModel = new SingleInterfaceModel();}public void getData(int curPage) {singleInterfaceModel.getData(curPage, new Callback<ArticleListBean, String>() {@Overridepublic void onSuccess(ArticleListBean loginResultBean) {//如果Model层请求数据成功,则此处应执行通知View层的代码//LP.w()是一个简单的log打印LP.w(loginResultBean.toString());}@Overridepublic void onFail(String errorMsg) {//如果Model层请求数据失败,则此处应执行通知View层的代码}});}
}
代码写到这里,笔者先把这些代码提交到github(https://github.com/serge66/MVPDemo),github上会有一次提交记录,如果想看此时的代码,可以根据提交记录"第一次修改"克隆此时的代码.
2. P层V层沟通桥梁
现在P层未持有V层对象,不能通知V层改变界面,那么就继续演变MVP架构.
在MVP架构中,我们要为每个Activity/Fragment写一个接口,这个接口需要让Presenter层持有,P层通过这个接口去通知V层更改界面.接口中包含了成功和失败的回调,这个接口Activity/Fragment要去实现,最终P层才能通知V层.
public interface SingleInterfaceIView {void showArticleSuccess(ArticleListBean bean);void showArticleFail(String errorMsg);
}
一个完整的项目以后肯定会有许多功能界面,那么我们应该抽出一个IView公共接口,让所有的Activity/Fragment都间接实现它.IVew公共接口是用于给View层的接口继承的,注意,不是View本身继承.因为它定义的是接口的规范, 而其他接口才是定义的类的规范(这句话请仔细理解).
/*** @Description: 公共接口 是用于给View的接口继承的,注意,不是View本身继承。* 因为它定义的是接口的规范, 而其他接口才是定义的类的规范* @Author: lishengjiejob@163.com* @Time: 2018/11/22 17:26*/
public interface IView {
}
这个接口中可以写一些所有Activigy/Fragment共用的方法,我们把SingleInterfaceIView继承IView接口.
public interface SingleInterfaceIView extends IView {void showArticleSuccess(ArticleListBean bean);void showArticleFail(String errorMsg);
}
同理Model层和Presenter层也是如此.
public interface IModel {
}
public interface IPresenter {
}
现在项目中Model层是一个SingleInterfaceModel类,这个类对象被P层持有,对于面向对象设计来讲,利用接口达到解耦目的已经人尽皆知,那我们就要对SingleInterfaceModel类再写一个可继承的接口.代码如下.
public interface ISingleInterfaceModel extends IModel {void getData(int curPage, final Callback callback);
}
如此,SingleInterfaceModel类的修改如下.
public class SingleInterfaceModel implements ISingleInterfaceModel {@Overridepublic void getData(int curPage, final Callback callback) {NetUtils.getRetrofit().create(Api.class).getData(curPage).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(new Subscriber<ArticleListBean>() {@Overridepublic void onCompleted() {LP.w("completed");}@Overridepublic void onError(Throwable e) {callback.onFail("出现错误");}@Overridepublic void onNext(ArticleListBean bean) {if (null == bean) {callback.onFail("出现错误");} else if (bean.errorCode != 0) {callback.onFail(bean.errorMsg);} else {callback.onSuccess(bean);}}});}
}
同理,View层持有P层对象,我们也需要对P层进行改造.但是下面的代码却没有像ISingleInterfaceModel接口继承IModel一样继承IPresenter,这点需要注意,笔者把IPresenter的继承放在了其他处,后面会讲解.
public interface ISingleInterfacePresenter {void getData(int curPage);
}
然后SingleInterfacePresenter类的修改如下:
public class SingleInterfacePresenter implements ISingleInterfacePresenter {private final ISingleInterfaceModel singleInterfaceModel;public SingleInterfacePresenter() {this.singleInterfaceModel = new SingleInterfaceModel();}@Overridepublic void getData(int curPage) {singleInterfaceModel.getData(curPage, new Callback<ArticleListBean, String>() {@Overridepublic void onSuccess(ArticleListBean loginResultBean) {//如果Model层请求数据成功,则此处应执行通知View层的代码//LP.w()是一个简单的log打印LP.w(loginResultBean.toString());}@Overridepublic void onFail(String errorMsg) {//如果Model层请求数据失败,则此处应执行通知View层的代码LP.w(errorMsg);}});}
}
3. 生命周期适配
至此,MVP三层每层的接口都写好了.但是P层连接V层的桥梁还没有搭建好,这个慢慢来,一个好的高楼大厦都是一步一步建造的.上面IPresenter接口我们没有让其他类继承,接下来就讲下这个.P层和V层相连接,V层的生命周期也要适配到P层,P层的每个功能都要适配生命周期,这里可以把生命周期的适配放在IPresenter接口中.P层持有V层对象,这里把它放到泛型中.代码如下.
public interface IPresenter<T extends IView> {/*** 依附生命view** @param view*/void attachView(T view);/*** 分离View*/void detachView();/*** 判断View是否已经销毁** @return*/boolean isViewAttached();}
这个IPresenter接口需要所有的P层实现类继承,对于生命周期这部分功能都是通用的,那么就可以抽出来一个抽象基类BasePresenter,去实现IPresenter的接口.
public abstract class BasePresenter<T extends IView> implements IPresenter<T> {protected T mView;@Overridepublic void attachView(T view) {mView = view;}@Overridepublic void detachView() {mView = null;}@Overridepublic boolean isViewAttached() {return mView != null;}
}
此时,SingleInterfacePresenter类的代码修改如下.泛型中的SingleInterfaceIView可以理解成对应的Activity,P层此时完成了对V层的通信.
public class SingleInterfacePresenter extends BasePresenter<SingleInterfaceIView> implements ISingleInterfacePresenter {private final ISingleInterfaceModel singleInterfaceModel;public SingleInterfacePresenter() {this.singleInterfaceModel = new SingleInterfaceModel();}@Overridepublic void getData(int curPage) {singleInterfaceModel.getData(curPage, new Callback<ArticleListBean, String>() {@Overridepublic void onSuccess(ArticleListBean loginResultBean) {//如果Model层请求数据成功,则此处应执行通知View层的代码//LP.w()是一个简单的log打印LP.w(loginResultBean.toString());if (isViewAttached()) {mView.showArticleSuccess(loginResultBean);}}@Overridepublic void onFail(String errorMsg) {//如果Model层请求数据失败,则此处应执行通知View层的代码LP.w(errorMsg);if (isViewAttached()) {mView.showArticleFail(errorMsg);}}});}
}
此时,P层和V层的连接桥梁已经搭建,但还未搭建完成,我们需要写个BaseMVPActvity让所有的Activity继承,统一处理Activity相同逻辑.在BaseMVPActvity中使用IPresenter的泛型,因为每个Activity中需要持有P层对象,这里把P层对象抽出来也放在BaseMVPActvity中.同时BaseMVPActvity中也需要继承IView,用于P层对V层的生命周期中.代码如下.
public abstract class BaseMVPActivity<T extends IPresenter> extends AppCompatActivity implements IView {protected T mPresenter;@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);initPresenter();init();}protected void initPresenter() {mPresenter = createPresenter();//绑定生命周期if (mPresenter != null) {mPresenter.attachView(this);}}@Overrideprotected void onDestroy() {if (mPresenter != null) {mPresenter.detachView();}super.onDestroy();}/*** 创建一个Presenter** @return*/protected abstract T createPresenter();protected abstract void init();}
接下来让SingleInterfaceActivity实现这个BaseMVPActivity.
public class SingleInterfaceActivity extends BaseMVPActivity<SingleInterfacePresenter> implements SingleInterfaceIView {private Button button;private TextView textView;@Overrideprotected void init() {setContentView(R.layout.activity_single_interface);button = findViewById(R.id.button);textView = findViewById(R.id.textView);button.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {mPresenter.getData(0);}});}@Overrideprotected SingleInterfacePresenter createPresenter() {return new SingleInterfacePresenter();}@Overridepublic void showArticleSuccess(ArticleListBean bean) {textView.setText(bean.data.datas.get(0).title);}@Overridepublic void showArticleFail(String errorMsg) {Toast.makeText(this, errorMsg, Toast.LENGTH_SHORT).show();}
}
到此,MVP架构的整个简易流程完成.
代码写到这里,笔者先把这些代码提交到github(https://github.com/serge66/MVPDemo),github上会有一次提交记录,如果想看此时的代码,可以根据提交记录"第二次修改"克隆此时的代码.
4. 优化MVP架构
上面是MVP的目录,从目录中我们可以看到一个功能点(网络请求)MVP三层各有两个文件需要写,相对于MVC来说写起来确实麻烦,这也是一些人不愿意写MVP,宁愿用MVC的原因.
这里我们可以对此优化一下.MVP架构中有个Contract的概念,Contract有统一管理接口的作用,目的是为了统一管理一个页面的View和Presenter接口,用Contract可以减少部分文件的创建,比如P层和V层的接口文件.
那我们就把P层的ISingleInterfacePresenter接口和V层的SingleInterfaceIView接口文件删除掉,放入SingleInterfaceContract文件中.代码如下.
public interface SingleInterfaceContract {interface View extends IView {void showArticleSuccess(ArticleListBean bean);void showArticleFail(String errorMsg);}interface Presenter {void getData(int curPage);}}
此时,SingleInterfacePresenter和SingleInterfaceActivity的代码修改如下.
public class SingleInterfacePresenter extends BasePresenter<SingleInterfaceContract.View>implements SingleInterfaceContract.Presenter {private final ISingleInterfaceModel singleInterfaceModel;public SingleInterfacePresenter() {this.singleInterfaceModel = new SingleInterfaceModel();}@Overridepublic void getData(int curPage) {singleInterfaceModel.getData(curPage, new Callback<ArticleListBean, String>() {@Overridepublic void onSuccess(ArticleListBean loginResultBean) {//如果Model层请求数据成功,则此处应执行通知View层的代码//LP.w()是一个简单的log打印LP.w(loginResultBean.toString());if (isViewAttached()) {mView.showArticleSuccess(loginResultBean);}}@Overridepublic void onFail(String errorMsg) {//如果Model层请求数据失败,则此处应执行通知View层的代码LP.w(errorMsg);if (isViewAttached()) {mView.showArticleFail(errorMsg);}}});}
}
public class SingleInterfaceActivity extends BaseMVPActivity<SingleInterfacePresenter>implements SingleInterfaceContract.View {private Button button;private TextView textView;@Overrideprotected void init() {setContentView(R.layout.activity_single_interface);button = findViewById(R.id.button);textView = findViewById(R.id.textView);button.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {mPresenter.getData(0);}});}@Overrideprotected SingleInterfacePresenter createPresenter() {return new SingleInterfacePresenter();}@Overridepublic void showArticleSuccess(ArticleListBean bean) {textView.setText(bean.data.datas.get(0).title);}@Overridepublic void showArticleFail(String errorMsg) {Toast.makeText(this, errorMsg, Toast.LENGTH_SHORT).show();}
}
代码写到这里,笔者先把这些代码提交到github(https://github.com/serge66/MVPDemo),github上会有一次提交记录,如果想看此时的代码,可以根据提交记录"第三次修改"克隆此时的代码.
5. 单页面多网络请求以及P层复用
上面的MVP封装只适用于单页面一个网络请求的情况,当一个界面有两个网络请求时,此封装已不适合.以及考虑到P层复用.为此,我们再次新建一个MultipleInterfaceActivity来进行说明.XML中布局是两个按钮两个Textview,点击则可以进行网络请求.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:layout_gravity="center"android:gravity="center"android:orientation="vertical"tools:context=".view.MultipleInterfaceActivity"><Buttonandroid:id="@+id/button"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="点击" /><TextViewandroid:id="@+id/textView"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginTop="50px"android:text="请点击上方按钮获取数据" /><Buttonandroid:id="@+id/btn"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginTop="100px"android:text="点击" /><TextViewandroid:id="@+id/tv"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginTop="50px"android:text="请点击上方按钮获取数据" />
</LinearLayout>
MultipleInterfaceActivity类代码暂时如下.
public class MultipleInterfaceActivity extends BaseMVPActivity {private Button button;private TextView textView;private Button btn;private TextView tv;@Overrideprotected void init() {setContentView(R.layout.activity_multiple_interface);button = findViewById(R.id.button);textView = findViewById(R.id.textView);button.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {}});btn = findViewById(R.id.btn);tv = findViewById(R.id.tv);btn.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {}});}@Overrideprotected IPresenter createPresenter() {return null;}}
此时我们可以想下,当一个页面中有多个网络请求时,Activity所继承的BaseMVPActivity的泛型中要写多个参数,那有没有上面代码的框架不变的情况下实现这个需求呢?答案必须有的.我们可以把多个网络请求的功能当做一个网络请求来看待,封装成一个MultiplePresenter,其继承至BasePresenter实现生命周期的适配.此MultiplePresenter类的作用就是容纳多个Presenter,连接同一个View.代码如下.
public class MultiplePresenter<T extends IView> extends BasePresenter<T> {private T mView;private List<IPresenter> presenters = new ArrayList<>();@SafeVarargspublic final <K extends IPresenter<T>> void addPresenter(K... addPresenter) {for (K ap : addPresenter) {ap.attachView(mView);presenters.add(ap);}}public MultiplePresenter(T mView) {this.mView = mView;}@Overridepublic void detachView() {for (IPresenter presenter : presenters) {presenter.detachView();}}}
因MultiplePresenter类中需要有多个网络请求,现在举例说明时,暂时用两个网络请求接口.MultipleInterfaceActivity类中代码改造如下.
public class MultipleInterfaceActivity extends BaseMVPActivity<MultiplePresenter>implements SingleInterfaceContract.View, MultipleInterfaceContract.View {private Button button;private TextView textView;private Button btn;private TextView tv;private SingleInterfacePresenter singleInterfacePresenter;private MultipleInterfacePresenter multipleInterfacePresenter;@Overrideprotected void init() {setContentView(R.layout.activity_multiple_interface);button = findViewById(R.id.button);textView = findViewById(R.id.textView);button.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {singleInterfacePresenter.getData(0);}});btn = findViewById(R.id.btn);tv = findViewById(R.id.tv);btn.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {multipleInterfacePresenter.getBanner();}});}@Overrideprotected MultiplePresenter createPresenter() {MultiplePresenter multiplePresenter = new MultiplePresenter(this);singleInterfacePresenter = new SingleInterfacePresenter();multipleInterfacePresenter = new MultipleInterfacePresenter();multiplePresenter.addPresenter(singleInterfacePresenter);multiplePresenter.addPresenter(multipleInterfacePresenter);return multiplePresenter;}@Overridepublic void showArticleSuccess(ArticleListBean bean) {textView.setText(bean.data.datas.get(0).title);}@Overridepublic void showArticleFail(String errorMsg) {Toast.makeText(this, errorMsg, Toast.LENGTH_SHORT).show();}@Overridepublic void showMultipleSuccess(BannerBean bean) {tv.setText(bean.data.get(0).title);}@Overridepublic void showMultipleFail(String errorMsg) {Toast.makeText(this, errorMsg, Toast.LENGTH_SHORT).show();}
}
写到这里,MVP框架基本算是完成.如果想再次优化,其实还是有可优化的地方,比如当View销毁时,现在只是让P层中的View对象置为null,并没有继续对M层通知.如果View销毁时,M层还在请求网络中呢,可以为此再加入一个取消网络请求的通用功能.这里只是举一个例子,每个人对MVP的理解不一样,而MVP架构也并不是一成不变,适合自己项目的才是最好的.
6. 完整项目地址
完整项目已提交到github(https://github.com/serge66/MVPDemo),若需要敬请查看.
五. 参考资料
一步步带你精通MVP
从0到1搭建MVP框架
Presenter层如何高度的复用
六. 后续
<<MVVM架构从入门到精通-真枪实弹>> 敬请期待~~~
原创文章,来自于Vitamio(http://blog.csdn.net/vitamio),转载请注明出处。
Android MVP架构从入门到精通-真枪实弹相关推荐
- mvp关联activity生命周期_Android MVP架构从入门到精通-真枪实弹
Android MVP架构从入门到精通-真枪实弹 一. 前言 你是否遇到过Activity/Fragment中成百上千行代码,完全无法维护,看着头疼? 你是否遇到过因后台接口还未写而你不能先写代码逻辑 ...
- [Android] Android MVP 架构下 最简单的 代码实现
Android MVP 架构下 最简单的 代码实现 首先看图: 上图是MVP,下图是MVC MVP和MVC的区别,在于以前的View层不仅要和model层交互,还要和controller层交互.而 ...
- java架构师入门教程,java技术架构师入门到精通高薪就业教程百度云下载
java技术架构师入门到精通高薪就业视频教程百度云 课程目录: JAVA架构课开班典礼 JVM性能调优专题 JVM整体结构深度解析 JVM内存分配机制详解(此视频作废) JVM字节码文件结构深度剖析 ...
- 微服务架构从入门到精通(一)微服务介绍
本系列文章包括微服务介绍.微服务架构.DevOps.APM等方面,尽量抓重点.不罗嗦,讲解微服务整个生态圈的技术性知识.期望各位同仁能快速的对微服务架构有个了解,加入到微服务最佳实践中来. 一.架构的 ...
- 微服务架构从入门到精通(二)微服务生态体系
本篇承接上一篇文章<微服务架构从入门到精通(一)微服务介绍>讲起,主要是介绍微服务架构的技术生态体系,让大家对微服务架构整个生态圈有个大体的了解.10来多年的从业经验来看,学习技术一般都是 ...
- Android基础教程——从入门到精通(上)
本文是对B站教程 动脑学院 Android教程 学习过程中所做的笔记. 文章分为上下两部分,此文是上部分,下部分链接为:Android基础教程--从入门到精通(下) 源视频教程并没有录制全,本文还补充 ...
- Android MVP架构
Android 架构的理解昨天看了Android框架模式(1)-MVP入门,感觉理解起来有点困难,我就重新把java回调重新理解了一遍,并写了一篇博客,有需要的朋友可以看一看,java回调机制,下面我 ...
- 谈谈 Android MVP 架构 | 掘金技术征文
前言:本文所写的是博主的个人见解,如有错误或者不恰当之处,欢迎私信博主,加以改正!原文链接,demo链接 MVP 架构简介 说起 MVP 架构,相信很多朋友都看过,网上也有很多这方面的资料.博主使用 ...
- android mvp框架基类,Android MVP架构项目搭建封装,基类封装
综述 对于MVP (Model View Presenter)架构是从著名的MVC(Model View Controller)架构演变而来的.而对于Android应用的开发中本身可视为一种MVC架构 ...
最新文章
- 图像的矩,以及利用矩求图像的重心,方向
- matlab中怎么给符号变量赋值
- 静态call 动态call LINK
- BluePrism初尝2
- echart旭日图_海报级设计感的旭日图,就在 ECharts 4.0
- 用单片机测量流体流速的_沟渠流量测量系统宝山哪家质量好广州顺仪品牌
- sql 联合_SQL联合,SQL联合全部
- 主要由javascript实现的网页打字小游戏
- 解决三星PM981硬盘无法正常安装黑苹果的问题(第一版)
- eclipse插件下载地址
- 武汉知名 IT 公司大盘点
- 使用scrapy爬取前程无忧51job网站
- SupeSite模板中的代码代表什么意思
- HashMap的底层原理你真的知道?
- 13-反向传播法求梯度
- css-超出内容省略号
- 为何国外的人都爱用电子邮箱?注册电子邮箱有哪些好处呢
- Ceph: ceph基础知识
- linux系统终端用户名和密码忘记了,主机大师(Linux)登录账户密码忘记的解决办法...
- quick-cocos2d-x可调试开发环境搭建(vs+babelua和vscode+luaide)
热门文章
- linux通过configfs方式开启uvc、uac、adb等功能
- Python推荐系统学习笔记(1)基于协同过滤的个性化推荐算法实战---隐语义模型
- 区块链技术将加速人类共享精神的进步
- 胡克定律(Hooke's law)
- CocoaPods原理
- CSS入门保姆级知识整理!!看到就是赚到!(1)
- jQuery学习:scroll滚动 垂直滚动scrollTop 水平滚动scrollLeft
- linux如何分区ssd和hdd,Linux下判断磁盘是SSD还是HDD的3种方法
- MATLAB 最短路
- 练习:有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,假如兔子都不死,问每个月的兔子总数为多少