Github地址:https://github.com/WangFion/mvp-mode
一褐隆、MVP 基本概念
??MVP 全稱:Model-View-Presenter 择同;MVP 是從經(jīng)典的模式MVC演變而來宋雏,它們的基本思想有相通的地方:Controller/Presenter負(fù)責(zé)邏輯的處理,Model提供數(shù)據(jù)冗酿,View負(fù)責(zé)顯示绪杏。
??談到這里,我們就不得不把它和MVC做一個(gè)對(duì)比了矫钓,首先看下圖:
??對(duì)于上圖,我們主要做以下幾點(diǎn)說明:
??1舍杜、MVP 模式下新娜, V 層和 P 層是分開定義的;而 MVC 模式下蝴簇,V 層和 C 層是混合定義的杯活,通常都在 Activity 或 Fragment 里面匆帚,故上圖把 V 和 P 圈在了一起熬词。
??2、MVP 模式下吸重,切斷了 V 層和 M 層的一個(gè)通訊互拾,他們之間的通訊必須經(jīng)過 P 層的控制,理論上 M 層是不允許持有 P 層的強(qiáng)引用嚎幸,故 M 層可以做到完全的獨(dú)立颜矿,因此 M 層的表現(xiàn)形式可以是本地 code、jar嫉晶、so骑疆、aar等等。而 MVC 模式下替废,M 層做不到完全獨(dú)立箍铭,M、V椎镣、C 三層之間耦合度較高诈火,很難應(yīng)對(duì)業(yè)務(wù)的擴(kuò)展。
??3状答、MVP 模式下冷守,理論上三層支持 N:N:N 的模式,但是鄙人建議三層之間的對(duì)應(yīng)關(guān)系是 1:1:N 的模式惊科,主要是從業(yè)務(wù)劃分和后期維護(hù)來看拍摇,至于具體的理由各位看官老爺就自行體會(huì)了。
??總的來看馆截,MVP 是在 MVC 的基礎(chǔ)上進(jìn)行了一個(gè)優(yōu)化充活,使得業(yè)務(wù)邏輯更加清晰,層次分明,降低了模塊層次耦合度堪唐。當(dāng)然了也不是沒有缺點(diǎn)巡语,像增加文件和接口個(gè)數(shù)等等,但鄙人認(rèn)為這點(diǎn)缺點(diǎn)相較于優(yōu)點(diǎn)來說可以忽略不記哈淮菠。
二男公、MVP 框架結(jié)構(gòu)
??首先,左側(cè)貫穿 M合陵、V枢赔、P 三層的包括 Application、工具類(Utils拥知、Tools)踏拜、實(shí)體類(Entitiy、java bean)低剔。Application就不說了整個(gè)項(xiàng)目的唯一對(duì)象速梗;工具類則是我們定義的一些工具,它和三層中的任何一層都不會(huì)有直接的聯(lián)系襟齿,故可以共同使用姻锁;而實(shí)體類則是數(shù)據(jù)的承載,用來在三層之間傳遞數(shù)據(jù)猜欺。
??其次位隶,最上層是我們的 View 層,主要用來顯示信息和做人機(jī)交互开皿,包括但不限于上圖所列項(xiàng)涧黄。這一層是直面用戶的、給人以真是感受的地方赋荆,故在 MVP 模式中此層主要專注于界面的顯示和事件的響應(yīng)而不會(huì)關(guān)心數(shù)據(jù)的具體信息以及業(yè)務(wù)的邏輯處理笋妥,所以在這層里面我們看到的是一系列類似 setXXX()、 showXXX()糠睡、hideXXX()挽鞠、onXXXListener() 的接口方法,它會(huì)持有一個(gè) Presenter 的引用狈孔,將一切的業(yè)務(wù)邏輯動(dòng)作都傳遞給 Presenter 處理信认。
??緊接著,View層之下的是我們的Presenter層均抽,此層主要是用來做業(yè)務(wù)邏輯處理和鏈接 M嫁赏、V兩層。在這層中我們看到的更多的是 switch-case油挥、if-else潦蝇、for款熬、while、toXXX等等一系列的邏輯處理和數(shù)據(jù)轉(zhuǎn)換等操作攘乒;另外贤牛,這層會(huì)持有 M、V兩個(gè)引用则酝,將邏輯和數(shù)據(jù)處理的結(jié)果通個(gè) V 層的引用傳遞到 View 來展示殉簸,通過 M 層的引用來獲取邏輯和數(shù)據(jù)處理過程中需要的數(shù)據(jù)服務(wù)支持。
??最后沽讹,最下層是我們的Modle層般卑,此層主要是提供數(shù)據(jù)服務(wù)支持的,這層包括但不限于我上圖所列的內(nèi)容爽雄。這層的設(shè)計(jì)我個(gè)人理解的有一個(gè)標(biāo)準(zhǔn):獨(dú)立于項(xiàng)目蝠检,怎么理解這個(gè)標(biāo)準(zhǔn)呢,套用毛主席的一句話:M 是項(xiàng)目的一塊磚挚瘟,哪里需要往哪搬叹谁,也就是說任何一個(gè)項(xiàng)目或者業(yè)務(wù)模塊需要,直接把這個(gè) Model 文件拷貝過去并且不需要做任何額外的工作就能直接使用刽沾,并且能正常工作本慕。因此在這層里面異步數(shù)據(jù)建議通過 callback、message等形式返回侧漓,對(duì)外部對(duì)象的持有一律采用 SoftReference 或者 WeakReference。
三监氢、MVP 架構(gòu)封裝
??上面的內(nèi)容布蔗,簡單的給大家介紹了一下關(guān)于 MVP 的基本信息,我相信仔細(xì)閱讀過上面內(nèi)容的童鞋心里已經(jīng)有了相關(guān)的概念浪腐。下面的內(nèi)容我會(huì)給大家分析一下我封裝的一個(gè) MVP 框架,從 code 層面讓大家對(duì) MVP 進(jìn)一步深入學(xué)習(xí),本框架分 Kotlin 和 Java 兩種編程語言摹察,雖然編程語言不一樣奶躯,但是基本思想和架構(gòu)是一樣的,我這里就不都分析了特漩,下面的內(nèi)容主要講解 Kotlin 版本的吧雹,Java 版本的大家可以去源碼自行查看。
??整個(gè)項(xiàng)目工程包含三個(gè) Module涂身。app Module 為項(xiàng)目業(yè)務(wù)模塊雄卷,在項(xiàng)目開發(fā)中用來編寫業(yè)務(wù)邏輯代碼,當(dāng)然了組件化開發(fā)模式下 app Module 一般會(huì)根據(jù)業(yè)務(wù)拆分出很多個(gè)子 Module蛤售,這里不是本文重點(diǎn)就不過多分析了丁鹉。mvp-java 和 mvp-kotlin 是本文分析的重點(diǎn)妒潭,也就是我封裝的 MVP 框架,所以接下來我就們開始詳細(xì)的分析 mvp-kotlin Module揣钦。
1雳灾、接口層
- 首先 Impl.kt 里面定義的是公共接口,內(nèi)容比較簡單大家一看就懂冯凹,這里就不詳細(xì)介紹了佑女。
- 接下來是 Presenter.kt,詳細(xì)內(nèi)容如下:
package com.wf.mvp.kotlin.impl
import com.wf.mvp.kotlin.customize.UiHandler
/**
* MvpMode -> com.wf.mvp.kotlin.impl -> IPresenter
* @Author: wf-pc
* @Date: 2020-05-10 15:24
* <p>
* Mvp's p-layer specification, used to limit the interfaces that the presenter must implement.
*/
interface Presenter : Impl {
fun <U: Ui> attachView(view: U, handler: UiHandler?)
fun detachView()
}
??此 Presenter 接口是所有 P 層都必須實(shí)現(xiàn)的頂層接口谈竿,它繼承至 Impl 接口团驱。這里定義了兩個(gè)最重要的接口方法 attachView 和 detachView,這兩個(gè)方法是用來供 V 層調(diào)空凸,使得 V 與 P 相互建立聯(lián)系嚎花,其中 attachView 綁定的 view 則是我們后面會(huì)講到的實(shí)現(xiàn)了 Ui 接口的 V 層的實(shí)例對(duì)象。
- 最后是 Ui.kt呀洲,詳細(xì)內(nèi)容如下:
package com.wf.mvp.kotlin.impl
/**
* MvpMode -> com.wf.mvp.kotlin.impl -> IView
* @Author: wf-pc
* @Date: 2020-05-10 15:26
* <p>
* Mvp's v-layer specification, used to limit the interfaces that the view must implement.
*/
interface Ui : Impl {
fun attachPresenter()
fun detachPresenter()
}
??此 Ui 接口則是所有 V 層都必須實(shí)現(xiàn)的頂層接口紊选,同 Presenter 接口一樣它亦繼承至 Impl 接口。這里定義了兩個(gè) attachPresenter 和 detachPresenter接口方法道逗,分別用來綁定和解綁 P 層的實(shí)例對(duì)象兵罢。
??總結(jié),以上便是接口協(xié)議層的所有內(nèi)容滓窍,這里規(guī)定了 V 層和 P 層的基礎(chǔ)協(xié)議規(guī)則卖词,使得 V 和 P 建立雙向聯(lián)系,后續(xù)具體的 View 和 Presenter 實(shí)例都是從這里派生出去吏夯。到這里你可能會(huì)問:為什么沒有 M 層的協(xié)議接口此蜈?如果有這樣的疑問那說明沒有認(rèn)真閱讀上兩節(jié)的內(nèi)容哦,前面我們講過 M 層是要做到獨(dú)立于項(xiàng)目噪生,那么它就不需要公共的協(xié)議裆赵,各自的數(shù)據(jù)服務(wù)實(shí)現(xiàn)各自的業(yè)務(wù)即可,另外 Presenter 下面還可能對(duì)應(yīng)于很多個(gè) M跺嗽,故 M 層沒有統(tǒng)一的接口協(xié)議战授,也不用必須于 P 層建立聯(lián)系。
2桨嫁、抽象層 Presenter
??其中 DefaultIPresenter 是一個(gè)空實(shí)現(xiàn)的 Presenter植兰,沒有什么可講的內(nèi)容。我們主要給童鞋們分析 IPresenter 的實(shí)現(xiàn)瞧甩。
package com.wf.mvp.kotlin.presenter
import ......
/**
* MvpMode -> com.wf.mvp.kotlin.presenter -> Presenter
* @Author: wf-pc
* @Date: 2020-05-10 16:13
* <p>
* Mvp's p-layer basic Presenter, used to attach activity and implement common functions.
*/
abstract class IPresenter<V : Ui> : Presenter {
/**
* The instance object of V layer.
*/
protected var mView: V? = null
/**
* The instance object of main thread handler.
*/
protected var mUiHandler: UiHandler? = null
/**
* Establish a reference relationship with the V layer.
*
* @param view The instance object of V layer, must be a subclass of Ui.
* @param handler The instance object of main thread handler.
*/
@Suppress("UNCHECKED_CAST")
override fun <U : Ui> attachView(view: U, handler: UiHandler?) {
mView = view as V;
mUiHandler = handler;
mUiHandler?.attachRefs(this)
init()
}
/**
* Release references to layer V.
*/
override fun detachView() {
release()
mUiHandler?.detachRefs()
mUiHandler = null
mView = null
}
/**
* Only initialization code can be written here, no other operations can be performed.
* For example, initialize the M layer instance object.
*/
protected abstract fun init()
/**
* Used to release resources.
*/
protected abstract fun release()
......
}
??上面的內(nèi)容注釋都寫的挺詳細(xì)的钉跷,IPresenter 抽象類里面持有了兩個(gè) V 層的實(shí)例對(duì)象 mView 和 mUiHandler。
??通過泛型方法 attachView 將 V 層的實(shí)例對(duì)象和 UI 線程的 Handler 對(duì)象傳遞到 P 層肚逸,使得 P 層持有了 V 層的引用爷辙,這樣 P 層就能通過 mView 對(duì)象來調(diào)用 V 層的接口方法了彬坏。而 detachView 則是用來與 V 層解除聯(lián)系和釋放相關(guān)的資源。
??在 attachView 和 detachView 里面分別預(yù)留了 init 和 release 抽象方法膝晾,子類通過實(shí)現(xiàn)這兩個(gè)接口來處理各自業(yè)務(wù)具體的初始化和釋放工作栓始。
??最后,省略掉的是 Impl 里面定義的公共接口血当,其中除了 onHandleMessage(msg: Message) 需要 Presenter 自己實(shí)現(xiàn)用來處理 Handler 消息以外幻赚,其余的全都是通過 mView 去調(diào)用了 V 層的實(shí)現(xiàn),其主要目的是方便 P 層使用臊旭。
3落恼、抽象層 View
??此層定義了四個(gè)類,其中 N 開頭的是指的不需要 P 層的業(yè)務(wù)模塊离熏,例如應(yīng)用的啟動(dòng)頁面等佳谦,在 MVP 模式下理論上是不推薦使用的,哪怕是定義一個(gè)空的 P 層滋戳,故本文我們不做詳細(xì)介紹钻蔑。而 IActivity 和 IFragment 的實(shí)現(xiàn)邏輯是一樣的,只不過一個(gè)是針對(duì) Activity奸鸯,一個(gè)是針對(duì) Fragment 而已咪笑,所以我們選擇分析 IActivity 即可。
package com.wf.mvp.kotlin.view
import ......
/**
* MvpMode -> com.wf.mvp.kotlin.view -> IActivity
* @Author: wf-pc
* @Date: 2020-05-10 16:42
* <p>
* Mvp's v-layer basic Activity, used to bind presenter and implement common functions.
*/
abstract class IActivity<P : Presenter> : Activity(), Ui {
/**
* The instance object of P layer.
*/
protected var mPresenter: P? = null
/**
* The instance object of UI thread handler.
*/
protected var mUiHandler: UiHandler? = null
private var mToast: Toast? = null
private var mLoading: ProgressDialog? = null
override fun onCreate(savedInstanceState: Bundle?) {
beforeCreate()
super.onCreate(savedInstanceState)
setContentView(bindLayoutId())
initView(intent)
attachPresenter()
initListener()
initData()
}
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
setIntent(intent)
}
override fun onDestroy() {
super.onDestroy()
detachPresenter()
release()
}
/**
* Binding P layer instance object.
* Obtain the generic class type through reflection, and then instantiate the Presenter object.
*/
@Suppress("UNCHECKED_CAST")
override fun attachPresenter() {
mUiHandler = UiHandler()
mUiHandler?.attachRefs(this)
mPresenter = try {
val type = this.javaClass.genericSuperclass as ParameterizedType
val tClass: Class<P> = type.actualTypeArguments[0] as Class<P>
tClass.newInstance()
} catch (e: Exception) {
DefaultIPresenter() as P
}
mPresenter?.attachView(this, mUiHandler)
}
/**
* Unbind the P layer instance object.
*/
override fun detachPresenter() {
mUiHandler?.detachRefs()
mPresenter?.detachView()
mUiHandler = null
mPresenter = null
}
/**
* Used to do something before Create.
*/
protected open fun beforeCreate() {}
/**
* Used to bind layout files.
*/
protected abstract fun bindLayoutId(): Int
/**
* Used to initialize view.
*/
protected abstract fun initView(intent: Intent?)
/**
* Used to initialize listener.
*/
protected abstract fun initListener()
/**
* Used to initialize data.
*/
protected abstract fun initData()
/**
* Used to release resources.
*/
protected abstract fun release()
/**
* It is not defined as an abstract function here,
* it is recommended to put the message to the Presenter layer for processing.
*/
override fun onHandleMessage(msg: Message) {}
......
}
??抽象 IActivity 繼承至 Activity 和實(shí)現(xiàn)了 Ui 接口娄涩,代表著 IActivity 擁有了 V 層的控制能力窗怒。同樣的,IActivity 持有了一個(gè) Presenter 的實(shí)例對(duì)象和一個(gè) UiHandler 實(shí)例對(duì)象钝满,在 V 層就是通過這個(gè) mPresenter 來調(diào)用 P 層的相關(guān)接口處理業(yè)務(wù)邏輯兜粘。
??首先,我們?cè)?onCreate 方法里面進(jìn)行了功能代碼模塊的劃分弯蚜,分別預(yù)留出了相應(yīng)的接口來供其子類實(shí)現(xiàn),各個(gè)接口是用來做什么的上面也有詳細(xì)的注釋剃法,就不一一翻譯了碎捺。
??接下來,我們著重分析一下 attachPresenter 和 detachPresenter贷洲,不知道大家還記得不收厨?這兩個(gè)接口是定義在接口層的 Ui 接口里面的。
在 attachPresenter 里面主要干了兩個(gè)事兒:
(1)實(shí)例化了 UiHandler 對(duì)象并將當(dāng)前對(duì)象也就是 this (這里要說明一下:抽象類是不能實(shí)例化的优构,因此是沒有 this 對(duì)象的诵叁,而這里的 this 其實(shí)是會(huì)向下轉(zhuǎn)型為它的具體的子類對(duì)象)傳遞給它,而在 UiHandler 里面會(huì)回調(diào) this 的 onHandleMessage 方法钦椭。
(2)通過反射獲取到泛型參數(shù)的 class 類型拧额,進(jìn)而通過 class 實(shí)例化了 mPresenter 對(duì)象碑诉,緊接著調(diào)用 mPresenter 的 attachView 將 this 和 mUiHandler 傳遞給 P 層,從而建立了 P 層和 V 層的綁定關(guān)系侥锦。其中进栽,當(dāng)反射出錯(cuò)的時(shí)候會(huì)默認(rèn)綁定一個(gè)空實(shí)現(xiàn)的 DefaultIPresenter。相應(yīng)的 detachPresenter 里面也干了兩個(gè)事兒:
(1)釋放 UiHandler 資源恭垦。
(2)斷開 V 層與 P 層的聯(lián)系快毛。
??最后,onHandleMessage(msg: Message) 有一段特殊的說明:建議消息處理放到 P 層去番挺,故此處采用了空實(shí)現(xiàn)唠帝。為什么這么建議呢?因?yàn)榇蠖鄶?shù)情況下消息也是用來處理業(yè)務(wù)邏輯的玄柏,只有極少數(shù)是用來作用于 UI的襟衰。其余省略掉的是 Impl 里面定義的公共接口,都很簡單禁荸,這里不做過多說明右蒲。
4、UiHandler
package com.wf.mvp.kotlin.customize
import android.os.Handler
import android.os.Message
import com.wf.mvp.kotlin.impl.Impl
/**
* MvpMode -> com.wf.mvp.kotlin.customize -> UiHandler
* @Author: wf-pc
* @Date: 2020-05-10 15:11
*/
class UiHandler: Handler(){
private var mRefsList = ArrayList<Impl>()
fun attachRefs(refs: Impl){
mRefsList.add(refs)
}
fun detachRefs(){
removeCallbacksAndMessages(null)
mRefsList.clear()
}
override fun handleMessage(msg: Message) {
super.handleMessage(msg)
for (refs in mRefsList){
refs.onHandleMessage(msg)
}
}
}
??UiHandler 是我們實(shí)現(xiàn)的一個(gè)自定義 Handler赶熟,這里有一點(diǎn)需要注意:我們只是取名為 UiHandler瑰妄,并不是說它一定就是 UI 線程的 Handler。
??其中我們通過一個(gè) List<Impl> 來存儲(chǔ)它所持有的外部引用映砖,通過 attachRefs 來添加引用间坐,detachRefs 來釋放引用。
??在 handleMessage 方法里面將消息回調(diào)給 List<Impl> 里面的引用的 onHandleMessage 方法邑退,因此我們這里的引用都必須是實(shí)現(xiàn)了 Impl 接口的對(duì)象竹宋。
??到這里,我們這個(gè)框架的封裝就講完了地技,整個(gè)框架主要是搭建了 V 層和 P 層的聯(lián)系蜈七,由于我們講過 M 層理論上是要獨(dú)立于項(xiàng)目,故不對(duì)其進(jìn)行封裝莫矗。后續(xù)我們?cè)陧?xiàng)目開發(fā)中只需要將 Activity 繼承至 IActivity 飒硅、Fragment 繼承至 IFragment、Presenter 繼承至 IPresenter 就可以使用 mView 和 mPresenter 來進(jìn)行相互調(diào)用了作谚。
四三娩、使用
1、索引 contract
package com.wf.mvp.mode.kotlin.contract
/**
* MvpMode -> com.wf.mvp.mode.kotlin.contract -> KotlinContract
*
* @Author: wf-pc
* @Date: 2020-05-10 17:55
*/
interface KotlinContract {
interface View {
fun setText(text: String?)
fun setTextColor(color: Int)
}
interface Presenter {
fun initText(text: String?);
fun getInfo()
}
}
??contract 翻譯過來就是合約的意思妹懒,他的功能就類似于我們字典的索引雀监,在我們不熟悉這個(gè)模塊的時(shí)候,我們只需要看這個(gè)文件就能大概知道這個(gè)模塊里面有些什么內(nèi)容眨唬。雖然不是必須的会前,但是強(qiáng)烈建議加上好乐。
2、V 層 KotlinIActivity
package com.wf.mvp.mode.kotlin.view
import ......
class KotlinIActivity : IActivity<KotlinIPresenter>(), KotlinContract.View {
override fun bindLayoutId(): Int {
return R.layout.activity_kotlin
}
override fun initView(intent: Intent?) {
}
override fun initListener() {
btn_kotlin.setOnClickListener {
mPresenter?.getInfo()
}
}
@SuppressLint("SetTextI18n")
override fun initData() {
// 獲取關(guān)聯(lián)的 P 層的具體 class 類型
mPresenter?.initText("${mPresenter?.javaClass}")
}
override fun release() {
}
override fun setText(text: String?) {
tv_kotlin.text = text ?: "null"
}
override fun setTextColor(color: Int) {
tv_kotlin.setTextColor(color)
}
}
??KotlinIActivity 為一個(gè)簡單的 V 層的實(shí)現(xiàn)回官,它實(shí)現(xiàn)了 IActivity 和 KotlinContract.View 索引曹宴,綁定了 KotlinIPresenter。我們可以看到歉提,除了我們封裝時(shí)預(yù)留的接口外笛坦,KotlinIActivity 只有兩個(gè) set 方法,這就是我們封裝的時(shí)候講到的 V 層只做 UI 的更新和響應(yīng)苔巨,具體的業(yè)務(wù)邏輯通過 mPresenter 交給 P 層去處理版扩。
3、P 層 KotlinIPresenter
package com.wf.mvp.mode.kotlin.presenter
import ......
/**
* MvpMode -> com.wf.mvp.mode -> MainPresenter
*
* @Author: wf-pc
* @Date: 2020-05-09 22:06
*/
class KotlinIPresenter : IPresenter<KotlinIActivity>(), KotlinContract.Presenter {
private var mText: StringBuilder = StringBuilder()
override fun init() {
}
override fun release() {
}
override fun onHandleMessage(msg: Message) {
if (msg.what == 1002) {
showLoading("1002")
}
}
override fun initText(text: String?) {
mText.clear()
mText.append(mView?.javaClass).append("\n")
mText.append(text).append("\n\n")
mView?.setText(mText.toString())
}
override fun getInfo() {
mText.append(getResources().toString()).append("\n")
mText.append(getString(R.string.app_name)).append("\n")
mText.append(getString(R.string.app_hello, "kotlin")).append("\n")
mText.append("dimen_720p=${getDimension(R.dimen.dimen_720p)}").append("\n")
mText.append(getDrawable(R.mipmap.ic_launcher).toString()).append("\n")
mView?.setText(mText.toString())
mView?.setTextColor(getColor(R.color.colorPrimary))
Thread(Runnable {
try {
Thread.sleep(3000);
} catch (e: InterruptedException) {
e.printStackTrace();
}
runOnUiThread {
showToast("Hello Kotlin !");
}
try {
Thread.sleep(3000);
} catch (e: InterruptedException) {
e.printStackTrace();
}
mUiHandler?.sendEmptyMessage(1002);
mUiHandler?.postDelayed({
hideLoading();
toggleKeyboard();
}, 3000)
mUiHandler?.postDelayed({
toggleKeyboard();
}, 6000)
}).start()
}
}
??KotlinIPresenter 為一個(gè)簡單的 P 層的實(shí)現(xiàn)侄泽,它實(shí)現(xiàn)了 IPresenter 和 KotlinContract.Presenter 索引礁芦,綁定了 KotlinIActivity。我們可以看到悼尾,除了我們封裝時(shí)預(yù)留的接口外柿扣,這里面做了很多的數(shù)據(jù)獲取和邏輯操作,最終通過 mView 將結(jié)果傳遞到 V 層顯示闺魏。
4未状、M 層實(shí)例
??我們說了 M 層是獨(dú)立于項(xiàng)目的,這里就不做演示實(shí)例了析桥,前面的內(nèi)容已經(jīng)講的很清楚了司草。
總結(jié)
??以上內(nèi)容就是我個(gè)人對(duì) MVP 模式的理解和簡單的封裝,模式或者架構(gòu)無非是一種規(guī)范或者約束泡仗,具體的實(shí)現(xiàn)仁者見仁智者見智埋虹,以上的內(nèi)容僅為本人的個(gè)人見解和思考,有不對(duì)的娩怎、可以優(yōu)化的或者更好的建議搔课,歡迎大家評(píng)論留言!
Github地址:https://github.com/WangFion/mvp-mode