最近工作上碰到了一个Android项目需要重构,因为之前的项目是MV模式,UI和业务混杂在一起,加上最初开发这个项目的程序员离职,这个项目已经处于维护十分困难的情况,最终选择重构。考虑到本项目属于业务复杂度中等的项目,而且希望新的项目具有鲜明的UI-业务-数据层次以方便维护,而MVP架构十分适合中大型项目,所以趁这个机会再次熟悉了一下MVP架构的搭建,下面说说我的做法。

我希望M、P层是不与Activity UI体系有关系的纯java代码,与AndroidUI有关的逻辑交给V层处理,出于这些考虑,我提供了了MVP三个层次的BASE抽象类(接口):

public interface BaseView {void showloading();void hideloading();
}

V层的具体实现需要实现网络取数据时的loading图的业务逻辑,当然你们有其它的业务需要所有v都实现,也可以写在BaseView里

public abstract class BasePresenter<V extends BaseView> {public V view;public void attachView(V view) {this.view = view;}public V getView() {return view;}protected boolean isAttach() {return null != view;}public void onDettach() {if (null != view) {view = null;}}abstract protected void start();
}

我希望P层和V层是一一绑定的,这样的好处在于业务逻辑会很清晰,比如我想知道某个页面有哪些业务,我不需要从ui看起,直接找这个V层绑定的P就可以找到全部的业务,如果代码比较规范或者注释比较到位,我们甚至可以直接知道各个业务的前后关系,这样维护起来即使没有接触过这个项目的人也很快可以上手。
那么在来说说我们的BasePresenter,这个抽象类主要使用泛型实现了绑定/解绑V层,注意如果Activity/Fragment已经destory而P层未解绑的话是会内存泄漏的。另外,提供了start抽象方法供V层在创建时就请求数据,典型的业务场景:首页创建时就请求首页数据来展示。

public interface BaseModel {void getRemoteData(String url, String content);void getRemoteData(String url, String content, int type);// void getLocalData();void setHttplistner(Httplistener httplistener);
}

BaseModel就十分简单啦,实现数据请求即可,setHttplistner方法设置数据请求的回调供P层使用。

下面是MVP这个三个基类的实现:
V层的主要载体是Acitivity和Fragment,因此需要实现BaseView接口,它同时也需要绑定P层,所以也需要实现BasePresenter:

public abstract class BaseActivity<P extends BasePresenter> extends AppCompatActivity implements BaseView {public P presenter;protected Context mContext;private CustomDialog dialog;@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(getLayoutId());mContext = this;presenter = getT(this, 0);if (presenter != null) {presenter.attachView(this);presenter.start();}initView();}@Overrideprotected void onDestroy() {super.onDestroy();if (Httphelper.handler != null) {Httphelper.handler.removeCallbacksAndMessages(null);}if (presenter != null) {presenter.onDettach();}}/*** 绑定activity的layout*/public abstract int getLayoutId();/*** 初始化页面的实现*/public abstract void initView();private <T> T getT(Object o, int i) {try {return ((Class<T>) ((ParameterizedType) (o.getClass().getGenericSuperclass())).getActualTypeArguments()[i]).newInstance();} catch (Exception e) {e.printStackTrace();}return null;}@Overridepublic void showloading() {if (dialog == null) {dialog = CustomDialog.instance(this);dialog.setCancelable(false);dialog.show();} else {dialog.show();}}@Overridepublic void hideloading() {if (dialog != null) {dialog.dismiss();dialog = null;}}
}
public abstract class BaseFragment<P extends BasePresenter> extends Fragment implements BaseView {protected View mRootView;public P presenter;private CustomDialog dialog;@Nullable@Overridepublic View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {mRootView = inflater.inflate(getContentViewId(), container, false);presenter = getT(this, 0);if (presenter != null) {presenter.attachView(this);presenter.start();}initAllMembersView(savedInstanceState);return mRootView;}@Overridepublic void onDetach() {super.onDetach();}@Overridepublic void onDestroy() {super.onDestroy();if (presenter != null) {presenter.onDettach();}}/*** 绑定Fragment Layout Id*/public abstract int getContentViewId();/*** 初始化Fragment UI** @param savedInstanceState*/protected abstract void initAllMembersView(Bundle savedInstanceState);private <T> T getT(Object o, int i) {try {return ((Class<T>) ((ParameterizedType) (o.getClass().getGenericSuperclass())).getActualTypeArguments()[i]).newInstance();} catch (Exception e) {e.printStackTrace();}return null;}@Overridepublic void showloading() {if (dialog == null) {dialog = CustomDialog.instance(getActivity());dialog.setCancelable(false);dialog.show();} else {dialog.show();}}@Overridepublic void hideloading() {if (dialog != null) {dialog.dismiss();dialog = null;}}
}

