網上有很多關於Android MVP開發模式的講解了,這裏就不詳細說了。這篇文章主要說一下怎麼搭建一個MVP框架,以及在搭建的過程中的一些注意事項等。因爲該框架是結合RxJava2+Retrofit2+MVP模式,所以需要注意的點還是挺多的,尤其是內存泄漏方面。廢話不多說,現在開始。
首先說一下怎麼搭建。首先我們肯定需要一個view的接口,讓我們項目中所有的Activity來實現該接口,這就是MVP中的V層。
interface IBaseView {
fun showLoading()
fun dissLoading()
}
例如,在上面的代碼中,我們定義了一個接口,裏面有基本的兩個操作方法,顯示加載和隱藏加載,這是每個View層都會涉及到的,所以放在基類中。然後我們再定義一個View接口,用於繼承該base接口,然後再定義該View接口中自己的邏輯。例如:
interface View : IBaseView {
fun showData(newsBean: NewsBean)
fun showError(msg: String)
}
在這個View接口中,繼承了上面定義的IBaseView接口,然後定義了自己的方法,顯示數據和顯示錯誤信息,最後我們的Activity只需要去實現這個View就好了,這個時候,我們的V層就算基本寫完了。
然後P層也是同樣的封裝邏輯。首先定義一個IBasePresenter接口,然後再定義一個Presenter繼承該接口。但P層的封裝又和V層的封裝有點不同,因爲它需要考慮到MVP模式下的內存泄漏的問題。因爲P層持有V層的引用,有業務邏輯操作,以及調用M層去請求網絡,如果這些事情沒有做完,這個時候Activity進行銷燬操作或者由於被系統自動回收,Activity的內存就釋放不了和無法回收,這就造成內存泄漏了,這裏有兩個機制來防止它內存泄漏的方式。首先是在初始化Activity的時候,讓V層和P層綁定,銷燬之後,它們解綁;第二就是在P層持有V層的弱引用對象。具體代碼如下:
interface IBasePresenter<T : IBaseView> {
fun attachView(view: T)
fun detachView()
fun isViewAttached() : Boolean
}
open class BasePresenter<T : IBaseView> : IBasePresenter<T> {
private var mRootView: T? = null
lateinit var mWeakReference: WeakReference<T>
override fun attachView(view: T) {
mWeakReference = WeakReference(view)
}
override fun detachView() {
mRootView = null
mWeakReference.clear()
}
override fun isViewAttached(): Boolean {
mRootView = mWeakReference.get()
return mRootView != null
}
}
在這裏,定義了一個IBasePresenter接口,裏面有三個方法,分別是和V層綁定,解綁和判斷是否已經綁定了,這在每個Activity中都需要用到,所以定義在基類中;然後再定義一個BasePresenter類實現該接口,在這個類裏面就用到了弱引用來包裹View;通過這兩種機制一般就能防止內存泄漏了。
最後我們定義和業務有關的Presenter來繼承BasePresenter,然後在調用業務邏輯方法之前判斷P層和V層是否綁定了,綁定了才走業務邏輯方法。相關代碼如下:
interface Presenter : IBasePresenter<View> {
fun loadData(params: ArrayMap<String, String>)
}
class NewsPresenter : BasePresenter<NewsContract.View>(), NewsContract.Presenter {
private val newsModel: NewsModel by lazy { NewsModel() }
override fun loadData(params: ArrayMap<String, String>) {
if (!isViewAttached()) {
return
}
newsModel.getNews(params, object : Callback<NewsBean, String> {
override fun onSuccess(successData: NewsBean) {
mWeakReference.get().let {
it?.showData(successData)
}
}
override fun onFail(failData: String) {
mWeakReference.get().let {
it?.showError(failData)
}
}
})
}
}
這裏的業務邏輯方法就是loadData這個方法,在loadData方法裏面,先去判斷了該P層有沒有和V層綁定,綁定了纔去調用M層的方法,最後當M層返回數據的時候,通過弱引用對象的get方法獲取到View,最後就能調用View中的方法了。
Model層就很簡單了,通過RxJava2+Retrofit2來進行網絡請求就好了。代碼如下:
class NewsModel {
fun getNews(params: ArrayMap<String, String>, callback: Callback<NewsBean, String>) {
RetrofitFactory.instance.service.getNews(params)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({
newsBean -> callback.onSuccess(newsBean)
},{
t ->
t.message?.let { callback.onFail(it) }
})
}
}
在這裏,有一個CallBack對象,這個是和P層之間通信的橋樑。當Model層返回數據的時候,使用CallBack對象返回數據給P層。當然,CallBack對象是在P層就實例化了。
interface Callback<K, V> {
fun onSuccess(successData: K)
fun onFail(failData: V)
}
CallBack有使用到泛型的原因是不知道返回的數據是什麼類型的,所以使用了泛型。
最後,我們定義一個BaseActivity,讓所有的Activity繼承它。
abstract class BaseActivity : AppCompatActivity(), IBaseView {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(layoutId())
attachView()
}
abstract fun layoutId() : Int
abstract fun attachView()
abstract fun detachView()
override fun onDestroy() {
super.onDestroy()
detachView()
}
}
在BaseActivity中,定義了兩個關鍵的方法,attachView和detachView,在這兩個方法裏面,調用P層定義的attachView和detachView方法,就實現了P層和V層的綁定了,最後在Activity的生命週期中調用即可。
如果嫌接口文件比較多,可以把View的接口和Presenter的接口放在一個契約類裏面。比如:
interface NewsContract {
interface View : IBaseView {
fun showData(newsBean: NewsBean)
fun showError(msg: String)
}
interface Presenter : IBasePresenter<View> {
fun loadData(params: ArrayMap<String, String>)
}
}
這樣就減少了項目中的接口文件數量。至此,一個用Kotlin+RxJava2+Retrofit2+MVP模式基本搭建完畢。每個人理解的MVP模式可能不一樣,所以寫法也可能有出入,不過大體思想就是這樣,所以不一定非要按照這種模式來寫。現在來說一下MVP模式的優缺點吧。
優點:每層職責清晰單一;Activity只關心自己的生命週期,無須再有業務邏輯相關的處理;代碼複用率提高(P層的邏輯可以被多個Activity使用);功能性測試脫於界面,便於測試
缺點:
當項目越來越大的時候,邏輯越來越多的時候,Presenter中除了邏輯以外,還有大量的View->Model,Model->View的邏輯操作,造成Presenter臃腫,維護困難。
UI和Presenter的交互會過於頻繁。一旦UI變動,Presenter也需要變
接口暴增,可以說代碼量成倍增長,交互都需要通過接口傳遞信息,讓人無法忍受
所以,爲了解決MVP模式的缺點,MVVM模式就誕生了。MVVM模式的開發,敬請參見下文:放棄了MVP,我選擇了MVVM。
以上是個人對該MVP的理解,有不對的地方,或者代碼有寫的不對的地方,麻煩指正,謝謝。
項目地址:https://github.com/AnyWayGobin/MVP_MVVM 如果有幫助,記得star哦,謝謝。