作者:Walkud 連結:https://www.jianshu.com/p/4cb1a56acf9e
先上本專案 Github 地址:
https://github.com/Walkud/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<P> : 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<V : Any, out M> : 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<SearchActivity, MainModel>() {//泛型指定 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,專案中重構部分的程式碼註釋比較完善,便於你查閱,謝謝!
本專案 Github 地址:JudyKotlinMvp
https://github.com/Walkud/JudyKotlinMvp
朋友會在“發現-看一看”看到你“在看”的內容