Android端的MVP架構已經出來有很長時間了。而對於Android的MVP實現模式,也並無個標準的實現方式。java
如今市面上最流行的是google開源出來的一套MVP模型,此模型可到此google家MVP開源地址進行查看。android
而此篇博客將要介紹的並非google的MVP模型。而是根據我自身理解所建立的一種MVP模型。與google的MVP模型相比,此種MVP模型具備如下一些優點:git
僅僅靠上面的文字來進行分層說明略顯空洞,因此這裏咱們來經過一個簡單的sample代碼
來作MVP分層概念說明, 以下方是個簡單的登陸頁面的MVP實現:github
interface LoginView:MVPView {
fun onLoginSuccess()
fun onLoginFailed()
}
class LoginPresenter(view:DemoView):MVPPresenter<DemoView>(view) {
fun login(username:String, password:String) {
LoginApis.login(username, password, object Callback {
override fun onSuccess() {
view.onLoginSuccess()
}
override fun onFailed() {
view.onLoginFailed()
}
})
}
}
class LoginActivity:BaseMVPActivity(),LoginView {
// 建立與綁定Presenter。
val presenter = LoginPresenter(this)
override fun createPresenters() = arrayOf(presenter)
override fun onLoginSuccess() {
// 接收數據請求任務的返回數據並展現
EasyToast.DEFAULT.show("登陸成功")
}
override fun onLoginFailed() {
// 接收數據請求任務的返回數據並展現
EasyToast.DEFAULT.show("登陸成功")
}
...
// 點擊登陸
fun onLoginClick() {
val username = ...
val password = ...
presenter.login(username, password)// 發起login任務請求
}
}
複製代碼
1. LoginViewapi
interface LoginView:MVPView {
fun onLoginSuccess()
fun onLoginFailed()
}
複製代碼
繼承並擴展MVPView接口。不少人喜歡直接將此類做爲MVP中的V層
,可是實際上,我更願意稱此爲通訊協議接口
,做用是由V層
提供給P層
進行P-V綁定
,用於在P層
中通知V層
進行界面更新,相似於提供了一個Callback給P層進行使用bash
2. LoginActivity網絡
class LoginActivity:BaseMVPActivity(),LoginView {
override fun onLoginSuccess() {...}
override fun onLoginFailed() {...}
// 發起login任務請求
fun onLoginClick() {presenter.login(username, password)}
}
複製代碼
真正的View
層。能夠是Activity
, Fragment
等。是真正進行界面更新的地方。架構
View
層須要持有Presenter
的對象,用於在須要的時候使用presenter
發起具體的數據請求處理任務
,好比上例中點擊進行登陸時。經過presenter
發起了登陸任務, 並經過LoginView
協議接口,接收任務處理回調
進行界面更新app
3. LoginApis框架
LoginApis.login(username, password, object Callback {
override fun onSuccess() { view.onLoginSuccess() }
override fun onFailed() { view.onLoginFailed() }
})
複製代碼
Model
層,也叫數據提供層。
與其餘的MVP
不一樣,這裏並無要求Model
層須要定義一個特殊的接口去進行實現。全部的功能性api
。都可被視做爲Model
層。好比這裏LoginApis
,即是用於提供登陸模塊的網絡任務入口
。
Model
層與Presenter
層的通訊方式主要分爲兩種:一種直接從Model層同步獲取
到指定數據,另外一種是經過異步回調
的方式獲取到指定數據,即此處LoginApis
的通訊方式。
請注意避免直接向Model
層傳遞Presenter
去進行數據獲取。保證Model
的獨立性
4. LoginPresenter
Presenter
層,鏈接V-M的中間樞紐層。複雜的數據業務邏輯都在這裏進行處理。
結合上方示例:一個完整的M-P-V通訊流程,可分爲如下幾步:
V層向P層
發起具體的處理任務P層
接收到V層
發起的任務。調用M層
的api,獲取到原始數據
P層
對從M層
獲取到的原始數據
進行預處理(本示例由於較簡單,故沒有這一步)。處理完畢後
的數據。經過V層
提供的協議接口,通知到V層
去進行界面更新。經過上面的實例與講解。相信可使你們對MVP模型有必定的瞭解了,下面咱們將一步步的介紹整個MVP模型框架的搭建
interface MVPView {
fun getHostActivity():Activity
fun showLoadingDialog()
fun hideLoadingDialog()
fun toastMessage(message:String)
fun toastMessage(resId:Int)
}
複製代碼
MVPView
中定義了一些基礎的協議方法。這些方法是全部V層
都須要的功能。好比Toast展現
、進行異步任務時的加載中Dialog展現
等。
open class MVPPresenter<T:MVPView>(private var view:T?){
fun attach(t:T) {
this.view = t
}
fun detach() {
this.view = null
}
fun isViewAttached() = view != null
fun getActivity() = view?.getHostActivity()?:throw RuntimeException("Could not call getActivity if the View is not attached")
// Lifecycle delegate
open fun onCreate(bundle: Bundle?) {}
...
open fun onDestroy(){}
}
複製代碼
咱們來一條條的捋一下:
首先。咱們在上面的MVP說明中說過,MVPView
爲協議接口,提供出來用於進行P-V綁定
,因此相應的就會有P-V的綁定與解綁功能
(爲了方便使用,這裏也讓默認構造器自動進行了P-V綁定)
fun attach(t:T) { this.view = t }
fun detach() { this.view = null }
複製代碼
而後,咱們會常常須要在P層中使用綁定的Activity去進行各類操做。而p層
是不建議去持有Context實例
的,因此在此提供一個getActivity
方法,從綁定的view
中去獲取正確的Activity實例
提供使用:
fun getActivity() = view?.getActivity()?:throw RuntimeException("Could not call getActivity if the View is not attached")
複製代碼
而isViewAttached
方法,則是專門爲異步回調任務
所設計的api。由於若是是使用異步回調的方式
去從model層獲取的數據。那麼極可能,接收到回調消息的以前,這個時候view已被解綁置空
。致使通知失敗
因此。通常來講。對於任務中有異步回調操做
的,應該在回調處。先行判斷是否已解綁
。若已解綁則跳過當前操做:
if (!isViewAttached()) return
view?.hideLoadingDialog()
複製代碼
最後,則是提供的一堆onXXX
生命週期方法了。用於與activity/fragment 中的生命週期進行綁定。
與其餘的MVP
相比不一樣,這裏提供MVPDispatcher
做爲V-P鏈接器
:
class MVPDispatcher{
private val presenters:MutableList<MVPPresenter<*>> = mutableListOf()
// ==== 添加與移除Presenter ========
fun <V:MVPView> addPresenter(presenter:MVPPresenter<V>) {...}
internal fun <V:MVPView> removePresenter(presenter:MVPPresenter<V>) {...}
// ==== 綁定生命週期 ==========
fun dispatchOnCreate(bundle:Bundle?) {...}
...
fun dispatchOnRestoreInstanceState(savedInstanceState: Bundle?) {...}
}
複製代碼
能夠看到此鏈接器幹了如下幾件事:
addPresenter
與removePresenter
:向容器presenters
中添加或移除presenter實例
。 作到對單頁面綁定多個presenter的效果。dispatchOnXXX
:經過已添加的presenter
進行生命週期通知
. 作到V-P生命週期綁定的效果destroy
銷燬通知時,自動移除解綁全部的presenter實例
。fun dispatchOnDestroy() {
presenters.forEach {
if (it.isViewAttached()) { it.onDestroy() }
// 生命方法派發完畢後。自動解綁
removePresenter(it)
}
}
複製代碼
最後就是真正的V層
的建立了:Activity/Fragment
的MVP基類搭建!
這裏使用BaseMVPActivity
做爲舉例說明。fragment
的基類搭建與此大同小異。
首先,咱們須要肯定BaseMVPActivity
所須要盡到的職責:
1. 一個具體的V層,須要持有一個惟一的MVPDispatcher進行操做
abstract class BaseMVPActivity:Activity() {
val mvpDispatcher = MVPDispatcher()
}
複製代碼
2. 統一實現默認協議方法:MVPView
abstract class BaseMVPActivity:Activity(), MVPView {
...
override fun getHostActivity():Activity {...}
override fun showLoadingDialog() {...}
override fun hideLoadingDialog() {...}
override fun toastMessage(message:String) {...}
override fun toastMessage(resId:Int) {...}
}
複製代碼
3. 藉助MVPDispatcher實現V-P,一對多的綁定
abstract class BaseMVPActivity:Activity(), MVPView {
...
// 由子類提供當前頁面全部須要綁定的Presenter。
open fun createPresenters():Array<out MVPPresenter<*>>? = null
override fun onCreate(savedInstanceState: Bundle?) {
...
// 建立全部的presenter實例,並經過mvpDispatcher進行綁定
createPresenters()?.forEach { mvpDispatcher.addPresenter(it) }
}
}
複製代碼
好比在最上面咱們所舉例的LoginActivity。假設如今咱們須要對登陸頁再添加一個驗證碼校驗邏輯
. 此邏輯被放在了CaptchaPresenter
中:
class LoginActivity:BaseMVPActivity(),LoginView, CaptchaView {
// 登陸的Presenter實現
val loginPresenter = LoginPresenter(this)
// 驗證碼的Presenter實現
val captchaPresenter = CaptchaPresenter(this)
// 綁定多個Presenter
override fun createPresenters() = arrayOf(loginPresenter, captchaPresenter)
...
}
複製代碼
這就作到了Presenter的複用。在須要共用一些基礎業務邏輯
的時候。此一對多的綁定
是個很好的特性!
4. 藉助MVPDispatcher實現V-P生命週期關聯管理
abstract class BaseMVPActivity:Activity(), MVPView {
...// other codes
override fun onCreate(savedInstanceState: Bundle?) {
...
mvpDispatcher.dispatchOnCreate(intent?.extras)
}
...
override fun onDestroy() {
...
// 銷燬時mvpDispatcher會自動進行全部的Presenter的解綁。
// 因此具體的V層並不須要再本身去手動進行解綁操做了
mvpDispatcher.dispatchOnDestroy()
}
}
複製代碼
這就是一個基本的V層基類實現類須要作到的事。到這裏。整個MVP基礎框架的搭建就算完成了!
因爲此MVP架構實際上是個挺簡單的架構。因此我將此架構源碼存放在了EasyAndroid組件庫中了。
EasyAndroid做爲一款集成組件庫,此庫中所集成的組件,均包含如下特色,你能夠放心使用~~
1. 設計獨立
組件間獨立存在,不相互依賴,且若只須要集成庫中的部分組件。也能夠很方便的
只copy對應的組件文件
進行使用
2. 設計輕巧
由於是組件集成庫,因此要求每一個組件的設計儘可能精練、輕巧。避免由於一個小功能而引入大量無用代碼.
每一個組件的方法數均
不超過100
. 大部分組件甚至不超過50
。
因爲V層基類實現
不一樣項目都會有必定的差別性。好比Activity基類選擇
(AppCompatActivity/v4Activity/Activity)、或者MVPView的展現樣式設計
等。因此BaseMVPActivity
這類真正的V層基類實現
並無被放入lib中。而是在示例工程中
單獨進行了提供.
在須要使用的時候,經過copy此部分的源碼直接到工程中使用便可
EasyAndroid庫中的mvp模塊。僅僅提供了MVPView
、MVPPresenter
、MVPDispatcher
這三個基礎支持類。避免引入無用代碼。
https://github.com/yjfnypeu/EasyAndroid
MVPView
、MVPPresenter
、MVPDispatcher
源碼地址https://github.com/yjfnypeu/EasyAndroid/tree/master/utils/src/main/java/com/haoge/easyandroid/mvp
V層基類實現
及簡單sample示例代碼
地址https://github.com/yjfnypeu/EasyAndroid/tree/master/app/src/main/java/com/haoge/sample/easyandroid/activities/mvp