Android MVP架构

Android MVP架构

这是一个Kotlin +Okhttp3+Retrofit2+Rxjava2搭建的MVP初始App架构

希望您在看文章之前对MVP有所了解

1. MVP的三大角色

按照国际惯例先放上图片

在这里插入图片描述

从上图可以知道:View和Presenter可以相互持有,只有Presenter可以与model进行交互。

Model(数据层): model提供了数据的存取功能,数据可以来自网络或者本地。提供操作数据的方法给presenter层使用。

View(UI层):View层不需要写具体的逻辑,只关心UI操作,因为逻辑封装在了presenter中,View负责调用presenter的方法,然后执行相关的回调来操作UI。

Presenter(调度层): 连接model和View,进行相关的调度。 您的程序逻辑写在presenter中

2. MVP的架构优缺点

先吹一波。

优点:易于扩展,易于维护,易于测试,耦合度低,复用性高,程序健壮稳定等等

接受现实。

缺点:程序复杂性提高,代码文件变多,要解决内存泄漏问题(下文说明)

3. 动手前的架初始依赖

//retrofit2
implementation 'com.squareup.retrofit2:retrofit:2.1.0'
implementation 'com.jakewharton.retrofit:retrofit2-rxjava2-adapter:1.0.0'
implementation 'com.squareup.okhttp3:logging-interceptor:3.1.2'

//Rxjava
implementation 'io.reactivex.rxjava2:rxjava:2.0.1'
implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'
implementation 'com.contrarywind:Android-PickerView:3.2.4'
implementation 'com.squareup.retrofit2:converter-gson:2.0.2'

4.从登录案例中来用MVP

项目整体代码
在这里插入图片描述

解决MVP中的内存泄漏:

由于Presenter经常性的需要执行一些耗时操作,那么当我们在操作未完成时候关闭了Activity,会导致Presenter一直持有Activity的对象,造成内存泄漏。有人可能觉得,我直接在activity中onDestory中解除Presenter和activity的关系不就行了吗?但是并不是所有情况下,onDestory都会被正常调用。

我们需要通过弱引用Activity来解决这个问题

先建立一个BasePresenter

/**
 * 弱引用View 防止内存泄漏
 * View的类型通过泛型类型传递进来,Presenter对这个View持有弱引用
 */
abstract class BasePresenter<T> {

    protected var mViewrRef: Reference<T>? = null

    protected val view: T
        get() = mViewrRef!!.get()!!


    val isAttachView: Boolean
        get() = null != mViewrRef && null != mViewrRef!!.get()

    /**
     * 与View建立关联
     */
    fun attachView(view: T) {
        mViewrRef = WeakReference(view)
    }

    /**
     * 解除与View的关联
     */
    fun detachView() {
        if (null != mViewrRef) {
            mViewrRef!!.clear()
            mViewrRef = null
        }
    }
}

创建一个BaseActivity基类,通过其生命周期来控制它与Presenter的关系

/**
两个泛型参数:第一个是View的接口类型,第二个是Presenter的具体类型
在oncreate()中会构建一个具体的Presenter,构建presenter后调用attachView与activity建立关联
在onDestory中解除对activity的引用。
这样子即使在onDetory()没有被正常调用的情况下。弱引用对象也能及时回收
*/
abstract class BaseActivity<V, T : BasePresenter<V>> : AppCompatActivity(){

    protected var mPresenter: T? = null
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        mPresenter = createPresenter()
        mPresenter?.attachView(this as V)
    }

    override fun onDestroy() {
        super.onDestroy()
        mPresenter?.detachView()
    }

    //创建一个Presenter(
    protected abstract fun createPresenter(): T
    //每个子类的初始化方法
    protected abstract fun init()
    
    /**
     * toast
     */
    protected fun Any.toast(context: Context, duration: Int = Toast.LENGTH_SHORT): Toast {
        val makeText = Toast.makeText(context, this.toString(), duration)
        makeText.setGravity(Gravity.CENTER, 0, 0)
        return makeText.apply { show() }
    }
  }

基类建立完毕后我们就可以用登录案例来模拟实现MVP了。

登录界面如下:
在这里插入图片描述

View层:MainActivity主要实现按钮点击登录和View接口的回调,进行一个UI操作

class MainActivity : BaseActivity<MainActivity, LoginPresenter>(), ILoginView{

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        init()
    }


    /**
     * 创建你的LoginPresenter
     */
    override fun createPresenter(): LoginPresenter {
       return LoginPresenter(this)
    }

    /**
     * 初始化方法
     */
    override fun init() {
        //按钮点击
        btnLogin.setOnClickListener {
            mPresenter!!.login(edtUserName.text.toString(),edtPassword.text.toString())
        }
    }

    /**
     * View的回调方法
     * 提示相关信息
     */
    override fun showInfoStr(msg: String) {
        msg.toast(this)
    }

    /**
     * View的回调方法
     * 登录成功回调
     */
    override fun loginSuccess(it: User) {
    }
}

Presenter层:具体的业务逻辑和连接View和model。这里主要使用了Rxjava

class LoginPresenter(private val loginView: ILoginView) : BasePresenter<MainActivity>() {

    private val mainManger = LoginModel()

    //LoginInterface
    @SuppressLint("CheckResult")
    fun login(userName: String, password: String) {

        mainManger.login(userName, password)
                .subscribeOn(Schedulers.newThread())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe({
//                    if("成功"){
//                        loginView.loginSuccess(it)
//                    }else{
//                        loginView.showInfoStr("错误信息")
//                    }
                }, {
                    loginView.showInfoStr(it.message!!)

                })
    }
}

Model层:进行接口API的调用,返回一个Observable 给Presenter进行进一步的处理

/**
 * 登录模块的model
 */
class LoginModel: ILoginModel{
    override fun login(userName: String, password: String): Observable<User> {
        return RetrofitClient.getApi().login(userName, password)
    }
}

项目结构相对清晰简单,更多的可以参照项目源码:
GitHub传送门

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值