最近看了两篇关于Android实现MVP的文章一种在android中实现MVP模式的新思路和用MVP架构开发Android应用。
两篇文章的思路都是一样的,即把Activity、Fragment作为Presenter,这种方式不同于现在主流的MVP方式,不过它很好的解决了Activity生命周期带来的问题,而且我认为它让MVP的实现更加轻松了。

那么问题来了,这么好的思路,我们怎么可以不去实现一下自己的MVP呢? 于是我花了一晚上的时间整出了一个小小的MVP框架——MVPro

MVPro介绍

MVPro的实现很简单,思想和上面两篇文章介绍的一样,都是将Activity和Fragment作为Presenter,首先一张图来解释一下MVPro的原理,

Created with Raphaël 2.1.0PresenterPresenterViewViewonCreatecreatebindPresentercreatecreatedsetContentViewbindEventcreated

Presenter即我们的Activity或者Fragment, View呢?说白了就是我们从Activity和Fragment中提取出来的和View操作相关的代码,思路很简单也很清晰,下面我们就以一个简单的demo详细学习一下MVPro的使用吧。

MVPro使用

因为MVPro是将Activity视为Presenter,所以我们代码的主线应该是Presenter了,首先上一个Presenter的代码,

public class MainActivity extends ActivityPresenterImpl<DataListView>implements AdapterView.OnItemClickListener, View.OnClickListener {@Overridepublic void create(Bundle savedInstance) {}@Overridepublic void created(Bundle savedInstance) {}@Overridepublic void onItemClick(AdapterView<?> parent, View view, int position, long id) {mView.toast(position);}@Overridepublic void onClick(View v) {if(v.getId() == R.id.newdata) newData();else getData();}private void newData() {new MainBiz().data(new MainBiz.Callback<ArrayList<String>>() {@Overridepublic void callback(ArrayList<String> data) {mView.setData(data);}});}private void getData() {new MainBiz().data(new MainBiz.Callback<ArrayList<String>>() {@Overridepublic void callback(ArrayList<String> data) {mView.addData(data);}});}
}

MainActivity继承自ActivityPresenterImpl类,而且在代码中并没有任何和Activity生命周期相关的代码,两个方法create和created是我们唯一关心的重点,但是也是非必须重写的,这两个方法都是Presenter提供的方法,他们两个的区别就是create在setContentView之前调用,而created是在setContentView之后调用。
剩下的几个方法我们可以猜测到都是从View层发起的,那么接下来我们就来看看View层该如何去写。从MainActivity实现的泛型中我们可以看出,这里我们对应的View层代码在DataListView中。

/*** Created by qibin on 2015/11/15.*/
public class DataListView extends ViewImpl {private Button mButton1, mButton2;private ListView mListView;private ArrayAdapter<String> mAdapter;@Overridepublic void created() {mButton1 = findViewById(R.id.newdata);mButton2 = findViewById(R.id.adddata);mListView = findViewById(R.id.list);}@Overridepublic void bindEvent() {EventHelper.click(mPresenter, mButton1, mButton2);EventHelper.itemClick(mPresenter, mListView);}@Overridepublic int getLayoutId() {return R.layout.list_layout;}public void setData(ArrayList<String> datas) {mAdapter = new ArrayAdapter<String>(mRootView.getContext(),android.R.layout.simple_list_item_1, datas);mListView.setAdapter(mAdapter);}public void addData(ArrayList<String> datas) {mAdapter.addAll(datas);}public void toast(int position) {Toast.makeText(mRootView.getContext(),mAdapter.getItem(position), Toast.LENGTH_SHORT).show();}
}

在View层中,我们并不关心布局文件是怎么设置到Activity上的,我们仅仅在getLayoutId中返回我们要使用的布局文件,和created中去获取我们需要的控件即可,
不过我们还重写一个bindEvent方法,在这个方法中我们为控件绑定了事件,这里需要注意一点就是MVPro希望各种事件都在Presenter上implements,因为EventHelper
提供了基于Presenter的事件监听。

