网上有很多关于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 {
fun attachView(view: T)
fun detachView()
fun isViewAttached() : Boolean
}
open class BasePresenter : IBasePresenter {
private var mRootView: T? = null
lateinit var mWeakReference: WeakReference
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 {
fun loadData(params: ArrayMap)
}
class NewsPresenter : BasePresenter(), NewsContract.Presenter {
private val newsModel: NewsModel by lazy { NewsModel() }
override fun loadData(params: ArrayMap) {
if (!isViewAttached()) {
return
}
newsModel.getNews(params, object : Callback {
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, callback: Callback) {
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 {
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 {
fun loadData(params: ArrayMap)
}
}
这样就减少了项目中的接口文件数量。至此,一个用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哦,谢谢。