微信扫一扫

028-83195727 , 15928970361
business@forhy.com

[置顶] Android MVP 实现。基于Dagger2 + RxJava + Retrofit2 + Realm + ButterKnife + EventBus

android,mvp,dagger2,Retrofit2,Rxjava2016-10-29

前言

随着 Android 项目的越来越大,主流正在向 MVP 靠拢,但是一直没有一个比较好的较为通用的实现模式。那么下面结合一些人的做法介绍一下我的想法。

基础模块

1.Dagger 2 依赖注入模块

  • 项目各个模块使用 Dagger 2进行依赖的管理以解耦各个模块。
  • MVP 三层间使用 Dagger 2进行生命周期以及依赖的管理。
  • 各个组件使用接口进行抽象,各种实现使用 Dagger 2 进行拼装,实现解耦。
  • 一些依赖使用 Dagger 2进行存储管理,并自动注入到需要的上下文中。依赖主要有以下 3种:
    1.业务依赖:包括 HttpApi,DaoApi等。生命周期为全局单例(正常),所以存储于 AppComponent全局容器,依赖图表为 ServiceModule。
    2.App全局依赖:包括工作的线程池,全局 Context,以及会话账户相关的存储等,生命周期为全局单例,存储于 AppComponent全局容器,依赖图表为 AppModule。
    3.与各个 Activity绑定的依赖。生命周期与各个 Activity一致,每个 Activity和它对应的 Presenter, Model,Fragment等绑定在一起。

2.事件订阅发布模块 RxJava

  • 使用 RxJava将网络请求发布到 IO线程池中,再于主线程中订阅网络请求的结果。
  • 使用 RxJava的 CompositeSubscription对一组订阅进行管理。在 View销毁时取消对网络请求结果的订阅以避免内存泄漏。
  • 使用 RxJava进行任务调度,减少线程与 Handler 的相关代码。
  • 使用 RxJava 的 map 操作对公共数据进行处理,统一抛出业务异常。

3.网络请求 Retrofit2 + OKHttp3

  • 基于Retrofit2 框架,使用 GSON解析封装的数据模型。
  • 使用 OkHttp3 的拦截器对请求进行拦截,加入公共请求参数。

4.组建间通讯模块 RxBus 或者 EventBus

  • 使用 EventBus 进行组建间通讯。
  • 需求可能较少,大多数事件的订阅工作可以用 RxJava 代替,如遇到较为分散的事件,即用 EventBus 代替。

5.数据持久化模块

  • 使用 Realm 框架,支持持久化数据 <—-> 对象的无缝转换,代替数据库和SharePreference 存储一些类似应用配置的持久化数据。
  • Realm 对比于 Sql 具有更高的性能,完全面向对象的接口。
  • 通过阅读源码发现其实 Realm 是吧对象 ORM 为 JSON 格式并序列化在磁盘上,而且其转换代码是编译期间由注解处理器生成的,不会增加运行时负担。

6.View 与 View 事件注入模块

  • 使用 ButterKnife 进行 View 的注入以及 View 事件的注入,以减少不必要的重复代码,并且使代码简洁明了,提高代码的可读性。

7.图片异步加载与缓存模块

  • 使用 Picasso 进行网络图片的加载和多级缓存。

8.Http 缓存模块

  • 内存缓存使用 Retrofit2 自带即可。
  • 本地缓存使用 OkHttp 自带即可。

对于 Dagger2,ButterKnife,Realm 的性能说明,三者均采用注解处理器的方式,所以对性能影响不大。

具体架构设计

1.Dagger 2 依赖图

2.包结构

  • View 视图层,包含 Activity/Fragment,自定义控件等
  • Presenter ,presener 连接 Model 与 View 的桥梁,是控制层。
  • Model 数据模型,存储数据的模型,ORM 关系,除此之外还包含数据的获取逻辑,Http,Dao,File,SharePrefence 等。
  • Protocal 协议层,Model,View,Presenter 三层接口组成一个协议。
  • Utils 工具类,包含各种工具类,包装等。
  • Module 依赖图,Dagger2 的依赖模型图,描述了待注入的依赖的来源。基本上每一个协议( Activity )对应一个Module。
  • Component 依赖容器,Dagger2 的注入依赖容器。是 Dagger2 暴露的注入接口,以及获取依赖的接口。
  • Api 接口协议层,包含 HttpApi,DaoApi 等业务协议接口。现在分别由 Retrofit2 和 Realm 代理实现。
public interface LoginProtocol {

    public interface View{
        public void onLoginSuccess();
        public void onLoginedFailed();
    }

    public interface Presenter extends BasePresenter.IBaseCallBack{
        public void login(String name, String pass);
    }

    public interface Model{
        /**
         * login
         * @param name
         * @param pass
         */
        Subscription login(String name, String pass);
    }

}

一个简单的协议

3.UML 类图

可以看见 Api 和 接口的装配是由 Dagger 2 完成的,实现类之间没有直接依赖,逻辑上依赖的是它们的抽象接口。这样的话如果需要更换实现,仅仅需要在 Daager 2 的 Module 依赖图中配置即可。

如何自动化测试

1.整体方案

  • 使用 Dagger-2 + Espresso-2 + Mockito。将在真机上测试。
  • Dagger 2 负责依赖替换 ,将各层,Api 实现替换为测试版本。
  • Espresso-2 负责 View 事件的模拟,以及显示结果比较。
  • Mockito 负责模拟各种数据情况,数据模拟原则参考主站文档。

2.Espresso 配置

  • 在 build.gradle 中配置 SDK
  • 添加 Espresso 的 TestRunner。
  • 新建测试 类 XXXXActivityTest。
  • 创建一个 @Rule, ActivityTestRule 用来指明被测试的 Activity,配置 @Test 等。
  • 编写 View 事件的发生逻辑以及检查策略。
  • 最后运行并关注 check() 检查结果。

3. Dagger 2 配置

  • 使用 Dagger2 配置依赖注入。
  • 流程分为3步: Module -> Component -> Application。
  • Module, 使用模拟 Api 类, MockApiClient。
  • Component,注入需要测试的类。
  • Application ,继承非测试的 Application (ShuduApplication), 设置测试组件, 重写获取组件的方法 (getAppComponent)。注册 Application 至 TestRunner。
  • 使用 Mock 数据类对数据各种情况进行模拟。

Demo 链接

Github MVP demo