ok, MVPro的使用就这么简单,不过相信很多人还是摸不着头脑,所以下面我们将会深入到源码中,来了解一下MVPro的实现流程。

MVPro源码

首先我们还是要从Presenter入手,Presenter的源头是一个接口,这里面定义了Presenter的必须需要的几个方法,

/*** Presenter的根接口<br />* Created by qibin on 2015/11/15.*/
public interface IPresenter<T> {/*** 获取当前presenter泛型的类型* @return*/Class<T> getViewClass();/*** View初始化之前可以在此方法做一些操作*/void create(Bundle savedInstance);/*** View初始化完毕后调用*/void created(Bundle savedInstance);
}

只有三个方法,其中getViewClass是获取我们对应的View层那个类的class, 例如上面的demo中对应的就是DataListView,create和created是两个扩展方法分别在onCreate的setContentView之前和之后调用。接下来,我们就来看看我们上面MainActivity继承的那个ActivityPresenterImpl的代码,

/*** 将Activity作为Presenter的基类 <br />* Created by qibin on 2015/11/15.*/
public class ActivityPresenterImpl<T extends IView> extends Activity implements IPresenter<T> {protected T mView;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);create(savedInstanceState);try {mView = getViewClass().newInstance();mView.bindPresenter(this);setContentView(mView.create(getLayoutInflater(), null));mView.bindEvent();created(savedInstanceState);} catch(Exception e) {throw new RuntimeException(e.getMessage());}}@Overridepublic Class<T> getViewClass() {return GenericHelper.getViewClass(getClass());}@Overridepublic void create(Bundle savedInstance) {}@Overridepublic void created(Bundle savedInstance) {}
}

我估计很多人在没看到ActivityPresenterImpl源码之前都会认为它应该很复杂,不过在看后大概都会忍不住吐槽一句:尼玛,这么少! 对代码确实少,而且基本都集中在了onCreate中,不过,在看onCreate之前,我们首先来看看getViewClass方法, 这个方法实现自IPresenter,而且仅仅有一行代码,它的作用就是获取当前Presenter泛型指定那个View层类,对应上面的demo中就是DataListView了。

接下来回到onCreate中,在onCreate中,我们首先调用了create方法,以便于我们执行一些在setContentView之前的代码,例如设置无标题啥的。
然后通过getViewClass获取到了View层的类,并且利用反射得到他的对象mView,接下的几步都和这个mView相关。
调用mView.bindPresenter方法,将当前Presenter关联到当前View层上。
调用mView.create方法生成我们布局文件对应的View对象,并且通过setContentView设置布局文件。
调用mView.bindEvent方法,在这个方法中我们可以对一些控件设置事件监听。
最后我们调用了created方法,以便在开发中书写初始化控件完毕后的代码。

ok, Presenter的代码很简单,主要是一些流程性的东西,解析来我们来看看View层的代码是怎么实现的。还是从接口——IView开始,

/*** View层的根接口 <br />* Created by qibin on 2015/11/15.*/
public interface IView {/*** 根据 {@link getLayoutId}方法生成生成setContentView需要的根布局* @param inflater* @param container* @return*/View create(LayoutInflater inflater, ViewGroup container);/*** 当Activity的onCreate完毕后调用*/void created();/*** 返回当前视图需要的layout的id* @return*/int getLayoutId();/*** 根据id获取view* @param id* @param <V>* @return*/<V extends View> V findViewById(int id);/*** 绑定Presenter* @param presenter*/void bindPresenter(IPresenter presenter);/*** {@link created}后调用,可以调用{@link org.loader.helper.EventHelper.click}* 等方法为控件设置点击事件,一般推荐使用{@link org.loader.helper.EventHelper.click(IPresenter presenter, View ...views)}* 方法并且让你的Presenter实现相应接口。*/void bindEvent();
}

IView接口中定义的方法就相对多了一些,我们一个个的来说一下这些方法存在的理由。
create方法根据提供的layout的id来生成布局对象,上面Presenter的setContentView的参数就是他的返回值。
created会在inflater布局完成后调用,以便我们一些操作。
getLayoutId需要我们去实现,返回需要的布局文件的id。
findViewById提供了一个泛型的查找控件方法,有了它我们再也不用类型转换了。
bindPresenter绑定对应的Presenter。
bindEvent我们需要实现这个方法来为控件设置监听。

