这是我参与8月更文挑战的第7天,活动详情查看:8月更文挑战
Android MVP架构实战
MVP架构是为了让各个模块之间降低耦合,方便维护,也可以让代码更简洁,让代码简洁的意思是让代码更清晰,并不是让代码更少;MVP契约者是为了进一步的低耦合、接口统一管理。 本文分为三部分:MVP入门、MVP契约者、MVP契约者封装,适合初学者,带你一步步打造实用的MVP架构。
一、图解MVP
以前写JSP的时候,经常使用MVC架构,写着很爽,一层一层自上而下,直到操作数据库,但是后来写Android之后,Activity又当做页面又处理逻辑,显得很臃肿,使用MVP后,Activity只当做页面,逻辑全部放在Presenter层处理,这样一来,逻辑就清晰了很多。
二、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();
}
}