Kotlin Andorid MVP 架構搭建

鼬神鎮樓

Github地址:https://github.com/cn-ljb/mvp-kotlin -> Star 一下o(∩_∩)o

該篇文章主要講解,如何使用Kotlin語言搭建一個高效、解耦、快捷的Android MVP架構。

什麼是MVP架構?

簡單介紹下:

通常一般的Android項目結構,我們會在Activity\Fragment中編寫大量代碼,例如:網絡請求、數據填充、頁面切換等等,這種項目結構宏觀的稱之爲MVC。

MVC:我們可以把數據源(網絡請求、IO…)看作Model層,xml等佈局文件看作View層,Activity\Fragment看作Controller層。但在android中xml能力太薄弱了,以至於Activity不得不做很多本不屬於它的工作。

MVP:在MVP架構中Model層與MVC一樣作爲數據源,不過將Activity\Fragment都看作爲View層的一部分負責數據的展示和填充,將Model層與View層的關聯操作交給了Presenter層。

一個基本的MVP架構圖如下:

MVP

與之前的Android MVC相比,不僅Activity的分工不明確問題得到了解決,還帶來另一個好處:Model層與View層不再直接可見,耦合問題得到解決。

該項目MVP架構

在此基礎上,該項目中的MVP架構對每個模塊進行細化,大致架構圖如下:

Kotin-MVP

  • 1、View層根據自己的需要繼承對應的BaseMvpActivity\BaseMvpFragment\BaseMvpFragmentActivity,並實現createPresenter()函數,它們提供基礎的View層模版;

    /**
     *  1、繼承BaseMvpActivity
     *  2、通過泛型告訴View層,當前Presenter的契約接口LoginContract.IPresenter
     *  3、實現自己的契約接口LoginContract.IView
     */
    class LoginActivity : BaseMvpActivity<LoginContract.IPresenter>(), LoginContract.IView {
    
        override fun createPresenter() = LoginPresenter(this)
        ...
    }   
    
  • 2、Presenter層提供了基礎的IBasePresent接口模板,考慮到整個項目使用rxjava2作爲異步庫,爲了方便管理rx生命週期,額外提供了一個BaseRxLifePresenter抽象類;

    /**
     * 1、繼承BaseRxLifePresenter
     * 2、通過泛型告訴Presenter層,當前View的契約接口LoginContract.IView
     * 3、實現自身的契約接口LoginContract.IPresenter
     */
    class LoginPresenter(mvpView: LoginContract.IView) : BaseRxLifePresenter<LoginContract.IView>(mvpView), LoginContract.IPresenter {
    
        //rxjava生命週期管理舉例
        override fun delayGoHomeTask() {
            Observable.timer(1500, TimeUnit.MILLISECONDS)
                    .subscribe { getMvpView().goHome() }
                    .bindRxLifeEx(RxLife.ON_DESTROY)
        }
    
        //登錄功能
        override fun login(userName: String) {
            RxUtils.dispose(mLoginDisposable)
            mLoginDisposable = UserProtocol.getUserInfoByName(userName)
                    .subscribeOn(Schedulers.io())
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribeEx({
                        if (it.message.isNullOrBlank()) {
                            getMvpView().loginSuccess()
                        } else {
                            getMvpView().loginError(it.message)
                        }
                    }, {
                        getMvpView().loginError(null)
                    }).bindRxLifeEx(RxLife.ON_DESTROY)
        }
    }
    
  • 3、View層與Presenter層的交互通過接口進行解耦。例如上方的LoginActivity與LoginPresenter的交互都在LoginContract中進行限制;

    /**
     * 登錄頁View層\Presenter層通訊契約接口
     */
    interface LoginContract {
    
        interface IView : IBaseViewContract {
            fun loginSuccess()
            fun loginError(errorMsg: String?)
            fun goHome()
        }
    
        interface IPresenter : IBasePresenterContract {
            fun login(userName: String)
            fun delayGoHomeTask()
        }
    }
    
  • 4、Modle層封裝Protocol,用於包裝數據源並提供轉換爲Observable的函數,方便與Rxjava2結合使用(項目中DaoProtocol通過繼承BaseDAOProtocol實現,HttpProtocol通過Retrofit實現,這塊可以根據自己實際情況封裝),並實現自身向Presenter公開的約束接口;

    class UserDaoProtocol : BaseDaoProtocol(), IUserDaoProtocol {
    
        override fun saveUser(context: Context, user: User) = createObservable {
            saveUserImpl(context, user)
        }
    
        private fun saveUserImpl(context: Context, user: User): Boolean =
                if (findUserByUserIdImpl(context, user.id) == null) {
                    insertUserImpl(context, user)
                } else {
                    updateUserImpl(context, user)
                }
    
        ...
    }
    

    ————— 分割線 ——————

    //Model層對外提供的約束接口
    interface IUserDaoProtocol : DaoInterface {
        /**
         * 保存用戶信息
         * */
        fun saveUser(context: Context, user: User): Observable<Boolean>
    
        ...
    }
    
  • 5、每個Protocol對象建議通過Factory產出(DaoProtocol需在DaoFactoryConfig中進行配置,HttpProtocol得益於Retrofit自身的實現,不需要我們手動關聯工廠代碼),從而與Presenter進行解耦;

    object DaoFactoryConfig {
    
        //TODO  在此處配置DAO接口
        @Suppress("UNCHECKED_CAST")
        fun <T> configProtocol(clazz: Class<T>): T = when (clazz) {
            IUserDaoProtocol::class.java -> UserDaoProtocol()
            else -> throw IllegalStateException("NotFound Dao Interface Object  : ${clazz.name}")
        } as T
    }
    

    例如:通過DaoFactory獲取UserDAOProtocol的父級IUserDaoProtocol接口引用,而不是它的自身引用,避免直接操作接口約束之外的公共域:

    DaoFactory.getProtocol(IUserDaoProtocol::class.java).saveUser(context, user)
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe()

  • 6、一個View對應一個Presenter,View與Presenter交互通過Contract接口進行約束,一個Presenter可對應多個Modle對象,這些Modle對象通過Factory產出(Protocol,每個Protocol都應該是可複用,且內存安全的).

截圖:

演示

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