ok, 在介绍完这些方法后,我们就来看看我们View层的基类都是做了什么工作。

/*** View层的基类* Created by qibin on 2015/11/15.*/
public abstract class ViewImpl implements IView {/*** create方法生成的view*/protected View mRootView;/*** 绑定的presenter*/protected IPresenter mPresenter;@Overridepublic View create(LayoutInflater inflater, ViewGroup container) {mRootView = inflater.inflate(getLayoutId(), container, false);created();return mRootView;}@Overridepublic <V extends View> V findViewById(int id) {return (V) mRootView.findViewById(id);}@Overridepublic void created() {}@Overridepublic void bindPresenter(IPresenter presenter) {mPresenter = presenter;}@Overridepublic void bindEvent() {}
}

在create方法中,我们使用LayoutInflater生成了View对象,并且返回给Presenter用来setContentView,在返回之前我们还调用了created方法,在项目中我们可以在这个方法中查找我们需要的控件。
created和bindEvent方法在这里都是空实现,这样做的目的就是在我们自己的代码中不需要任何时候都要实现这两个方法。

ok, 很简单,MVPro的代码大体就这些,整个流程我们也分析完了,那Model层呢?还没有看到关于Model层的代码呢!在MVPro中并不关心你业务层是怎么实现,你完全可以使用你现在正在使用的业务层代码的架构,而不会对MVP产生任何影响。

最后,我们还是要来看看EventHelper是怎么设置事件监听的。

public class EventHelper {public static void click(IPresenter li, View ...views) {if(!(li instanceof View.OnClickListener)) return;click((View.OnClickListener) li, views);}public static void click(View.OnClickListener li, View ...views) {if(views == null || views.length == 0) return;for(View v : views) v.setOnClickListener(li);}...
}

这里拿click事件为例,可以看到我们的参数是一个IPresenter类型, 而且是判断了你传递的这个Presenter是不是实现了View.OnClickListener接口,如果实现了,就进行强制类型转换使用,从这里我们也可以看到,为了尽量减少我们的Presenter和View层的代码耦合,MVPro并没有使用泛型,也不建议从Presenter传递OnClickListener对象,而是建议我们的Presenter直接实现OnClickListener接口,这样做,EventHelper会自动帮我们完成类型的检查和监听的设置。

恩,到这里,我们还有一个helper没有看实现,就是那个获取泛型类型的帮助类。

/*** Created by qibin on 2015/11/15.*/
public class GenericHelper {public static <T> Class<T> getViewClass(Class<?> klass) {Type type = klass.getGenericSuperclass();if(type == null || !(type instanceof ParameterizedType)) return null;ParameterizedType parameterizedType = (ParameterizedType) type;Type[] types = parameterizedType.getActualTypeArguments();if(types == null || types.length == 0) return null;return (Class<T>) types[0];}
}

这个类中仅仅一个方法,不过可能大部分人对这里面的代码非常陌生,这里做的事很直接,就是根据我们提供的class,来获取这个类实现的那个泛型的类型,打个比方,下面的代码获取到的就是java.lang.String,

class Child extends Super<String> {}

ok, 文章到这里就要结束了,MVPro的总体实现还是非常简单的,如果大家有什么疑问或者感觉MVPro需要什么改进的地方,大家可以在下面为我留言,我会不断去完善这个小框架的。

最后是MVPro的下载地址,这里提供了MVPro的源码(AS版)、jar包、实例代码,https://github.com/qibin0506/MVPro