BaseActivity和BaseFragment要做的事情相似,利用泛型获取P层具体的实现对象,然后绑定这个对象,showloading、hideloading方法实现loading页面的展示/隐藏,再次说明,生命周期destory中要解绑presenter,否则会导致内存泄漏。

现在我们的MVP基类都准备好了,那么具体怎么使用呢,以一个项目的首页为例:
首先首页肯定需要加载首页内容,再加一个,首页需要检查更新。
那么我们的P层就需要提供这些业务:

public class HomePresenter extends BasePresenter<HomeView> {@Overrideprotected void start() {if (!isAttach()) {return;}getIndexData();getVersionInfo();}public void getIndexData() {if (!isAttach()) {return;}BaseModel model = new HomePresenterModel();model.setHttplistner(new Httplistener<HomeBean>() {@Overridepublic void onFail(int status, String message) {if (!isAttach()) {return;}view.onHttpfail(status, message);}@Overridepublic void onSuccess(HomeBean bean) {if (!isAttach()) {return;}view.onHttpsuccess(bean);}@Overridepublic void onError() {if (!isAttach()) {return;}view.onHttpError();}});String url = "XXXXXXXX";//首页数据的地址,这里是网络加载model.getRemoteData(url, "", HomePresenterModel.GETDATE);}public void getVersionInfo() {if (!isAttach()) {return;}String app_version = "x.x.x";//这里获取版本号JsonObject json = new JsonObject();json.addProperty("app_version", app_version);BaseModel model = new HomePresenterModel();model.setHttplistner(new Httplistener<GetVersionInfoBean>() {@Overridepublic void onFail(int status, String message) {if (!isAttach()) {return;}view.getVersionInfoFail(status, message);}@Overridepublic void onSuccess(GetVersionInfoBean bean) {if (!isAttach()) {return;}view.getVersionInfoSuccess(bean);}@Overridepublic void onError() {if (!isAttach()) {return;}view.getVersionInfoError();}});String url = "XXXXXXXX";//检查更新的地址,这里是网络加载model.getRemoteData(url, json.toString());}
}

说明一下,每个业务方法都需要调用一下isAttach方法来判断一下View是否绑定了Presenter,因为View层只要没有绑定,业务层就不需要对View层进行数据回调,还有另一种情况,在View层发起数据请求后随即destory,而Presenter层因为网络延迟还没有返回数据,所以等到数据回调的时候会发生因为View层已经解绑导致view对象为空的错误,所以数据回调后同样也需要用isAttach判断一下。另外,我是采用Gson进行json的解析,当然也可以用其它的工具,Gson组装了请求数据提供给Model层使用。设置了回调把数据返回给View层,所以View层应该提供这些回调方法:

public interface HomeView extends BaseView {void onHttpsuccess(HomeBean bean);void onHttpfail(int status, String message);void onHttpError();void getVersionInfoFail(int status, String message);void getVersionInfoSuccess(GetVersionInfoBean bean);void getVersionInfoError();
}

Model层的具体实现:

class HomePresenterModel implements BaseModel {public static final int GETDATE = 1;private Httplistener httplistener;@Overridepublic void getRemoteData(String url, String content) {new Httphelper(httplistener, GetVersionInfoBean.class).getAsynHttp(url, content);}@Overridepublic void getRemoteData(String url, String content, int type) {switch (type) {case GETDATE:new Httphelper(httplistener, HomeBean.class).getAsynHttp(url, content);break;}}@Overridepublic Object getLocalData() {return null;}@Overridepublic void setHttplistner(Httplistener httplistener) {this.httplistener = httplistener;}
}

model层要做的事情就是获取数据,可以是本地的也可以是网络上的,我封装了一个httphelper来处理,封装了Javabean来保存Gson解析出的数据,方便View层使用。

MVP的具体实现有了,现在要在android的ui上使用:

public class HomeActivity extends BaseActivity<HomePresenter> implements HomeView {@Overridepublic int getLayoutId() {return R.layout.xxxx;//绑定xml}@Overridepublic void initView() {//view初始化}@Overridepublic void getVersionInfoFail(int status, String message) {}@Overridepublic void getVersionInfoSuccess(GetVersionInfoBean bean) {//对返回的数据进行处理}@Overridepublic void getVersionInfoError() {}@Overridepublic void onHttpsuccess(HomeBean bean) {//请求首页数据成功后的处理}@Overridepublic void onHttpfail(int status, String message) {hideloading();new MsgAlert().show(message);}@Overridepublic void onHttpError() {hideloading();}
}

首页Activity具体实现了View接口,这样数据和UI就实现了分离,做UI的程序员可以专注于UI的实现,尤其是在开发一些非常复杂的页面的时候,可以多个程序员开发不同的层次,使得同一个复杂功能可以并行开发,大大减少开发周期。
需要说明的是,这个例子中的presenter层的业务都是在presenter的start中进行的,在实际的开发中,View层当然也可以直接调用presenter的业务方法来请求数据,这也是MVP架构所希望的:View层、Model层只和Presenter层交互,VM不直接进行交互。

Android基础架构之MVP模型相关推荐

