持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第13天,点击查看活动详情
前言
上一篇我们了解了MVC业务架构在项目中的使用,这一篇我们来了解MVP业务架构。MVP的使用还是挺长的,那时候公司业务变得不再单一,一个界面所承载的业务和功能都有大幅度的提升,所以就我们开始向MVP的业务架构进化。
MVP时代
MVP作为MVC的演化而来的业务架构,它解决了MVC不少的问题,就Android而言,MVP的中的Model层和MVC是一样的,而Activity不再包含业务逻辑,而是纯粹的UI逻辑,所有业务逻辑相关的全部交由presenter层处理。
职能
类型 | 职能 |
---|---|
M-Model | 模型层,主要用于数据的处理及存储,像网络请求和本地存储数据请求及处理等。 |
V-View | 视图层,主要用于UI样式的呈现及处理,像xml布局和Activity中的控件及其展示效果等。 |
P-Presenter | 逻辑层,主要用于View层和Model层交互处理,像数据接口回调等 |
实例展示
假设我们需要展示某个列表数据
- Model
class ListModel{
fun getListDataByNet(callBack:MVCCallBack){
thread {
val okHttpClient = OkHttpClient()
val request = Request.Builder().get().url("https://api/getListDataByNet").build()
okHttpClient.newCall(request).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
callBack.onFailure("失败")
}
override fun onResponse(call: Call, response: Response) {
callBack.onSuccess(Gson().fromJson(response.body?.string(), ListEntity::class.java))
}
})
}
}
}
复制代码
- 接口
interface IMvpPresenter{
// 获取网络数据
fun getListDataByNet()
}
interface IMvpView: IView{
// 获取数据成功
fun showListDataByNet(list: MutableList<DataEntity>)
// 获取数据失败
fun showErrorMsg(msg:String)
}
interface IView{}
复制代码
- Base
abstract class BasePresenter<V> {
// 补充弱引用
protected var viewReference: Reference<V>? = null
// 获取view,Presenter回调View层时需要做一次空校验
protected fun getView(): V? {
return viewReference?.get()
}
// 绑定
fun attachView(view: V) {
viewReference = WeakReference<V>(view)
}
// 销毁view
fun detachView() {
if (viewReference != null) {
viewReference?.clear()
viewReference = null
}
}
}
abstract class MVPBaseActivity<V, P : BasePresenter<V>?> : AppCompatActivity() {
protected var presenter: P? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
presenter = bindPresenter()
presenter!!.attachView(this as V)
}
// 页面销毁时调用presenter销毁
override fun onDestroy() {
super.onDestroy()
presenter!!.detachView()
}
abstract fun bindPresenter(): P
}
复制代码
- View 这里省略Adater和RecyclerView的实现
class MAPActivity : MVPBaseActivity<IMvpView, MvpPresenter>(),IMvpView {
private lateinit var adapter: MVPListAdapter
private var list :MutableList<DataEntity> = mutableListof()
private var rv:RecyclerView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_mvp)
initView()
presenter?.getListDataByNet()
}
// View实现,包括Adpater和RecyclerView的实现
private fun initView() {
val lm = LinearLayoutManager(this)
rv.layoutManager = lm
rv.addItemDecoration(DividerItemDecoration(this, DividerItemDecoration.VERTICAL))
adapter = MVPListAdapter(list, this)
rv.adapter = adapter
}
// View 层持有Presenter层
override fun bindPresenter(): MvpPresenter = MvpPresenter()
// View接口
override fun showListDataByNet(list: MutableList<DataEntity>){
this.list = list
adapter.notifyDataSetChanged()
}
// View接口
override fun showErrorMsg(msg:String){
Toast.makeText(this, msg Toast.LENGTH_SHORT).show()
}
}
复制代码
- Presenter
class MvpPresenter:BasePresenter<IMvpView>(),IMvpPresenter{
override fun getListDataByNet(){
ListModel().getListDataByNet(object : MVCCallBack {
override fun onSuccess(listEntity: ListEntity) {
// 补充空校验
getView()?.showListDataByNet(listEntity.list)
}
override fun onFailure(errorMsg: String) {
getView()?.showErrorMsg(errorMsg)
}
})
}
}
复制代码
理解
当用户触发View
层逻辑事件时会调用Presenter
层的getListDataByNet
事件,接下来Presenter
层会通知Model
层进行网络数据的获取,当Model
层的Api产生回调时,会通过Presenter
层让View
层进行数据列表的展示。
看起来就像是我们将MVC
中的Controller
简单的变成了Presenter
层,但是可以看到 :
- 原本在
Controller
中实现的逻辑交给了一个独立的Presenter
实现类; Presenter
层所持有的并不是Activity
这个对象,而是IMvpView
接口,也就是说当我们存在多个Activity
业务相同时我们完全可以复用这个Presenter
实现类来达到我们的需求。
优点
- 复杂业务可以放到Presenter层进行处理,降低了Activity的臃肿。
- 将Model层和View层完全解耦,调用Model层交给了Presenter层处理,同时调整View层的业务不再影响Model层的实现。
- 职能更加清晰,View层专注实现UI的呈现,不关心业务逻辑,Presenter层专注业务逻辑的实现,并担任View和Model之间的桥梁,Model层还是专注数据的获取与存储。
- IMvpView接口的实现更加方便了单元测试,保证代码的健壮性。
缺点
- 复杂业务情况下存在大量的Presenter层到View层的视图逻辑回调,Model层对Presenter层的回调也是没有解决,所有造成Presenter层业务的臃肿,降低代码可读性。
- 正常情况下我们一个Activity对应一个View层接口,很难实现多个Activity对Presenter的复用,增加了前期业务规划的难度。
总结
对于MVP这种业务架构而言,它的优点便是职能划分明确,易于业务维护,但是它的缺点就是Presenter层和View层的交互非常频繁,接口众多,一旦项目复杂度上来Presenter层的臃肿就随之而来了。同时有个非常致命的问题,由于Presenter层无法感知Activity的生命周期,当Activity被销毁的同时我们需要手动销毁Presenter层,否则就会造成内存泄漏问题。尽管我们通过Base层这种实现手段来处理这类问题,但本质上MVP这种业务架构的诟病依旧是无法得到解决。
为了解决上述 MVP 模式存在的问题,减少Presenter层和View层的交互,所以就有了 MVVM 模式。下一篇我们将一起看看MVVM又是什么样的,也是我们重点要谈的业务架构。
更新
- 有掘友指出文章代码不严谨,补充两个base基类作更正:
- Presenter层在回调View层的时候未进行空校验,可能存在空指针风险。当Activity被销毁时,Presenter未销毁,这时再回调view层接口时会发生空指针情况。故在回调时补充空校验。
- Presenter层持有View的实例未使用弱引用,可能存在内存泄漏风险。当无法执行Activity的onDestory,导致detachView不会执行,使用弱引用可以在GC时候回收未被销毁的View。