美文网首页Android开发经验谈Android开发Android技术知识
Android MVP框架如何快速构建及一P对多M优化

Android MVP框架如何快速构建及一P对多M优化

作者: MrTangFB | 来源:发表于2019-06-15 18:26 被阅读30次

      转发请注明出处:https://www.jianshu.com/p/b3e15cbf83a9
      开发android的几年时间,从一开始接触到MVP模式,就对于代码逻辑分离管理产生了浓厚的兴趣,对官方MVP模式进行研究与实战,发现在实际应用中,官方MVP模式不能满足绝大部分场景,近一年来,在不断的应用中,对MVP模式进行了改进,本文将对其一一解说,如果有更好的做法,还望能一起探讨。
      MVP模式的优势与弊端之类的,网上已经有太多的文章,不再对其进行述说,本文只对项目是如何构造的进行述说与解释:

    一、MVP三部分简述

    1.Model的基类
    abstract class BaseMvpModel {
    
        private var mContext: Context? = null
        fun getContext(): Context? {
            return mContext
        }
        /*************分界线以上为外部使用的方法,以下为私有或生命周期方法不应主动调用*****************/
        fun onCreate(context: Context?) {
            mContext = context
        }
        fun onDestroy() {
            mContext = null
        }
    }
    

      Model专门与数据打交道,此处会维护一个成员变量Context,Context由Presenter生成Model时传入,Context的生命周期跟界面视图生命周期保持一致,onCreate()与onDestroy()方法均为框架自动调用,调用者只需在生命周期内调用getContext()方法即可获取到Context对象,保持封闭性。
      此处维护Context的原因,是在于处理或者获取数据时,并不是所有数据均从网上获取,还有很多是本地数据,如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
        }
    
        /*************分界线以上为外部使用的方法,以下为私有或生命周期方法不应主动调用*****************/
    
        /**
         * 保存所有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使用泛型,类型转换都是自动和隐式的,提高代码的重用率。
      Presenter内部维护的Context与View视图,由Activity生成Presenter时传入,同样与界面视图生命周期保持一致,Context是为了生成Model时动态传参,有时候也需要在此处使用,View则不必多说,用于视图的回调。
      此处唯一特殊的就是在对Model的维护上,使用了泛型与反射的机制,由于经常会有多个不同的Model,所以此处用一个ArrayMap来统一管理Model,获取时调用getModel()方法,如果ArrayMap里面没有则会反射生成(泛型约束不存在强转换错误问题),同时保存在ArrayMap里面以便复用,所有的Model均会在界面视图销毁时统一销毁,外部只需要注意在生命周期内调用即可。

    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
        }
    
        /*************分界线以上为外部使用的方法,以下为私有或生命周期方法不应主动调用*****************/
    
        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已经定义好要传入的视图实例,P泛型定义为必须是BaseMvpPresenter的子类,注意mPresenter?.attach(this as V)此代码同时也约束了View视图必须同时实现V,否则运行时会类型转换异常。
      初始化Presenter时同样使用泛型与反射的机制动态生成(泛型约束不存在强转换错误问题),且与界面视图生命周期一致,外部只需要注意在生命周期内调用getPresenter()获取即可。
      为什么定义V泛型:View视图必须实现V泛型,关联上Presenter需求的V泛型,实际上V泛型就是View视图的接口,定义了刷新视图的方法,关联之后能在Presenter直接调取从而实现刷新界面。

    二、实际使用场景

      说得再多,不如直接使用看看效果如何来得实在,我们下面来看下每个模块的实现类,应用场景属于我最常遇到的一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只是一个回调接口,里面有成功或失败的回调方法。
      这里我们简单定义了几个方法,展示标题与展示用户数据,假设展示标题是该界面特有,展示用户数据是所有界面共有,此时ICommonContract为所有界面共用。

    2.Model的实现类
    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的实现类比较简单,这里只为了示例用法,所以都直接返回字符串,同样CommonModel为所有界面共用。

    3.Presenter的实现类
    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的实现类是重点,此时前面所做的一切,价值就提现出来了,Presenter的实现类,仅需要重写业务逻辑所定义的方法,实现只关心业务逻辑而并不需要操心其余有关生命周期的事情,并且我们在这里也看到,只需要传对应Model的Class就能使用其内部的方法,getView()也能直接调用对应的方法,不需要再进行强转,这就是泛型的好处,一心关注业务逻辑

    3.View的实现类
    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的实现类是同样也是重点,mContentViewRes变量、initView()函数、initData()函数这三个为在BaseMvpActivity继承的BaseActivity所定义的初始化的接口,有兴趣可以看源码,这里只需关心MVP请求数据的逻辑,我们看到,在initData()初始化时候,直接可以调用Presenter获取数据,并且在回调方法里面实时刷新。

    三、总结

      此时整个流程介绍完毕了,此时,在MVP的三个实现类中,均可以做到只关心业务逻辑而不需要去管其他,遇到业务逻辑大改的时候,为了不影响原来的逻辑,可以新写一个类替换原本的泛型类,不需要一发动全身,比如,此时遇到Presenter大改,可以重新写一个Presenter类的实现类,在Activity的泛型替换即可,不需要修改View的实现类,当然,你要是视图也大改的时候,就只能全部推倒了,但是无论如何,此时我们的代码结构就会变得异常清晰。
      还有一点需要提醒,或许有人觉得我在这里用了反射会不会很影响性能,其实一开始我也在担心这个问题,后面经过我的实战测试,实际上在比较复杂的界面中也只多出了几十毫秒,在人的感官上是根本感觉不出来,也有可能是反射在6.0以后的优化,加上现在的机器性能太好了的原因,所以不需要有这方面的担心,毕竟我不是在死循环反射(后期打算优化为用注解的方式,提高性能)。

      同时这个基础库已经开源,方便使用:
      1.确保项目的build.gradle

        allprojects {
            repositories {
                ...
                maven { url 'https://jitpack.io' }
            }
        }
    

      2.在模块中添加依赖,最新的版本请点击下面的链接跳转获取:

        dependencies {
                implementation 'com.github.MrTangFB:MVPCommon:1.0.1'
        }
    

      Demo源码介绍并获取最新版本
      该项目开源前是打算作为基础公共模块使用,后期还会不断更新迭代。
      如果你有什么问题,请私信或者在下方留言给我以便交流,必回哦。

    相关文章

      网友评论

        本文标题:Android MVP框架如何快速构建及一P对多M优化

        本文链接:https://www.haomeiwen.com/subject/dyqufctx.html