??轉(zhuǎn)發(fā)請注明出處:http://www.reibang.com/p/b3e15cbf83a9
??開發(fā)android的幾年時間怜校,從一開始接觸到MVP模式间影,就對于代碼邏輯分離管理產(chǎn)生了濃厚的興趣,對官方MVP模式進行研究與實戰(zhàn)茄茁,發(fā)現(xiàn)在實際應用中魂贬,官方MVP模式不能滿足絕大部分場景,近一年來裙顽,在不斷的應用中付燥,對MVP模式進行了改進,本文將對其一一解說愈犹,如果有更好的做法键科,還望能一起探討闻丑。
??MVP模式的優(yōu)勢與弊端之類的,網(wǎng)上已經(jīng)有太多的文章勋颖,不再對其進行述說嗦嗡,本文只對項目是如何構(gòu)造的進行述說與解釋:
一、MVP三部分簡述
1.Model的基類
abstract class BaseMvpModel {
private var mContext: Context? = null
fun getContext(): Context? {
return mContext
}
/*************分界線以上為外部使用的方法饭玲,以下為私有或生命周期方法不應主動調(diào)用*****************/
fun onCreate(context: Context?) {
mContext = context
}
fun onDestroy() {
mContext = null
}
}
??Model專門與數(shù)據(jù)打交道侥祭,此處會維護一個成員變量Context,Context由Presenter生成Model時傳入茄厘,Context的生命周期跟界面視圖生命周期保持一致矮冬,onCreate()與onDestroy()方法均為框架自動調(diào)用,調(diào)用者只需在生命周期內(nèi)調(diào)用getContext()方法即可獲取到Context對象次哈,保持封閉性胎署。
??此處維護Context的原因,是在于處理或者獲取數(shù)據(jù)時亿乳,并不是所有數(shù)據(jù)均從網(wǎng)上獲取硝拧,還有很多是本地數(shù)據(jù),如SharedPreferences葛假,或者是獲取本地已安裝APP信息障陶,靜默卸載APP時的處理等等,均需要用到Context聊训。
2.Presenter的基類
abstract class BaseMvpPresenter<V> {
private var mContext: Context? = null
fun getContext(): Context? {
return mContext
}
/**
* MVP模式中持有View對象
*/
private var mView: V? = null
fun getView(): V? {
return mView
}
/**
* 此方法直接獲取Model抱究,自動生成管理
*/
fun <M : BaseMvpModel> getModel(modelClass: Class<M>): M {
val typeName = modelClass.name
var model = mModelMap?.get(typeName)
if (model == null) {
model = modelClass.newInstance() as BaseMvpModel
model.onCreate(mContext)
mModelMap?.put(typeName, model)
}
@Suppress("UNCHECKED_CAST")
return model as M
}
/*************分界線以上為外部使用的方法,以下為私有或生命周期方法不應主動調(diào)用*****************/
/**
* 保存所有Model的Map
*/
private var mModelMap: ArrayMap<String, BaseMvpModel>? = ArrayMap()
fun onCreate(context: Context?) {
mContext = context
}
fun attach(view: V) {
mView = view
}
fun detach() {
cleanAllModel()
mView = null
mContext = null
}
private fun cleanAllModel() {
val iterator = mModelMap?.keys?.iterator()
if (iterator != null)
while (iterator.hasNext()) {
val model = mModelMap?.get(iterator.next()) as BaseMvpModel
model.onDestroy()
}
mModelMap?.clear()
mModelMap = null
}
}
??Presenter是View與Model的橋梁带斑,所以要同時處理好View與Model的初始化與銷毀鼓寺,此處的View使用泛型,類型轉(zhuǎn)換都是自動和隱式的勋磕,提高代碼的重用率妈候。
??Presenter內(nèi)部維護的Context與View視圖,由Activity生成Presenter時傳入挂滓,同樣與界面視圖生命周期保持一致苦银,Context是為了生成Model時動態(tài)傳參,有時候也需要在此處使用赶站,View則不必多說幔虏,用于視圖的回調(diào)。
??此處唯一特殊的就是在對Model的維護上贝椿,使用了泛型與反射的機制想括,由于經(jīng)常會有多個不同的Model,所以此處用一個ArrayMap來統(tǒng)一管理Model烙博,獲取時調(diào)用getModel()方法瑟蜈,如果ArrayMap里面沒有則會反射生成(泛型約束不存在強轉(zhuǎn)換錯誤問題)烟逊,同時保存在ArrayMap里面以便復用,所有的Model均會在界面視圖銷毀時統(tǒng)一銷毀踪栋,外部只需要注意在生命周期內(nèi)調(diào)用即可焙格。
3.View的基類
abstract class BaseMvpActivity<V, P : BaseMvpPresenter<V>> : BaseActivity() {
/**
* MVP模式中Presenter對象
*/
private var mPresenter: P? = null
fun getPresenter(): P? {
if (mPresenter == null) {
throw RuntimeException("Presenter is Null, You can't get it at this time!")
}
return mPresenter
}
/*************分界線以上為外部使用的方法,以下為私有或生命周期方法不應主動調(diào)用*****************/
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mPresenter = createPresenter()
@Suppress("UNCHECKED_CAST")
mPresenter?.attach(this as V)
}
override fun onDestroy() {
mPresenter?.detach()
mPresenter = null
super.onDestroy()
}
/**
* 此方法自動生成Presenter
*/
private fun <P : BaseMvpPresenter<V>> createPresenter(): P {
val type = javaClass.genericSuperclass as ParameterizedType
val actualTypeArguments = type.actualTypeArguments
@Suppress("UNCHECKED_CAST")
val presenterClass = actualTypeArguments[1] as Class<P>
val presenter = presenterClass.newInstance() as BaseMvpPresenter<V>
presenter.onCreate(this)
@Suppress("UNCHECKED_CAST")
return presenter as P
}
}
??View同樣使用泛型夷都,V泛型為Presenter已經(jīng)定義好要傳入的視圖實例眷唉,P泛型定義為必須是BaseMvpPresenter的子類,注意mPresenter?.attach(this as V)此代碼同時也約束了View視圖必須同時實現(xiàn)V囤官,否則運行時會類型轉(zhuǎn)換異常冬阳。
??初始化Presenter時同樣使用泛型與反射的機制動態(tài)生成(泛型約束不存在強轉(zhuǎn)換錯誤問題),且與界面視圖生命周期一致党饮,外部只需要注意在生命周期內(nèi)調(diào)用getPresenter()獲取即可肝陪。
??為什么定義V泛型:View視圖必須實現(xiàn)V泛型,關(guān)聯(lián)上Presenter需求的V泛型刑顺,實際上V泛型就是View視圖的接口氯窍,定義了刷新視圖的方法,關(guān)聯(lián)之后能在Presenter直接調(diào)取從而實現(xiàn)刷新界面蹲堂。
二狼讨、實際使用場景
??說得再多,不如直接使用看看效果如何來得實在柒竞,我們下面來看下每個模塊的實現(xiàn)類政供,應用場景屬于我最常遇到的一P對多M。
1.接口的定義
interface IMainContract {
interface IMainView {
fun showTitle(title: String)
fun showUser(user: String)
}
interface IMainPresenter {
fun getTitle()
fun getUser()
}
interface IMainModel {
fun getTitle(callback: ResultCallBack<String>)
}
}
interface ICommonContract {
interface ICommonModel {
fun getUser(callback: ResultCallBack<String>)
}
}
??ResultCallBack只是一個回調(diào)接口朽基,里面有成功或失敗的回調(diào)方法布隔。
??這里我們簡單定義了幾個方法,展示標題與展示用戶數(shù)據(jù)稼虎,假設展示標題是該界面特有衅檀,展示用戶數(shù)據(jù)是所有界面共有,此時ICommonContract為所有界面共用霎俩。
2.Model的實現(xiàn)類
class MainModel : BaseMvpModel(), IMainContract.IMainModel {
override fun getTitle(callback: ResultCallBack<String>) {
callback.onSuccess("I am title by kotlin")
}
}
class CommonModel : BaseMvpModel(), ICommonContract.ICommonModel {
override fun getUser(callback: ResultCallBack<String>) {
callback.onSuccess("Mr.T")
}
}
??Model的實現(xiàn)類比較簡單术吝,這里只為了示例用法,所以都直接返回字符串茸苇,同樣CommonModel為所有界面共用。
3.Presenter的實現(xiàn)類
class MainPresenter : BaseMvpPresenter<IMainContract.IMainView>(), IMainContract.IMainPresenter {
override fun getTitle() {
getModel(MainModel::class.java).getTitle(object : ResultCallBack<String>() {
override fun onSuccess(data: String, code: Int, msg: String) {
getView()?.showTitle(data)
}
override fun onFail(e: Exception, code: Int, msg: String) {
}
})
}
override fun getUser() {
getModel(CommonModel::class.java).getUser(object : ResultCallBack<String>() {
override fun onSuccess(data: String, code: Int, msg: String) {
getView()?.showUser(data)
}
override fun onFail(e: Exception, code: Int, msg: String) {
}
})
}
}
??Presenter的實現(xiàn)類是重點沦寂,此時前面所做的一切学密,價值就提現(xiàn)出來了,Presenter的實現(xiàn)類传藏,僅需要重寫業(yè)務邏輯所定義的方法腻暮,實現(xiàn)只關(guān)心業(yè)務邏輯而并不需要操心其余有關(guān)生命周期的事情彤守,并且我們在這里也看到,只需要傳對應Model的Class就能使用其內(nèi)部的方法哭靖,getView()也能直接調(diào)用對應的方法具垫,不需要再進行強轉(zhuǎn),這就是泛型的好處试幽,一心關(guān)注業(yè)務邏輯筝蚕。
3.View的實現(xiàn)類
class MainActivity : BaseMvpActivity<IMainContract.IMainView, MainPresenter>(), IMainContract.IMainView {
override val mContentViewRes: Int = R.layout.activity_main
override fun initView() {
}
override fun initData() {
getPresenter()?.getTitle()
getPresenter()?.getUser()
}
override fun showTitle(title: String) {
mTitle_TV.text = title
}
override fun showUser(user: String) {
mUser_TV.text = user
}
}
??View的實現(xiàn)類是同樣也是重點,mContentViewRes變量铺坞、initView()函數(shù)起宽、initData()函數(shù)這三個為在BaseMvpActivity繼承的BaseActivity所定義的初始化的接口,有興趣可以看源碼济榨,這里只需關(guān)心MVP請求數(shù)據(jù)的邏輯坯沪,我們看到,在initData()初始化時候擒滑,直接可以調(diào)用Presenter獲取數(shù)據(jù)腐晾,并且在回調(diào)方法里面實時刷新。
三丐一、總結(jié)
??此時整個流程介紹完畢了藻糖,此時,在MVP的三個實現(xiàn)類中钝诚,均可以做到只關(guān)心業(yè)務邏輯而不需要去管其他颖御,遇到業(yè)務邏輯大改的時候,為了不影響原來的邏輯凝颇,可以新寫一個類替換原本的泛型類潘拱,不需要一發(fā)動全身,比如拧略,此時遇到Presenter大改芦岂,可以重新寫一個Presenter類的實現(xiàn)類,在Activity的泛型替換即可垫蛆,不需要修改View的實現(xiàn)類禽最,當然,你要是視圖也大改的時候袱饭,就只能全部推倒了川无,但是無論如何,此時我們的代碼結(jié)構(gòu)就會變得異常清晰虑乖。
??還有一點需要提醒懦趋,或許有人覺得我在這里用了反射會不會很影響性能,其實一開始我也在擔心這個問題疹味,后面經(jīng)過我的實戰(zhàn)測試仅叫,實際上在比較復雜的界面中也只多出了幾十毫秒帜篇,在人的感官上是根本感覺不出來,也有可能是反射在6.0以后的優(yōu)化诫咱,加上現(xiàn)在的機器性能太好了的原因笙隙,所以不需要有這方面的擔心,畢竟我不是在死循環(huán)反射(后期打算優(yōu)化為用注解的方式坎缭,提高性能)竟痰。
??同時這個基礎庫已經(jīng)開源,方便使用:
??1.確保項目的build.gradle
allprojects {
repositories {
...
maven { url 'https://jitpack.io' }
}
}
??2.在模塊中添加依賴幻锁,最新的版本請點擊下面的鏈接跳轉(zhuǎn)獲瓤痢:
dependencies {
implementation 'com.github.MrTangFB:MVPCommon:1.0.1'
}
??Demo源碼介紹并獲取最新版本
??該項目開源前是打算作為基礎公共模塊使用,后期還會不斷更新迭代哄尔。
??如果你有什么問題假消,請私信或者在下方留言給我以便交流,必回哦岭接。