Android MVP 实现。基于Dagger2 + RxJava + Retrofit2 + Realm + ButterKnife + EventBus

前言

随着 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 对于收藏分页的影响:因为 Realm 使用了懒加载的技术,所以说其内部已经实现类似功能,所以在使用 Realm 方案下其实是不需要进行分页的。
  • 对于 Realm 对上层的高入侵性:直接使用业务 Model 作为 Realm 的对象是高耦合的,首先 Model 需要继承 RealmObject,其次 Model 层中的所有集合都需要换成 RealmArray;除此之外,在取数据的时候,Realm 希望我们使用 RealmResults 这个 List 代理来作为查询结果,这样对于 View 层也是较为严重的耦合。
  • 解决方法:首先对于 Model,我们使用适配器模式,将 Model 转换为 Realm 缓存数据结构再缓存,保持业务 Model 的独立。对于查询的 RealmArray 数据结构我们对他做List 接口代理,这样无论 MVP 哪一层,接口都是独立的。
  • Realm 的优势:对于异常的封装,结合 RxJava 可以方便的进行错误处理;极高的性能,查询性能是 GreenDao 的 100 倍,加上懒加载技术,完全可以省略异步和分页的逻辑;除此之外,他保证了全局的数据一致性,这样的话在收藏和删除操作发生时,查询出的数据也会同步更新,免去了数据同步的逻辑处理。

6.View 与 View 事件注入模块

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

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

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

8.Http 缓存模块

  • 如果 URL 固定,业务简单:
  • 内存缓存使用 Retrofit2 自带即可。
  • 本地缓存使用 OkHttp 自带即可。
  • 如果 URL 不固定,业务具有某些特点:
  • 本地缓存库建议 ASimpleCache,当的缓存场景比较少时,选取这类轻量级的框架,它仅仅只有一个文件,十几个类。
  • 怎样与 RxJava 结合:把缓存操作包装为 Observable ;首先读取缓存,如果没有缓存则使用网络请求的 Observable ;使用 defer 操作符来包装上述的数据源选择逻辑为新的 Observable。这样就保证了 Model 层,Presenter 层的逻辑一致性,不会因为加入缓存而对整个逻辑造成影响。

9.异常处理

  • 异常路径:异常通常产生于 API,Model 层,现在需要反应于 View 层呈现给用户。当然除此之外,也可以在其他层统一截获某些异常做一些 log 之类的操作。整体上异常是在 Model 层的 RxJava 的 onError 中捕获,最终传递到 View 层呈现给用户。
  • 异常的转换与加工:我们需要一个异常处理引擎,对各种异常进行处理,并转换成用户可以接受的异常信息。利用 RxJava 的 onErrorResumeNext 插入对异常的转换加工逻辑。这样的话,在 onError 的 Callback 中的到的就已经是用户可读的异常了。
  • 异常的统一记录:为了方便对异常进行统一记录,可以利用 RxJava 的 doOnError,插入对异常的监听逻辑。

对于 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

猜你喜欢

转载自blog.csdn.net/ganyao939543405/article/details/52963144