[置顶] Android MVP 实现。基于Dagger2 + RxJava + Retrofit2 + Realm + ButterKnife + EventBus
2016-10-29 10:28
399 查看
前言
随着 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相关文章推荐
- Android Retrofit+Rxjava+MVP+EventBus+ButterKnife实现接口登录(无正则表达式)
- MVP框架做的登陆注册,商品列表,商品详情,购物车功能 RxJava+Retrofit ijkplayer EventBus ButterKnife
- [android架构篇]mvp+rxjava+retrofit+eventBus
- [置顶] 基于Android的校园跳蚤市场(二手)的设计与实现
- 【Android 进阶】ButterKnife+Retrofit+Picasso+RecycleView 实现一个小案例
- MVP+OKHTTP+Fresco+RecyclerView+EventBus实现点击传值
- [android架构篇]mvp+rxjava+retrofit+eventBus
- [置顶] MVP实战心得(三)---封装Retrofit2.0+RxAndroid+RxBus
- [置顶] Android从上车到漂移之ButterKnife完全解析
- mvp+okgo+butterknife搭建android app架构
- android Service+EventBus实现异地登录提示
- android架构篇mvp+rxjava+retrofit+eventBus
- Android问题之ButterKnife和Realm同时引用的小问题
- android MVP + dagger2 + Retrofit + Rxjava+okhttp android基础项目框架搭建(3)--完美实现
- 关于Android依赖注入框架ButterKnife和Dagger的简单比较
- [置顶] 基于UDP实现的android局域网视频同步播放
- [置顶] 【Android】Android开发之著名框架ButterKnife的使用详解,butterknife8.1.0版本的使用方法
- Android实现文章+评论(MVP,RxJava,Dagger2,ButterKnife)
- fresco加载图片+EventBus Activity之间跳转传值+GreenDAo数据库+retrofit请求数据+recyclerview展示数据+ButterKnife找控件
- Android注解使用之通过annotationProcessor注解生成代码实现自己的ButterKnife框架