  1. Android基础架构:Native层 Looper、Handler、Message 研究

    Android基础架构:Native层 Looper.Handler.Message 研究1,参考: https://www.cnblogs.com/roger-yu/p/15099541.html ...

  2. Android 开发架构-MVC MVP MVVM详解

    何为架构 架构,即程序的逻辑组织结构,是指导开发过程中划分程序逻辑模块的关键,好的架构要使程序达到高内聚低耦合的设计目标.例如一个人,身体的骨骼即为身体的架构,有了基本骨架之后,才可以决定在头颅里开发 ...

  3. Android基础架构

    引言 我们对android有了个大致的了解,知道如何搭建android的环境及简单地写一个HelloWorld程序,而且知道一个android项目包括哪些文件夹和文件及相应的作用.本篇将站在顶级的高度 ...

  4. Android APP架构设计——MVP的使用示例

    0. 前言 为了更好地进行移动端架构设计,我们最常用的就是MVC.MVP和MVVM,作为三个最耳熟能详的三大架构,应用可谓非常广泛.对于这三种架构设计以及优缺点已经在Android APP架构设计-- ...

  5. Android App 架构设计相关资料汇总

    1. 前言 只要有1,2年工作经验的程序员,多多少少都会接触到架构东西.可能平时工作中不一定会有机会从0到1完完全全自己去设计一套架构出来,但是如果想成为高级工程师,技术专家,架构师--尽早接触架构方 ...

  6. Android基础知识巩固系列 Android之四大组件——ContentProvider(内容提供者)

    因为最近要面试,于是打算整理整理一下Android的基础知识,由于之前本人已经学习过大概的Android基础知识,这里主要讲这四大组件.五大存储.六大布局.网络请求等这些内容,其他一些等有时间再整理, ...

  7. 机器学习的中流砥柱:用于模型构建的基础架构工具有哪些?

    本文转载自公众号"读芯术"(ID:AI_Discovery) 人工智能(AI)和机器学习(ML)已然"渗透"到了各行各业,企业们期待通过机器学习基础架构平台,以 ...

  8. Android基础入门教程——1.1 背景相关与系统架构分析

    Android基础入门教程--1.1 背景相关与系统架构分析 标签: Android基础入门教程 1.Android背景与当前的状况 Android系统是由Andy Rubin创建的,后来被Googl ...

  9. android由几层基础架构所组成

    Android一共是由4层基础架构组成 最底层是基于Linux内核实现的,它负责硬件驱动.网络管理.电源管理.系统安全.内存管理等. 第二层是由大多数开源的函数库组成,它负责为每个程序配备专有的虚拟机 ...

最新文章

  1. python写小程序-用python写个简单的小程序,编译成exe跑在win10上
  2. 拼接符 防注入正则校验_Apache Kylin 命令注入漏洞调试分析(CVE-2020-1956)
  3. 有关不蒜子访问统计无法显示的解决方法
  4. matlab矩阵信号,matlab - 如何在Matlab中使用移位版本的信号样本创建矩阵? - SO中文参考 - www.soinside.com...
  5. Hadoop有什么用
  6. Python的静态类型之旅
  7. 解决办法:/usr/bin/ld: 找不到 -lstdc++
  8. html中响应ocx事件,JS实现OCX控件的事件响应示例
  9. c语言编程串级控制,组态王-串级控制
  10. Lodop,前端自定义打印
  11. 基于Android的人脸门禁系统
  12. 人生何尝不是一盘“大富翁”呢
  13. java 中文分割_在java中只分割中文字符
  14. 新数据时代,浪潮存储如何革故鼎“新”
  15. ctf镜子里面的世界_一个小编姐姐的CTF入坑之旅
  16. Java面试:数据库,Java,框架,前端技术。应有尽有
  17. Linux文件系统及文件储存方式
  18. 姿态估计论文思路整理 -- Realtime Multi-Person 2D Pose Estimation using Part Affinity Fields
  19. HTML怎么设置自动滚动的图片,转:HTML中让图片滚动的marquee标签的使用方法
  20. 进销存系统--ERP软件常用货物计量单位汇总

热门文章

  1. 还在等机会?教你一种更清晰的Android架构!详细的Android学习指南
  2. R语言:如何画竖线,横线,添加标签,画固定长度的线段
  3. QTcentralWidget 禁止标志———没有设置布局
  4. 贝聿铭,让建筑成为永恒
  5. Android Accessibility CR 无障碍Talkback
  6. 音乐应用新宠TuneIn:应用普及率超过Spotify和Pandora
  7. python-消息推送(企业微信)
  8. 2023.3.6-3.12 AI行业周刊(第140期):AI视觉算法开发平台
  9. 初学者冒泡排序C语言
  10. 目前区块链相关的人才市场状况如何?