我的Android项目架构进化论(二)

持续创作,加速成长!这是我参与「掘金日新计划 · 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基类作更正:
  1. Presenter层在回调View层的时候未进行空校验,可能存在空指针风险。当Activity被销毁时,Presenter未销毁,这时再回调view层接口时会发生空指针情况。故在回调时补充空校验。
  2. Presenter层持有View的实例未使用弱引用,可能存在内存泄漏风险。当无法执行Activity的onDestory,导致detachView不会执行,使用弱引用可以在GC时候回收未被销毁的View。

猜你喜欢

转载自juejin.im/post/7108012382655840286
今日推荐