Android MVP 從入門到放棄

網上有很多關於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哦,謝謝。 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章