浅谈Android中的架构模式——MVP(二)

  接上一篇博客讲到,MVC框架的耦合性使得Model层和View层仍然存在关联性,虽说分离了业务代码,但是这种耦合性的存在会随着业务的扩增导致维护的成本很高。那么为了实现解耦,也就是彻底把View层和Model层之间的这条关联线扯开,我们不妨试用一下MVP这个框架。

MVP框架

 

  这里可以和MVC框架的图对比一下,发现Model层和View层之间的连线是断开了,取而代之的是Model层业务在执行完任务后会告知Presenter,从而通过Presenter来控制View。也就是说,Model层并不会管View的后续操作,他只负责处理完业务,然后通知Presenter即完成了他的工作;至于View该如何变化(交互),那就交给Presenter来控制。所以,这里我的理解是Presenter充当的是一个中介的角色,负责两边协调。

  分析完流程,再来看看Android工程中与框架的对应关系。

  Model层:与MVC架构类似,是独立的业务模块,比如联网、访问数据库或者一些特定的业务操作;一般会有对应生成的接口供外部实现。

  View层:这里的View层指代的是Activity/Fragment,因为原本调用的业务都全部移至Presenter层去处理了,使得Activity/Fragment操作的都是布局内的View,职责变得更有针对性了。

  Presenter层:独立的业务逻辑代码,一般一个业务对应一个特定的Presenter,从而便于管理与维护;负责调用业务模块,同时持有View层的接口用来通知View进行更新。

  这里为了便于对比,同样拿登录模块来做例子进行分析。

  1. 用户在输入用户名和密码以及点击登录的操作这一步是属于View层的工作模块。
  2. 接而在点击登录后,会调用Presenter层的方法。
  3. Presenter层接收到信息后会调用Model层的业务代码。
  4. Model层接收到指令后,完成任务,并通过接口回调给外部,Model层完成其工作。
  5. Presenter层在Model层的接口回调时,会通知View来进行更新,Presenter层完成了其工作。
  6. View层在接口回调时,会对UI进行更新,View层完成了其工作。

  说了那么多,接下来看看代码如何实现吧!

  

  工程代码的话,可以对比一下前一篇博客,这里主要讲下View层的实现以及Presenter层的实现。

  首先我们要看下这个ILoginView的View层接口,这里定义了一系列关于LoginView的操作接口,从而方便Presenter层去调用。

interface ILoginView {
    fun showLoading()

    fun hideLoading()

    fun onLoginSuccess(msg: String?)

    fun onLoginError(errMsg: String?)

    fun getLoginRequestBean(): LoginRequestBean
}

  然后自然地,View层的Activity/Fragment要去实现这里的接口方法。

class MVPFragment : Fragment(), ILoginView {

    companion object {
        private const val TAG = "MVPFragment"
    }

    private lateinit var mRootView: View
    private lateinit var mEtUsername: EditText
    private lateinit var mEtPwd: EditText
    private lateinit var mBtnLogin: Button
    private lateinit var mProgressBar: ProgressBar
    private lateinit var mFragmentTag: TextView

    private lateinit var mLoginPresenter: LoginPresenter

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        mRootView = inflater.inflate(R.layout.layout_login, container, false)
        initViews()
        return mRootView
    }

    private fun initViews() {
        mEtUsername = mRootView.findViewById(R.id.et_username)
        mEtPwd = mRootView.findViewById(R.id.et_pwd)
        mBtnLogin = mRootView.findViewById(R.id.btn_login)
        mProgressBar = mRootView.findViewById(R.id.progress_bar)
        mFragmentTag = mRootView.findViewById(R.id.fragment_tag)

        mFragmentTag.text = TAG

        mLoginPresenter = LoginPresenter(this)
        mBtnLogin.setOnClickListener {
            mLoginPresenter.doLogin()
        }
    }

    override fun showLoading() {
        activity?.runOnUiThread {
            mProgressBar.visibility = View.VISIBLE
        }
    }

    override fun hideLoading() {
        activity?.runOnUiThread {
            mProgressBar.visibility = View.GONE
        }
    }

    override fun onLoginSuccess(msg: String?) {
        activity?.runOnUiThread {
            Toast.makeText(context, msg, Toast.LENGTH_SHORT).show()
        }
    }

    override fun onLoginError(errMsg: String?) {
        activity?.runOnUiThread {
            Toast.makeText(context, errMsg, Toast.LENGTH_SHORT).show()
        }
    }

    override fun getLoginRequestBean(): LoginRequestBean {
        return LoginRequestBean(mEtUsername.text.toString(), mEtPwd.text.toString())
    }
}

  接下来看下Presenter的实现。Presenter主要关键还是持有这个IView,在业务回调后去回调给View层对应的方法来更新UI,从而实现解耦的目的。

class LoginPresenter(private val mIView: ILoginView) {
    private lateinit var mLoginModel: LoginModel

    fun doLogin() {
        if (!paramChecked(mIView.getLoginRequestBean().username, mIView.getLoginRequestBean().pwd)) {
            mIView.onLoginError("请先填写完整")
            return
        }
        mIView.showLoading()
        mLoginModel = LoginModel()
        mLoginModel.login(mIView.getLoginRequestBean().username!!, mIView.getLoginRequestBean().pwd!!, object : NetCallback {
            override fun onSuccess(res: LoginResponseBean) {
                mIView.hideLoading()
                mIView.onLoginSuccess(res.msg)
            }

            override fun onError(errMsg: String?) {
                mIView.hideLoading()
                mIView.onLoginError(errMsg)
            }
        })
    }

    private fun paramChecked(username: String?, pwd: String?): Boolean {
        return !(username.isNullOrEmpty() || pwd.isNullOrEmpty())
    }
}

  对比MVC工程,MVP工程的特点就是将Model层和View层的联系断开了,改而通过Presenter这个中介来完成通信,从而实现了解耦的目的,便于整体工程的维护。缺点其实也比较容易想到,就是每个业务都对应一个Presenter、View等,那么类的定义就会非常非常多。

  那么,到这里的话,登陆模块的MVP“非标准”架构工程就实现了~

  之所以是非标准,因为Google官方的demo,是需要一个Contract的接口类对各业务模块的Presenter以及View接口进行管理;官方的考虑应该是为了便于业务进行一一对应,以免由于Presenter以及View的接口过多而导致维护成本变得巨大;同时,还会抽出一个BasePresenter(进行业务控制,如统一的联网操作接口由Base定义,具体Presenter实现)以及BaseView(进行一般的UI控制,如加载/取消加载等通用的操作接口的定义,由具体View实现)来供业务的Presenter/View进行继承。

  这里主要还是为了方便与之前的MVC工程对比,具体的标准工程可以参考Google官方的MVP架构工程,项目地址为:https://github.com/googlesamples/android-architecture. 把branch切换为todo-mvp即可。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值