[置顶]
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