Android MVP框架MVPro的使用和源码分析相关推荐

  1. android mvp 代码范例,Android MVP开发模式有案例和源码,反正我能看懂的MVP

    丁先森 博客园 MVP 理论知识 在MVP 架构中跟MVC类似的是同样也分为三层. Activity 和Fragment 视为View层,负责处理 UI. Presenter 为业务处理层,既能调用U ...

  2. Android解耦库EventBus的使用和源码分析

    github:https://github.com/greenrobot/EventBus 官方使用文档:https://github.com/greenrobot/EventBus/blob/mas ...

  3. java校验框架源码解析_Spring Boot原理剖析和源码分析

    Spring Boot原理剖析和源码分析 依赖管理 问题一:为什么导入dependency时不需要指定版本? spring-boot-starter-parent依赖 org.springframew ...

  4. android lottie字体json,从json文件到炫酷动画-Lottie实现思路和源码分析

    从json文件到炫酷动画-Lottie实现思路和源码分析,Lottie是最近Airbnb开源的动画项目,支持Android.iOS.ReactNaitve三个平台,本文分析主要Lottie把json文 ...

  5. Android RxJava(一) create操作符的用法和源码分析

    RxJava(一) create操作符的用法和源码分析 转载于:https://www.cnblogs.com/zhujiabin/p/7291901.html

  6. Google Mock(Gmock)简单使用和源码分析——源码分析

    源码分析 通过<Google Mock(Gmock)简单使用和源码分析--简单使用>中的例子,我们发现被mock的相关方法在mock类中已经被重新实现了,否则它们也不会按照我们的期待的行为 ...

  7. 【Android 启动过程】Activity 启动源码分析 ( AMS -> ActivityThread、AMS 线程阶段 二 )

    文章目录 前言 一.热启动与冷启动选择 二.AMS 进程中执行的相关操作 三.通过 Binder 机制转到 ActivityThread 中执行的操作 总结 前言 上一篇博客 [Android 启动过 ...

  8. Android-FixBug热修复框架的使用及源码分析(不发版修复bug)

    前面几篇博文已经介绍了2种热修复框架的使用及源码分析,AndFix兼容性比较好,而Dexposed Art处于Beta版. AndFix和Dexposed都是阿里的开源项目.  Alibaba-And ...

  9. Nacos高级特性Raft算法以及原理和源码分析

    Nacos高级特性Raft算法以及原理和源码分析 对比springcloud-config配置中心 springcloud-config工作原理 Nacos的工作原理图 springcloud-con ...

最新文章

  1. tf.variable和tf.get_Variable以及tf.name_scope和tf.variable_scope的区别
  2. 如何确定一个IAR工程所使用的IAR版本
  3. Mysql5.X重点难点速记
  4. 二值图像连通 C语言,二值图像统计连通区域C语言版
  5. sizeof你真的弄明白了吗?来看看这个例子
  6. c语言 炸弹文件,炸弹超人游戏c语言简板
  7. mp4 视频在网页上播放不了
  8. 年后跳槽 BAT 必看,10 种干货帮你 Offer 拿到手软!
  9. springboot和springcloud及常用注解积累
  10. 递归算法1加到100_「算法」北京大学算法基础—递归(1)
  11. 如何理解UCB-Upper Confidence Bound
  12. Landsat9卫星简介
  13. 谷歌浏览器屏蔽广告插件
  14. [ZT]Inside WINDOWS NT Object Manager
  15. EF(Entity Framework、EF Core)
  16. PiaolinPlatformV2.0.0 - 获取手机或电脑GPS位置信息(定位平台)
  17. 【物联网】LoRa vs NBIoT
  18. Python爬虫---爬取数据(上)
  19. Docker部署web项目
  20. 点击按钮控制滚动条滚动

热门文章

  1. PC 游戏修改常用工具
  2. Progamming Erlang 通过 Makefile 自动编译 .erl 文件
  3. python二级证书含金量排名_计算机二级证书含金量有多高?墙妹带你熟悉今年考试系统!...
  4. 数字货币投资评估和估值方法讨论
  5. 新手如何快速入门IT行业
  6. 计算机网络基础之OSI七层参考模型(二、应用层、表示层、会话层)
  7. win7电脑黑屏,只有鼠标
  8. 为什么编译tiny工程出错,提示不兼容的类型
  9. 一气之下,手撸了一个抖音去水印的工具!
  10. 声控开关电路原理图-门电路、限流电阻应用经典分析