android MVP模式代码示例

以下是一个 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>

三、核心亮点说明

  1. 解耦性:View 层(Activity)只负责界面展示,不处理业务逻辑;Model 层只负责数据处理;Presenter 层作为中间层协调两者,符合单一职责原则。
  2. 内存泄漏防护 :Presenter 中使用 WeakReference 持有 View 引用,且在 Activity 销毁时调用 detachView() 释放引用。
  3. 线程切换 :Model 层模拟子线程网络请求,通过 Handler 切回主线程更新 UI。
  4. 可测试性:Presenter 层不依赖具体的 View 实现,可通过 Mock View 进行单元测试。

该示例是 MVP 模式的基础实现,实际项目中可结合 RxJava / 协程优化线程处理、使用 Dagger 依赖注入、封装 BasePresenter/BaseActivity 等进一步提升代码复用性。

相关推荐
qq_717410013 小时前
删除设置-流量使用情况、更多
android
QuantumLeap丶3 小时前
《Flutter全栈开发实战指南:从零到高级》- 23 -混合开发与WebView
android·flutter·ios
_李小白5 小时前
【Android FrameWork】延伸阅读:ViewRootImpl如何管理整个view世界
android
Yang-Never6 小时前
Open GL ES->以指定点为中心缩放图片纹理的完整图解
android·java·开发语言·kotlin·android studio
介一安全6 小时前
【Frida Android】实战篇11:企业常用加密场景 Hook(1)
android·网络安全·逆向·安全性测试·frida
峥嵘life6 小时前
Android EDLA 认证测试内容详解
android
eybk7 小时前
局域网文件传输器安卓版本+win版本
android·python
未来猫咪花7 小时前
LiveData "数据倒灌":一个流行的错误概念
android·android jetpack
2501_937154937 小时前
神马影视 8.8 源码:1.5 秒加载 + 双系统部署
android·源码·源代码管理·机顶盒