转发请注明出处: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源码介绍并获取最新版本
该项目开源前是打算作为基础公共模块使用,后期还会不断更新迭代。
如果你有什么问题,请私信或者在下方留言给我以便交流,必回哦。
网友评论