
先上本項(xiàng)目 Github 地址:JudyKotlinMvp
首先聲明
本項(xiàng)目是參考 git-xuhao/KotlinMvp ,對(duì)原項(xiàng)目 Mvp 核心代碼及Adapter代碼按照自己的想法進(jìn)行了重構(gòu),布局 (xml) 文件(除 fragment_mine.xml )、工具類、自定義 View 都直接使用的原項(xiàng)目的文件,本項(xiàng)目的主要目的是為了 Kotlin 學(xué)習(xí),將自己對(duì) Java 版 Mvp 的理解用 Kotlin 實(shí)現(xiàn)。
說明
我希望用盡可能少的文字和代碼來說明是自己對(duì) Mvp 的理解。
Mvp
Mvp 最主要的目的就是為了解耦,讓各個(gè)模塊各司其職。
M (Model)
負(fù)責(zé)業(yè)務(wù)模型的構(gòu)建,可簡(jiǎn)單理解為P所需要的數(shù)據(jù)結(jié)構(gòu)就在這一層處理及封裝(包括網(wǎng)絡(luò)、Sqlite、sp等)。
可參考 MainModel ,Model的粒度根據(jù)實(shí)際情況自行控制。
P (Presenter)
負(fù)責(zé)處理業(yè)務(wù)邏輯,可簡(jiǎn)單理解為業(yè)務(wù)中的UI操作、 if 判斷等。
UI操作不僅僅是調(diào)用 UI 的數(shù)據(jù)更新操作,還包含何時(shí)顯示進(jìn)度框、隱藏進(jìn)度框、綁定Ui生命周期等。
model.getSearchResult(keyWords!!)
.compose(NetTransformer())//異步調(diào)度(異步發(fā)起請(qǐng)求 --> 主線程回調(diào))
.compose(ProgressTransformer(view))//進(jìn)度框的顯示與隱藏
.compose(bindUntilOnDestroyEvent())//綁定UI生命周期 (PS: 生命周期時(shí)機(jī)可控)
.subscribe(object : RxSubscribe<HomeBean.Issue>() {
……
})
以上代碼用三行代碼進(jìn)行了異步調(diào)度、進(jìn)度框顯示與隱藏控制、UI 的聲明周期綁定(防止內(nèi)存泄漏)操作,這就是 RxJava 魅力所在。
V (View)
負(fù)責(zé)提供 UI 交互的能力,可簡(jiǎn)單理解為 V 監(jiān)聽到用戶的點(diǎn)擊事件 --> P 處理業(yè)務(wù)邏輯 --> V 更新UI。
Activity、Fragment 為 V ,其中 Adapter、View(xml、自定義View)、Dialog 等為 V 的一部分,所有的交互事件都放在 Activity 和 Fragment 中進(jìn)行監(jiān)聽,便于日后定位。
項(xiàng)目中 Mvp 實(shí)現(xiàn)
//依賴關(guān)系
View < —— > Presenter —— > Model
本項(xiàng)目中使用泛型來指定依賴關(guān)系,且直接依賴的具體實(shí)現(xiàn),并沒有依賴抽象,可以有效提高開發(fā)效率,但違背了設(shè)計(jì)原則,個(gè)人認(rèn)為過于看重這些原則,導(dǎo)致開發(fā)效率降低也有些得不償失。
//基類 V (MvpFragment 同理)
abstract class MvpActivity<P> : MvcActivity() {
val presenter: P by lazy {
getP()
}
/**
* 獲取邏輯處理實(shí)例,子類實(shí)現(xiàn)
*/
abstract fun getP(): P
……
}
//具體實(shí)現(xiàn)
class SearchActivity : MvpActivity<SearchPresenter>() {//泛型指定 P 的依賴關(guān)系
// 這里實(shí)例化 P 并且 注入 V
override fun getP() = SearchPresenter().apply { view = this@SearchActivity }
}
//基類 P
open class BasePresenter<V : Any, out M> : ViewLifecycle() {
/**
* UI視圖,即Activity或Fragment
*/
var view: V by Delegates.notNull()
/**
* 業(yè)務(wù)模型,即XXXModel,這里使用java反射(kotlin反射太慢,暫時(shí)不建議使用)創(chuàng)建示例,省去在每個(gè)Presenter中創(chuàng)建實(shí)例
*/
val model: M by lazy {
//這里使用 Java 反射實(shí)例化 M,
ReflectionUtils.getSuperClassGenricType<M>(this, 1)
}
……
}
//具體實(shí)現(xiàn)
class SearchPresenter : BasePresenter<SearchActivity, MainModel>() {//泛型指定 V 和 M 的具體實(shí)現(xiàn)
……
}
通過上面代碼,Mvp 之間的關(guān)系就建立起來了,需要注意的是 M 是通過Java 反射實(shí)例化的(省去在 P 的實(shí)現(xiàn)類中去實(shí)例化,直接使用即可),具體使用請(qǐng)參考代碼。
解決痛點(diǎn)
抽象(創(chuàng)建過多無用的接口)
之前用過Mvp的在這點(diǎn)上應(yīng)該深有體會(huì),直接依賴具體實(shí)現(xiàn)可以很好的解決這個(gè)問題,對(duì)于需要復(fù)用的地方也可以指定泛型為接口,從而達(dá)到可復(fù)用目的,實(shí)際項(xiàng)目中這種復(fù)用需求很少。
異步導(dǎo)致的內(nèi)存泄漏
異步時(shí)間過長(zhǎng)而界面已退出,回調(diào)中依然隱式的持有 Activity 實(shí)例,這個(gè)問題在開發(fā)中很常見,所以本項(xiàng)目中使用了 RxJava (異步利器)來處理異步問題,由于它的靈活性,在加上另外一位大神開源庫(kù) RxLifecycle 可以很方便的處理這個(gè)問題,而且使代碼也非常美觀(一行代碼搞定),最重要的是處理時(shí)機(jī)可控(比如在 onStop 或 onDestory )。
//為了控制篇幅,偷懶了,參照前面貼的代碼
compose(bindUntilOnDestroyEvent())//綁定UI生命周期 (PS: 生命周期時(shí)機(jī)可控)
進(jìn)度UI的控制
對(duì)于彈框進(jìn)度、下拉刷新、上拉加載、異常布局等控制問題,使用 RxJava 可以完美的解決,一行代碼解放開發(fā)者大腦,不需要再去想何時(shí)顯示、何時(shí)隱藏。
//為了控制篇幅,偷懶了,參照前面貼的代碼
compose(ProgressTransformer(view))//進(jìn)度框的顯示與隱藏
交互都由 Activity 或 Fragment 來處理
有沒有同學(xué)遇到修改遺留 Bug 找某個(gè)點(diǎn)擊事件的時(shí)候,要跳轉(zhuǎn) N 個(gè)類,最后發(fā)現(xiàn)事件監(jiān)聽里面發(fā)了一個(gè) Event 事件,然后又得找是誰(shuí)消費(fèi)了事件,發(fā)現(xiàn)居然有 N 個(gè)類都有消費(fèi)(一臉懵逼,我的真實(shí)遭遇。。。) ,本項(xiàng)目的處理方式是將最終的監(jiān)聽都放在 V 中 的 addListener 方法中,找交互事件的時(shí)候可以快速定位。
RxJava
RxJava 使代碼更連貫、邏輯更清晰(特別是去看原來的代碼)、簡(jiǎn)潔,項(xiàng)目中使用了 RxJava 優(yōu)雅的解決了異步調(diào)度、進(jìn)度框顯示與隱藏控制、UI 的聲明周期綁定的問題,使用簡(jiǎn)單靈活。
RxJava 對(duì)于初學(xué)者來說,算是一個(gè)比較難上手的庫(kù),網(wǎng)上也有很多文章,但看完之后也不知道哪里好用,哪里簡(jiǎn)潔。在這里分享一個(gè)我最初學(xué)習(xí)的方法:
- 先依葫蘆畫瓢,硬著頭皮去使用,對(duì) RxJava 有個(gè)初步的概念。
- 有了初步的概念,回頭再去看文章會(huì)有不一樣的體會(huì),會(huì)理解作者在說什么,隨便學(xué)習(xí)幾個(gè)常用的操作符。
- 結(jié)合自己的理解,最好能從生活中找個(gè)例子對(duì)照理解。
- 學(xué)習(xí)理解更多的操作符及操作符示意圖。
- fuck 源碼、分享、寫文章。
以上方法不適合所有人,以后有機(jī)會(huì)我也寫寫對(duì) RxJava 的理解。
Retrofit
Retrofit 是一個(gè)遵循 RESTful 設(shè)計(jì)標(biāo)準(zhǔn)的一個(gè)網(wǎng)絡(luò)請(qǐng)求封裝庫(kù)。
Retrofit 使用了大量的設(shè)計(jì)模式,其中動(dòng)態(tài)代理 + 注解的思路來聲明后端接口非常優(yōu)雅,再加上提供網(wǎng)絡(luò)請(qǐng)求適配器及數(shù)據(jù)轉(zhuǎn)換器的擴(kuò)展,基本上已滿足大部分的業(yè)務(wù)需求了。

上圖是我整理的一個(gè) Retrofit 簡(jiǎn)易流程圖。Retrofit 源碼推薦閱讀,難度小,里面的技術(shù)知識(shí)非常多,可以在網(wǎng)上找篇源碼分的文章結(jié)合著看。
總結(jié)
- 分享自己對(duì) Mvp 的理解。
- 分享了如何優(yōu)雅的解決異步調(diào)度、進(jìn)度框顯示與隱藏控制、UI 的聲明周期綁定問題。
- 說明項(xiàng)目 Mvp 關(guān)系的實(shí)現(xiàn)邏輯。
- 說明項(xiàng)目解決的痛點(diǎn)。
- 分享 RxJava 的學(xué)習(xí)方法。
- 整理出 Retrofit 簡(jiǎn)易流程圖。
有不同意見的可以通過留言或者Issues,項(xiàng)目中重構(gòu)部分的代碼注釋比較完善,便于你查閱,如果你喜歡并認(rèn)可,可以點(diǎn)擊喜歡并star項(xiàng)目,謝謝!
本項(xiàng)目 Github 地址:JudyKotlinMvp