先上本项目 Github 地址:JudyKotlinMvp
首先声明
本项目是参考 git-xuhao/KotlinMvp ,对原项目 Mvp 核心代码及Adapter代码按照自己的想法进行了重构,布局 (xml) 文件(除 fragment_mine.xml )、工具类、自定义 View 都直接使用的原项目的文件,本项目的主要目的是为了 Kotlin 学习,将自己对 Java 版 Mvp 的理解用 Kotlin 实现。
说明
我希望用尽可能少的文字和代码来说明是自己对 Mvp 的理解。
Mvp
Mvp 最主要的目的就是为了解耦,让各个模块各司其职。
M (Model)
负责业务模型的构建,可简单理解为P所需要的数据结构就在这一层处理及封装(包括网络、Sqlite、sp等)。
可参考 MainModel ,Model的粒度根据实际情况自行控制。
P (Presenter)
负责处理业务逻辑,可简单理解为业务中的UI操作、 if 判断等。
UI操作不仅仅是调用 UI 的数据更新操作,还包含何时显示进度框、隐藏进度框、绑定Ui生命周期等。
model.getSearchResult(keyWords!!)
.compose(NetTransformer())//异步调度(异步发起请求 --> 主线程回调)
.compose(ProgressTransformer(view))//进度框的显示与隐藏
.compose(bindUntilOnDestroyEvent())//绑定UI生命周期 (PS: 生命周期时机可控)
.subscribe(object : RxSubscribe() {
……
})
以上代码用三行代码进行了异步调度、进度框显示与隐藏控制、UI 的声明周期绑定(防止内存泄漏)操作,这就是 RxJava 魅力所在。
可参考 VideoDetailPresenter
V (View)
负责提供 UI 交互的能力,可简单理解为 V 监听到用户的点击事件 --> P 处理业务逻辑 --> V 更新UI。
Activity、Fragment 为 V ,其中 Adapter、View(xml、自定义View)、Dialog 等为 V 的一部分,所有的交互事件都放在 Activity 和 Fragment 中进行监听,便于日后定位。
项目中 Mvp 实现
//依赖关系
View < —— > Presenter —— > Model
本项目中使用泛型来指定依赖关系,且直接依赖的具体实现,并没有依赖抽象,可以有效提高开发效率,但违背了设计原则,个人认为过于看重这些原则,导致开发效率降低也有些得不偿失。
//基类 V (MvpFragment 同理)
abstract class MvpActivity : MvcActivity() {
val presenter: P by lazy {
getP()
}
/**
* 获取逻辑处理实例,子类实现
*/
abstract fun getP(): P
……
}
//具体实现
class SearchActivity : MvpActivity() {//泛型指定 P 的依赖关系
// 这里实例化 P 并且 注入 V
override fun getP() = SearchPresenter().apply { view = this@SearchActivity }
}
//基类 P
open class BasePresenter : ViewLifecycle() {
/**
* UI视图,即Activity或Fragment
*/
var view: V by Delegates.notNull()
/**
* 业务模型,即XXXModel,这里使用java反射(kotlin反射太慢,暂时不建议使用)创建示例,省去在每个Presenter中创建实例
*/
val model: M by lazy {
//这里使用 Java 反射实例化 M,
ReflectionUtils.getSuperClassGenricType(this, 1)
}
……
}
//具体实现
class SearchPresenter : BasePresenter() {//泛型指定 V 和 M 的具体实现
……
}
通过上面代码,Mvp 之间的关系就建立起来了,需要注意的是 M 是通过Java 反射实例化的(省去在 P 的实现类中去实例化,直接使用即可),具体使用请参考代码。
MvpActivity
BasePresenter
解决痛点
抽象(创建过多无用的接口)
之前用过Mvp的在这点上应该深有体会,直接依赖具体实现可以很好的解决这个问题,对于需要复用的地方也可以指定泛型为接口,从而达到可复用目的,实际项目中这种复用需求很少。
异步导致的内存泄漏
异步时间过长而界面已退出,回调中依然隐式的持有 Activity 实例,这个问题在开发中很常见,所以本项目中使用了 RxJava (异步利器)来处理异步问题,由于它的灵活性,在加上另外一位大神开源库 RxLifecycle 可以很方便的处理这个问题,而且使代码也非常美观(一行代码搞定),最重要的是处理时机可控(比如在 onStop 或 onDestory )。
//为了控制篇幅,偷懒了,参照前面贴的代码
compose(bindUntilOnDestroyEvent())//绑定UI生命周期 (PS: 生命周期时机可控)
进度UI的控制
对于弹框进度、下拉刷新、上拉加载、异常布局等控制问题,使用 RxJava 可以完美的解决,一行代码解放开发者大脑,不需要再去想何时显示、何时隐藏。
//为了控制篇幅,偷懒了,参照前面贴的代码
compose(ProgressTransformer(view))//进度框的显示与隐藏
ProgressTransformer
交互都由 Activity 或 Fragment 来处理
有没有同学遇到修改遗留 Bug 找某个点击事件的时候,要跳转 N 个类,最后发现事件监听里面发了一个 Event 事件,然后又得找是谁消费了事件,发现居然有 N 个类都有消费(一脸懵逼,我的真实遭遇。。。) ,本项目的处理方式是将最终的监听都放在 V 中 的 addListener 方法中,找交互事件的时候可以快速定位。
RxJava
RxJava 使代码更连贯、逻辑更清晰(特别是去看原来的代码)、简洁,项目中使用了 RxJava 优雅的解决了异步调度、进度框显示与隐藏控制、UI 的声明周期绑定的问题,使用简单灵活。
RxJava 对于初学者来说,算是一个比较难上手的库,网上也有很多文章,但看完之后也不知道哪里好用,哪里简洁。在这里分享一个我最初学习的方法:
- 先依葫芦画瓢,硬着头皮去使用,对 RxJava 有个初步的概念。
- 有了初步的概念,回头再去看文章会有不一样的体会,会理解作者在说什么,随便学习几个常用的操作符。
- 结合自己的理解,最好能从生活中找个例子对照理解。
- 学习理解更多的操作符及操作符示意图。
- fuck 源码、分享、写文章。
以上方法不适合所有人,以后有机会我也写写对 RxJava 的理解。
Retrofit
Retrofit 是一个遵循 RESTful 设计标准的一个网络请求封装库。
Retrofit 使用了大量的设计模式,其中动态代理 + 注解的思路来声明后端接口非常优雅,再加上提供网络请求适配器及数据转换器的扩展,基本上已满足大部分的业务需求了。
上图是我整理的一个 Retrofit 简易流程图。Retrofit 源码推荐阅读,难度小,里面的技术知识非常多,可以在网上找篇源码分的文章结合着看。
总结
- 分享自己对 Mvp 的理解。
- 分享了如何优雅的解决异步调度、进度框显示与隐藏控制、UI 的声明周期绑定问题。
- 说明项目 Mvp 关系的实现逻辑。
- 说明项目解决的痛点。
- 分享 RxJava 的学习方法。
- 整理出 Retrofit 简易流程图。
有不同意见的可以通过留言或者Issues,项目中重构部分的代码注释比较完善,便于你查阅,如果你喜欢并认可,可以点击喜欢并star项目,谢谢!
本项目 Github 地址:JudyKotlinMvp