以下是一个 Android MVP 模式的完整示例,以「用户登录功能」为核心场景,包含 Model、View、Presenter 三层架构,代码简洁且符合 Android 开发规范。
一、MVP 架构分层说明
- Model:负责数据处理(网络请求、本地存储等)
- View:负责界面展示和用户交互(Activity/Fragment)
- Presenter:作为 View 和 Model 的中间层,处理业务逻辑,解耦视图和数据
二、完整代码实现
1. 基础接口定义
(1) 通用回调接口(处理异步请求结果)
java
运行
// BaseCallback.java
public interface BaseCallback<T> {
// 请求成功
void onSuccess(T data);
// 请求失败
void onFailure(String errorMsg);
}
(2) View 层接口(定义登录界面的行为)
这个interface LoginView 很关键,可以省很多Ui层的代码,在P层处理数据和UI各种状态的关联
连接p层和view层的桥梁
// LoginView.java
public interface LoginView {
// 显示加载中
void showLoading();
// 隐藏加载中
void hideLoading();
// 登录成功回调
void onLoginSuccess(String msg);
// 登录失败回调
void onLoginFailure(String errorMsg);
// 获取输入的用户名
String getUsername();
// 获取输入的密码
String getPassword();
}
(3) Presenter 层接口(定义登录业务逻辑)
java
运行
// LoginPresenter.java
public interface LoginPresenter {
// 执行登录逻辑
void login();
// 销毁资源(防止内存泄漏)
void detachView();
}
2. Model 层实现(模拟登录数据请求)
java
运行
// LoginModel.java
public class LoginModel {
// 模拟网络请求登录
public void login(String username, String password, BaseCallback<String> callback) {
// 模拟异步请求(子线程)
new Thread(() -> {
try {
// 模拟网络延迟
Thread.sleep(1000);
// 模拟登录校验逻辑
if ("admin".equals(username) && "123456".equals(password)) {
callback.onSuccess("登录成功!");
} else {
callback.onFailure("用户名或密码错误");
}
} catch (InterruptedException e) {
callback.onFailure("请求异常:" + e.getMessage());
}
}).start();
}
}
3. Presenter 层实现(核心业务逻辑处理)
java
运行
// LoginPresenterImpl.java
public class LoginPresenterImpl implements LoginPresenter {
// View 层引用(使用弱引用防止内存泄漏)
private WeakReference<LoginView> mLoginViewRef;
private LoginModel mLoginModel;
public LoginPresenterImpl(LoginView loginView) {
this.mLoginViewRef = new WeakReference<>(loginView);
this.mLoginModel = new LoginModel();
}
@Override
public void login() {
LoginView loginView = mLoginViewRef.get();
if (loginView == null) return;
// 显示加载中
loginView.showLoading();
// 获取View层输入的参数
String username = loginView.getUsername();
String password = loginView.getPassword();
// 简单参数校验
if (TextUtils.isEmpty(username)) {
loginView.onLoginFailure("用户名不能为空");
loginView.hideLoading();
return;
}
if (TextUtils.isEmpty(password)) {
loginView.onLoginFailure("密码不能为空");
loginView.hideLoading();
return;
}
// 调用Model层处理登录
mLoginModel.login(username, password, new BaseCallback<String>() {
@Override
public void onSuccess(String data) {
// 切回主线程更新UI
new Handler(Looper.getMainLooper()).post(() -> {
if (loginView != null) {
loginView.hideLoading();
loginView.onLoginSuccess(data);
}
});
}
@Override
public void onFailure(String errorMsg) {
// 切回主线程更新UI
new Handler(Looper.getMainLooper()).post(() -> {
if (loginView != null) {
loginView.hideLoading();
loginView.onLoginFailure(errorMsg);
}
});
}
});
}
@Override
public void detachView() {
// 释放View引用,防止内存泄漏
if (mLoginViewRef != null) {
mLoginViewRef.clear();
mLoginViewRef = null;
}
}
}
4. View 层实现(Activity 作为视图载体)
java
运行
// LoginActivity.java
public class LoginActivity extends AppCompatActivity implements LoginView {
private EditText etUsername, etPassword;
private Button btnLogin;
private ProgressDialog progressDialog;
private LoginPresenter mLoginPresenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
// 初始化控件
initView();
// 创建Presenter实例
mLoginPresenter = new LoginPresenterImpl(this);
// 登录按钮点击事件
btnLogin.setOnClickListener(v -> mLoginPresenter.login());
}
private void initView() {
etUsername = findViewById(R.id.et_username);
etPassword = findViewById(R.id.et_password);
btnLogin = findViewById(R.id.btn_login);
// 初始化加载对话框
progressDialog = new ProgressDialog(this);
progressDialog.setMessage("登录中...");
progressDialog.setCancelable(false);
}
@Override
public void showLoading() {
if (!progressDialog.isShowing()) {
progressDialog.show();
}
}
@Override
public void hideLoading() {
if (progressDialog.isShowing()) {
progressDialog.dismiss();
}
}
@Override
public void onLoginSuccess(String msg) {
Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
// 登录成功跳转到主页面
startActivity(new Intent(this, MainActivity.class));
finish();
}
@Override
public void onLoginFailure(String errorMsg) {
Toast.makeText(this, errorMsg, Toast.LENGTH_SHORT).show();
}
@Override
public String getUsername() {
return etUsername.getText().toString().trim();
}
@Override
public String getPassword() {
return etPassword.getText().toString().trim();
}
@Override
protected void onDestroy() {
super.onDestroy();
// 销毁Presenter,释放引用
mLoginPresenter.detachView();
}
}
5. 布局文件(activity_login.xml)
xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
android:padding="20dp">
<EditText
android:id="@+id/et_username"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="请输入用户名"
android:inputType="text"
android:maxLines="1"/>
<EditText
android:id="@+id/et_password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="请输入密码"
android:inputType="textPassword"
android:maxLines="1"
android:layout_marginTop="15dp"/>
<Button
android:id="@+id/btn_login"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="登录"
android:layout_marginTop="30dp"/>
</LinearLayout>
三、核心亮点说明
- 解耦性:View 层(Activity)只负责界面展示,不处理业务逻辑;Model 层只负责数据处理;Presenter 层作为中间层协调两者,符合单一职责原则。
- 内存泄漏防护 :Presenter 中使用
WeakReference持有 View 引用,且在 Activity 销毁时调用detachView()释放引用。 - 线程切换 :Model 层模拟子线程网络请求,通过
Handler切回主线程更新 UI。 - 可测试性:Presenter 层不依赖具体的 View 实现,可通过 Mock View 进行单元测试。
该示例是 MVP 模式的基础实现,实际项目中可结合 RxJava / 协程优化线程处理、使用 Dagger 依赖注入、封装 BasePresenter/BaseActivity 等进一步提升代码复用性。