前言
接著上一篇的博客,在上一篇中已經(jīng)把網(wǎng)絡(luò)框架Retrofit
進(jìn)行簡(jiǎn)單的封裝徐矩,接下來說說MVP
模式
MVC&MVP&MVVM區(qū)別
先來說說MVC
抱完,如圖
圖是最好的記憶方式蔚舀,MVC
呈現(xiàn)閉環(huán)的形式附较。其中:
-
View
使用xml
文件做為視圖樣式 -
Controller
Activity/Fragment
控制業(yè)務(wù)邏輯吃粒,比如發(fā)起網(wǎng)絡(luò)請(qǐng)求,控制視圖里面的輸入輸出信息拒课,更新本地?cái)?shù)據(jù)庫(kù)信息等等 -
Model
網(wǎng)絡(luò)請(qǐng)求數(shù)據(jù)模型徐勃,本地?cái)?shù)據(jù)庫(kù)數(shù)據(jù)模型,以及一些耗時(shí)處理操作并更新View
的展示信息等等
MVC具有兩種設(shè)計(jì)方式
- 從
View
獲取用戶操作信息早像,然后把信息傳遞給Controller
進(jìn)行一些邏輯處理 - 直接把操作信息給
Controller
疏旨,比如Controller
獲取EditText
的輸入信息等等
MVC優(yōu)缺點(diǎn)
優(yōu)點(diǎn)
-
MVC
已經(jīng)在framework
層搭建好,上手比較容易扎酷,模塊化比較清晰 - 實(shí)現(xiàn)了視圖層
View
,業(yè)務(wù)邏輯層Controller
以及數(shù)據(jù)模型層Model
的分離遏匆,降低了代碼的耦合性
缺點(diǎn)
-
Controller
任務(wù)重法挨,容易導(dǎo)致代碼臃腫 -
View
依賴Model
,layout
無(wú)法實(shí)現(xiàn)數(shù)據(jù)綁定幅聘,從而導(dǎo)致各種自定義View
凡纳,提高了代碼的重復(fù)性 - 由于
View
依賴Model
,導(dǎo)致不好寫代碼的測(cè)試用例帝蒿,只能編譯調(diào)試荐糜。
MVP
為了解決MVC
的View
依賴Model
,以及Controller
代碼臃腫葛超,增加了Presenter
來處理業(yè)務(wù)邏輯以及View
和Model
的分離暴氏,如下圖
通過Presenter
成功的把View
和Model
進(jìn)行分離,其中:
-
View
指Activity/Fragment
绣张,主要是實(shí)現(xiàn)MVPView
的抽象方法供Presenter
調(diào)用答渔,以及調(diào)用Presenter
方法進(jìn)行網(wǎng)絡(luò)請(qǐng)求或者業(yè)務(wù)處理,也就是圖上的雙向通信 -
Model
同MVC
一樣侥涵,主要是網(wǎng)絡(luò)請(qǐng)求數(shù)據(jù)模型或者本地?cái)?shù)據(jù)庫(kù)數(shù)據(jù)模型以及一些耗時(shí)的操作沼撕。 -
Presenter
業(yè)務(wù)處理宋雏,調(diào)用Model
的方法網(wǎng)絡(luò)請(qǐng)求獲取數(shù)據(jù)并且調(diào)用MVPView
的方法更新View
的數(shù)據(jù)
設(shè)計(jì)MVP
的時(shí)候,會(huì)遇到一個(gè)問題务豺,就是在手機(jī)屏幕切換的時(shí)候磨总,如果保存Presenter
當(dāng)前的狀態(tài)。這就涉及到Presenter
生命周期問題笼沥。這個(gè)問題在網(wǎng)上能夠搜到很多資料蚪燕,主要解決辦法有:
- 把
Activity/Fragment
當(dāng)做Presenter
來使用,然后Presenter
當(dāng)做View
來使用敬拓,參考一種在android中實(shí)現(xiàn)MVP模式的新思路這篇文章 - 使用
Loader
的生命周期來管理Presenter
的生命周期 -
Presenter
繼承Fragment
來管理生命周期
主要是以Presenter
生命周期要比Activity/Fragment
的生命周期更長(zhǎng)的原理來解決邻薯,這里使用的Loader
類來解決。
MVP優(yōu)缺點(diǎn)
優(yōu)點(diǎn)
- 實(shí)現(xiàn)了
View
和Model
的解耦 - 便于寫代碼的測(cè)試用例
缺點(diǎn)
- 上下文
context
容易丟失 - 具有
presenter
生命周期問題以及內(nèi)存泄漏問題 - 雙向通信回調(diào)乘凸,導(dǎo)致項(xiàng)目復(fù)雜化
MVVM
在MVP
中厕诡,當(dāng)我們?cè)?code>Presenter中獲取到Model
需要調(diào)用MVPView
的方法綁定View
并更新數(shù)據(jù),而MVVM
結(jié)合databinding
可以直接響應(yīng)Model
數(shù)據(jù)變化自動(dòng)化更新View
营勤,使Activity/Fragment
代碼變得更加簡(jiǎn)潔灵嫌。如下圖
-
Model
同上,網(wǎng)絡(luò)數(shù)據(jù)模型或者本地?cái)?shù)據(jù)模型 -
View
用xml
格式來表示視圖格式葛作,結(jié)合databinding
可以直接綁定Model
數(shù)據(jù)和視圖點(diǎn)擊事件 -
ViewModel
處理業(yè)務(wù)邏輯寿羞,發(fā)送網(wǎng)絡(luò)請(qǐng)求,更新Model
數(shù)據(jù)等操作
主要步驟就是View
獲取響應(yīng)信息后通知ViewModel
從Model
更新數(shù)據(jù)赂蠢,數(shù)據(jù)更新后绪穆,Model
通知ViewModel
更新View
展示的數(shù)據(jù)
MVVM優(yōu)缺點(diǎn)
優(yōu)點(diǎn)
-
View
和Model
低耦合 -
Activity/Fragment
代碼變得更加簡(jiǎn)潔,減少了更新View
數(shù)據(jù)以及點(diǎn)擊事件的處理 - 解決了
MVP
中View(Activity/Fragment)
和Presenter
相互持有的問題 - 不在需要考慮主線程更新
UI
的問題
缺點(diǎn)
-
View
層變得更加復(fù)雜 - 數(shù)據(jù)綁定使得
BUG
不好調(diào)試以及更大的性能消耗
MVP實(shí)現(xiàn)
實(shí)現(xiàn)的幾個(gè)步驟:
-
MVPView
虱岂,定義一些回調(diào)的方法玖院,并讓Activity/Fragment
實(shí)現(xiàn)它 -
Presenter
獲取View
實(shí)例,并提供幾個(gè)加載數(shù)據(jù)的加載框處理的回調(diào)方法 - 使用
Loader
管理Presenter
生命周期(一個(gè)PresenterFactory
和一個(gè)PresenterLoader
) -
Activity/Fragment
通過Loader
的LoaderManager.LoaderCallbacks
回掉獲取Presenter
實(shí)例
先定義MVPView
object Mvp {
interface View {
val sub: CompositeDisposable
}
}
其中CompositeDisposable
在BaseActivity
實(shí)現(xiàn)創(chuàng)建CompositeDisposable
對(duì)象第岖,統(tǒng)一管理接口請(qǐng)求的Obserable
的綁定和解綁
定義Presenter
interface Presenter{
fun attachView(attach: Any)
fun detachView()
}
attachView()
綁定View
难菌,也就是Activity/Fragment
detachView()
釋放View
通過BasePresenter
實(shí)現(xiàn)如下
@Suppress("UNCHECKED_CAST")
abstract class BasePresenter<V : Mvp.View> : Presenter,IProgressDialog{
private var attachView:V? = null
private var dialog:IProgressDialog ?= null
lateinit var mvpView:V
override fun attachView(attach: Any) {
this.attachView = attach as V
this.dialog = attach as IProgressDialog
mvpView = mvpView()
initHandler()
}
override fun detachView() {
this.attachView = null
}
private fun mvpView(): V {
checkAttachView()
return attachView!!
}
fun checkAttachView(){
if(this.attachView==null) throw RuntimeException("Call the attachView method before using the Mvp.View")
}
open fun initHandler() {
}
override fun showLoading() {
dialog?.showLoading()
}
override fun dismissLoading() {
dialog?.dismissLoading()
}
override fun updateNextPage(haveNext: Boolean) {
dialog?.updateNextPage(haveNext)
}
}
attachView
對(duì)應(yīng)View
層的引用,attach
傳Activity/Fragment
dialog
對(duì)應(yīng)BaseActivity
實(shí)現(xiàn)的IProgressDialog
接口蔑滓,實(shí)現(xiàn)showLoading()郊酒,dismissLoading(),updateNextPage()
方法管理網(wǎng)絡(luò)請(qǐng)求彈出框顯示和消失的操作以及RecyclerView
分頁(yè)加載的一些操作键袱。
mvpView
供繼承BasePresenter
的Presenter
網(wǎng)絡(luò)請(qǐng)求處理以及一些Activity/Fragment
方法的調(diào)用或者MVPView
的一些方法
Presenter生命周期管理
Loader
類生命周期有:
onStartLoading()
Activity
的onStart()
調(diào)用之后燎窘,會(huì)回調(diào)這個(gè)方法創(chuàng)建一個(gè)Loader
實(shí)例
onForceLoad()
當(dāng)onStartLoading
調(diào)用forceLoad
方法后,會(huì)回調(diào)這個(gè)方法蹄咖,可在這里創(chuàng)建Presenter
荠耽。
deliverResult()
調(diào)用這個(gè)方法可以把Presenter
分發(fā)給Activity/Fragment
onReset()
Loader
銷毀前回調(diào)
定義一個(gè)創(chuàng)建Presenter
的接口PresenterFactory
interface PresenterFactory<out P:BasePresenter<*>> {
fun create() :P
}
這樣就可以在Activity/Fragment
實(shí)現(xiàn)這個(gè)接口創(chuàng)建對(duì)應(yīng)Presenter
。從而得到相應(yīng)Presenter
的實(shí)例方法的引用比藻。
定義PresenterLoader
實(shí)現(xiàn)Loader
類并傳入PresenterFactory
來管理生命周期
class PresenterLoader<P :BasePresenter<*>>(context: Context, val factory:PresenterFactory<P>): Loader<P>(context) {
private var presenter:P? = null
override fun onStartLoading() {
if(presenter != null){
deliverResult(presenter)
return
}
forceLoad()
}
override fun onForceLoad() {
presenter = factory.create()
deliverResult(presenter)
}
override fun onReset() {
presenter = null
}
}
結(jié)合上面描述的Loader
生命周期就可以很好的理解上面的代碼了铝量。
LoaderManager.LoaderCallbacks
最后一步在BaseLoaderActivity
實(shí)現(xiàn)LoaderManager.LoaderCallbacks
的回調(diào)方法
abstract class BaseLoaderActivity<P:BasePresenter<*>>: AppCompatActivity(),Mvp.View,LoaderManager.LoaderCallbacks<P>{
protected var presenter:P? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
supportLoaderManager.initLoader(Consts.BASE_ACTIVITY_LOADER_ID,null,this)
}
override fun onLoadFinished(loader: Loader<P>?, data: P) {
presenter = data
presenter?.attachView(this)
}
override fun onCreateLoader(id: Int, args: Bundle?): Loader<P>? {
return createLoader()
}
override fun onLoaderReset(loader: Loader<P>?) {
presenter = null
}
override fun onDestroy() {
presenter?.detachView()
super.onDestroy()
}
abstract fun createLoader():Loader<P>
}
Loader
會(huì)先調(diào)用onCreateLoader
創(chuàng)建一個(gè)Loader
實(shí)例倘屹,然后調(diào)用onLoadFinished
,我們可以在這里獲取到Presenter
實(shí)例慢叨,然后當(dāng)Loader
銷毀前調(diào)用onLoaderReset()
纽匙,這里我們可以對(duì)Presenter
進(jìn)行釋放。其中
supportLoaderManager.initLoader(Consts.BASE_ACTIVITY_LOADER_ID,null,this)
是進(jìn)行Loader
初始化的一個(gè)操作
createLoader()
供繼承BaseLoaderActivity
的Activity
結(jié)合PresenterLoader
和PresenterFactory
創(chuàng)建Loader<P>
對(duì)象拍谐。
同理烛缔,BaseLoaderFragment
也如上創(chuàng)建即可。