Android MVP架构实战

1,496 阅读3分钟

这是我参与8月更文挑战的第7天,活动详情查看:8月更文挑战

Android MVP架构实战

MVP架构是为了让各个模块之间降低耦合,方便维护,也可以让代码更简洁,让代码简洁的意思是让代码更清晰,并不是让代码更少;MVP契约者是为了进一步的低耦合、接口统一管理。 本文分为三部分:MVP入门、MVP契约者、MVP契约者封装,适合初学者,带你一步步打造实用的MVP架构。

一、图解MVP

以前写JSP的时候,经常使用MVC架构,写着很爽,一层一层自上而下,直到操作数据库,但是后来写Android之后,Activity又当做页面又处理逻辑,显得很臃肿,使用MVP后,Activity只当做页面,逻辑全部放在Presenter层处理,这样一来,逻辑就清晰了很多。

image.png

二、MVP的思想

在Eclipse年代,我们把逻辑操作、请求网络数据、更新视图全部放在了Activity中,这样Activity就会越来越臃肿,臃肿到一个代码能有好几千行代码,这对开发者来说显然是不能接受的。

后来,我们把公共的方法类提取出来,把网络请求部分提取出来,这样减少了Activity的负担,但对于需求的变动,我们改动仍然很大。

了解了MVP之后,才完全感受到代码结构原来可以这么爽。

虽然每个Activity我们都需要构建view、presenter、model,但代码的整洁了太多。 相当于一个房间摆放了很多箱子,把物品归类在这些箱子里。

MVP对接口灵活的调用可以轻松的应对产品的变更。

presenter 类与View 和 Model 通信,做到视图和逻辑的解耦。


graph TD

Presenter

Presenter --> View

Presenter --> Model

三、MVP实战

eg: 登陆功能

3.1 项目结构

  • callback(回调)

   LoginCallBack(接口,处理网络请求的回调的接口)

  • model(M层:处理网络和数据持久化等)

   ILoginModel(接口,定义model中需要使用的方法)

   LoginModel(类,实现ILoginModel接口,处理网络和数据持久化)

  • view(V层:用于显示界面)

   ILoginView(接口,定义界面中需要使用的方法)

   LoginActivity(类,实现ILoginView接口,显示界面)

  • presenter(P层,用于控制逻辑和衔接M和V)

   LoginPresenter(类,衔接M和V并控制逻辑)

说明:

V 调用 P,P 操作 V,P 调用 M,M 回调给 P

3.2 代码结构

activity_login.xml

布局文件


<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

    xmlns:tools="http://schemas.android.com/tools"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    android:orientation="vertical"

    tools:context=".mvp_normal.view.LoginActivity">

    <EditText

        android:id="@+id/et_name"

        android:layout_width="match_parent"

        android:layout_height="wrap_content" />

    <EditText

        android:id="@+id/et_password"

        android:layout_width="match_parent"

        android:layout_height="wrap_content" />

    <Button

        android:id="@+id/btn_login"

        android:layout_width="match_parent"

        android:layout_height="wrap_content"

        android:onClick="Login"

        android:text="Login" />

</LinearLayout>

LoginActivity.java


package com.mvp.demo.mvp_normal.view;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;

import android.view.View;

import android.widget.EditText;

import android.widget.Toast;

import com.mvp.demo.R;

import com.mvp.demo.mvp_normal.presenter.LoginPresenter;

/**

 * note: MVP

 */

public class LoginActivity extends AppCompatActivity implements ILoginView {

    private EditText etName, etPassword;

    private LoginPresenter mLoginPresenter;

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_login);

        etName = findViewById(R.id.et_name);

        etPassword = findViewById(R.id.et_password);

        mLoginPresenter = new LoginPresenter(this);

    }

    public void Login(View view) {

        mLoginPresenter.login(etName.getText().toString(), etPassword.getText().toString());

    }

    @Override

    public void showToastFailed() {

        Toast.makeText(this, "登陆失败", Toast.LENGTH_LONG).show();

    }

    @Override

    public void showToastSuccess() {

        Toast.makeText(this, "登陆成功", Toast.LENGTH_LONG).show();

    }

}

ILoginView

需要显示界面的方法


package com.mvp.demo.mvp_normal.view;

public interface ILoginView {

    void showToastFailed();

    void showToastSuccess();

}

LoginPresenter

用于控制逻辑和衔接M和V,用构造当法衔接 M 和 V


package com.mvp.demo.mvp_normal.presenter;

import com.mvp.demo.mvp_normal.callback.LoginCallBack;

import com.mvp.demo.mvp_normal.model.ILoginModel;

import com.mvp.demo.mvp_normal.model.LoginModel;

import com.mvp.demo.mvp_normal.view.ILoginView;

public class LoginPresenter implements LoginCallBack {

    private ILoginView mLoginView;

    private ILoginModel mLoginModel;

    //用构造当法衔接 M 和 V

    public LoginPresenter(ILoginView loginView) {

        this.mLoginView = loginView;

        mLoginModel = new LoginModel(this);

    }

    public void login(String name, String password) {

        mLoginModel.login(name, password);

    }

    @Override

    public void success() {

        mLoginView.showToastSuccess();

    }

    @Override

    public void failed() {

        mLoginView.showToastFailed();

    }

}

LoginModel

处理网络请求的类,用LoginCallBack回调结果


package com.mvp.demo.mvp_normal.model;

import com.mvp.demo.mvp_normal.callback.LoginCallBack;

public class LoginModel implements ILoginModel {

    private LoginCallBack mCallBack;

    // 此处用构造方法 将P 和 M 连接起来,具体的连接就是接口

    public LoginModel(LoginCallBack callBack) {

        this.mCallBack = callBack;

    }

    @Override

    public void login(String name, String password) {

        //假设请求了网络, 假设用户名和密码一样为校验成功; 如果是异步网络请求,再写个接口回调

        if (name.equals(password))

            mCallBack.success();

        else

            mCallBack.failed();

    }

}

ILoginModel

定义model中需要使用的方法


package com.mvp.demo.mvp_normal.model;

public interface ILoginModel {

    void login(String name, String password);

}

LoginCallBack

请求结果的回调


package com.mvp.demo.mvp_normal.callback;

public interface LoginCallBack {

    void success();

    void failed();

}

上诉的架构很清晰,却因为接口过多显得累赘,MVP的契约者(Contact)主要统一了接口管理,让代码看起来更清晰。

示例契约类


package com.mvp.demo.mvp_contract.contract;

/**

 * @author by Talon, Date on 2019-10-27.

 * note: 契约类,统一接口管理

 */

public class LoginContact {

    public interface presenter {

        void login(String name, String password);

    }

    public interface view {

        void showToastFailed();

        void showToastSuccess();

    }

    public interface loginCallBack {

        void success();

        void failed();

    }

}