Android随笔-MVC/MVP/MVVM/MVI

一、MVC (Model-View-Controller)

数据流向

User → View(点击) → Controller(Activity) → Model(数据/网络) → 回调更新 View

职责划分

角色 职责
Model 数据层(Bean、网络请求、数据库)
View XML + Activity(显示 + 交互监听)
Controller Activity(业务逻辑 + UI 更新)

实现

java 复制代码
// Model
public class UserModel {
    public void login(String name, String pwd, Callback callback) {
        api.login(name, pwd, callback);
    }
}

// Activity = Controller + View
public class LoginActivity extends Activity {
    private UserModel model;
    private EditText etName, etPwd;
    private Button btnLogin;
    private TextView tvResult;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.login);
        
        model = new UserModel();
        
        btnLogin.setOnClickListener(v -> {
            // Controller 处理逻辑
            model.login(etName.getText().toString(),
                etPwd.getText().toString(),
                new Callback() {
                    @Override
                    public void onSuccess(User user) {
                        // Controller 直接更新 View
                        tvResult.setText("Success");
                        startActivity(new Intent(LoginActivity.this, MainActivity.class));
                    }
                    @Override
                    public void onError(String msg) {
                        tvResult.setText(msg);
                    }
                });
        });
    }
}
✅ 优点 ❌ 缺点
简单直观,容易理解 Activity 既当 C 又当 V
适合小型项目 代码臃肿,难以维护
职责分离初步体现 View 和 Controller 耦合
单元测试困难
Model 直接操作 View
推荐度

二、MVP (Model-View-Presenter)

数据流向

User → View(点击) → Presenter(中介) → Model(数据) → 回调 → Presenter → View 更新

职责划分

角色 职责
Model 数据层(网络、数据库、Bean)
View Activity/Fragment(纯 UI 展示 + 事件上报)
Presenter 业务逻辑层(处理逻辑 + 调度 M/V)

核心改进:View 和 Presenter 通过接口解耦,Presenter 可独立测试。

实现

java 复制代码
// Contract 接口定义
public interface LoginContract {
    interface View {
        void showLoading();
        void hideLoading();
        void showError(String msg);
        void navigateToMain();
    }
    
    interface Presenter {
        void login(String name, String pwd);
        void detachView();
    }
}

// Presenter 实现
public class LoginPresenter implements LoginContract.Presenter {
    private LoginContract.View view;
    private LoginRepository repo;

    public void attachView(LoginContract.View view) {
        this.view = view;
    }

    @Override
    public void login(String name, String pwd) {
        if (view == null) return;
        view.showLoading();
        
        repo.login(name, pwd, new Callback() {
            @Override
            public void onSuccess() {
                if (view != null) {
                    view.hideLoading();
                    view.navigateToMain();
                }
            }
            @Override
            public void onError(String msg) {
                if (view != null) {
                    view.hideLoading();
                    view.showError(msg);
                }
            }
        });
    }

    @Override
    public void detachView() {
        this.view = null;  // 防止内存泄漏
    }
}

// Activity 实现 View 接口
public class LoginActivity extends AppCompatActivity implements LoginContract.View {
    private LoginPresenter presenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.login);
        
        presenter = new LoginPresenter(new LoginRepository());
        presenter.attachView(this);
        
        btnLogin.setOnClickListener(v -> {
            presenter.login(etName.getText().toString(), etPwd.getText().toString());
        });
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        presenter.detachView();  // 必须解绑,防内存泄漏
    }

    @Override
    public void showLoading() { progressBar.setVisibility(View.VISIBLE); }

    @Override
    public void hideLoading() { progressBar.setVisibility(View.GONE); }

    @Override
    public void showError(String msg) { Toast.makeText(this, msg, Toast.LENGTH_SHORT).show(); }

    @Override
    public void navigateToMain() { startActivity(new Intent(this, MainActivity.class)); }
}
✅ 优点 ❌ 缺点
View 和 Presenter 解耦 接口爆炸(IView/IPresenter)
Presenter 可独立测试 Presenter 持有 View 引用
职责清晰,代码分层 需手动管理生命周期
支持多 View 复用 P 内存泄漏风险(未 detach)
代码量增加
推荐度 ⭐⭐

三、MVVM (Model-View-ViewModel) ⭐⭐⭐⭐⭐

数据流向

User → View(点击) → ViewModel(业务) → Model(数据) → 数据变化 → ViewModel → View 自动更新

(LiveData/Flow/DataBinding)

职责划分

角色 职责
Model 数据层(Repository + 网络 + 数据库)
View Activity/Fragment(纯 UI 观察数据变化)
ViewModel 业务逻辑(处理数据 + 暴露 LiveData)

核心改进:数据驱动,生命周期自动管理,无需手动 detach。

实现

java 复制代码
// ViewModel
public class LoginViewModel extends ViewModel {
    private MutableLiveData<UiState> uiState = new MutableLiveData<>();
    private LoginRepository repo;

    public LoginViewModel(LoginRepository repo) {
        this.repo = repo;
    }

    public LiveData<UiState> getUiState() {
        return uiState;
    }

    public void login(String name, String pwd) {
        uiState.setValue(UiState.loading());
        repo.login(name, pwd, new Callback() {
            @Override
            public void onSuccess() {
                uiState.setValue(UiState.success());
            }
            @Override
            public void onError(String msg) {
                uiState.setValue(UiState.error(msg));
            }
        });
    }
}

// Activity 观察数据
public class LoginActivity extends AppCompatActivity {
    private LoginViewModel vm;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.login);
        
        vm = new ViewModelProvider(this, 
            new LoginViewModelFactory(new LoginRepository())
        ).get(LoginViewModel.class);

        // 自动观察数据变化
        vm.getUiState().observe(this, state -> {
            if (state.isLoading()) {
                showLoading();
            } else if (state.isSuccess()) {
                navigateToMain();
            } else if (state.isError()) {
                showError(state.getMsg());
            }
        });

        btnLogin.setOnClickListener(v -> {
            vm.login(etName.getText().toString(), etPwd.getText().toString());
        });
    }
}
✅ 优点 ❌ 缺点
生命周期自动管理 ViewModel 可能过重
数据驱动,自动更新 DataBinding 调试困难
ViewModel 可复用 学习成本较高
单元测试方便 简单场景过度设计
Jetpack 原生支持
无需手动 detach
推荐度 ⭐⭐⭐⭐⭐

四、MVI (Model-View-Intent)

数据流向

User → Intent(意图) → ViewModel 处理 → Model(Reducer) → 不可变 State → View渲染新 State

职责划分

角色 职责
Intent 用户意图(Click/Refresh/LoadMore)
ViewModel 处理 Intent,调用 Model,生成新 State
State 不可变状态(UI 完整快照)
View 纯渲染函数(State → UI 映射)

核心改进:单向数据流,不可变 State,状态可预测,支持时间旅行调试。

实现

java 复制代码
// 不可变 State
public class LoginState {
    public final boolean isLoading;
    public final String error;
    public final User user;
    public final boolean isLoggedIn;

    public LoginState(boolean isLoading, String error, User user, boolean isLoggedIn) {
        this.isLoading = isLoading;
        this.error = error;
        this.user = user;
        this.isLoggedIn = isLoggedIn;
    }

    // 工厂方法生成新状态
    public static LoginState idle() {
        return new LoginState(false, null, null, false);
    }
    public static LoginState loading() {
        return new LoginState(true, null, null, false);
    }
    public static LoginState success(User user) {
        return new LoginState(false, null, user, true);
    }
    public static LoginState error(String msg) {
        return new LoginState(false, msg, null, false);
    }
}

// Intent 枚举
public enum LoginIntent {
    LOGIN_CLICK,
    USERNAME_CHANGED,
    PASSWORD_CHANGED
}

// ViewModel
public class LoginViewModel extends ViewModel {
    private MutableLiveData<LoginState> state = new MutableLiveData<>(LoginState.idle());
    private LoginRepository repo;

    public LiveData<LoginState> getState() {
        return state;
    }

    public void processIntent(LoginIntent intent, String... params) {
        switch (intent) {
            case LOGIN_CLICK:
                handleLogin(params[0], params[1]);
                break;
            case USERNAME_CHANGED:
                // 处理用户名变化
                break;
            case PASSWORD_CHANGED:
                // 处理密码变化
                break;
        }
    }

    private void handleLogin(String name, String pwd) {
        state.setValue(LoginState.loading());
        repo.login(name, pwd, new Callback() {
            @Override
            public void onSuccess(User user) {
                state.setValue(LoginState.success(user));
            }
            @Override
            public void onError(String msg) {
                state.setValue(LoginState.error(msg));
            }
        });
    }
}

// Activity
public class LoginActivity extends AppCompatActivity {
    private LoginViewModel vm;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.login);
        
        vm = new ViewModelProvider(this).get(LoginViewModel.class);

        vm.getState().observe(this, state -> {
            render(state);  // 纯渲染函数
        });

        btnLogin.setOnClickListener(v -> {
            vm.processIntent(LoginIntent.LOGIN_CLICK,
                etName.getText().toString(),
                etPwd.getText().toString());
        });
    }

    private void render(LoginState state) {
        progressBar.setVisibility(state.isLoading ? View.VISIBLE : View.GONE);
        if (state.error != null) {
            tvError.setText(state.error);
            tvError.setVisibility(View.VISIBLE);
        } else {
            tvError.setVisibility(View.GONE);
        }
        if (state.isLoggedIn) {
            startActivity(new Intent(this, MainActivity.class));
        }
    }
}
✅ 优点 ❌ 缺点
单向数据流,状态可预测 样板代码多
不可变 State,调试友好 State 类膨胀
时间旅行调试 简单场景过度设计
状态复现简单 学习曲线陡峭
适合复杂交互场景 每个 Intent 都需定义
推荐度 ⭐⭐⭐⭐

五、四大架构模式对比总结

维度 MVC MVP MVVM MVI
数据流向 双向 双向 双向/响应式 单向
View 更新 Controller 直接操作 Presenter 回调 LiveData 自动 State 不可变渲染
生命周期 手动管理 手动 detach 自动管理 自动管理
单元测试 困难 较容易 容易 容易
代码量 多(接口) 中等 多(State/Intent)
内存泄漏 中(需 detach)
适用场景 小型 Demo 中小型项目 中大型项目 复杂交互/状态多
推荐度 ⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐

六、选型建议

  • 新项目:首选 MVVM(Jetpack 原生支持,生态成熟)
  • 状态复杂/团队协作:选 MVI(状态可预测,调试友好)
  • 遗留项目维护:用 MVP(逐步迁移)
  • 避免 MVC(Activity 臃肿,难以维护)
相关推荐
阿pin1 小时前
Android随笔-View绘制流程
android·开发语言·view
2501_916008891 小时前
iOS 证书管理最佳实践 从创建到续期的完整指南
android·ios·小程序·https·uni-app·iphone·webview
丁小未1 小时前
基于MVVM框架的XUUI 扩展的UI管理系统教程
unity·mvvm·ui框架·xuui·ui管理器
私人珍藏库10 小时前
[Android] PeakFinder AR v4.8.89 (山峰全景识别+增强现实山峰查看器)
android·人工智能·智能手机·ar·工具·软件
researcher-Jiang11 小时前
高性能计算之OpenMP——超算习堂学习1
android·java·学习
alexhilton12 小时前
Kotlin DSL深度解析:从Gradle脚本到构建你自己的DSL
android·kotlin·android jetpack
烽火聊员12 小时前
查看Android Studio错误日志
android·ide·android studio
星空语17 小时前
音频001_Android+Linux车载/手机音频全链路分层架构图
android·linux·音视频
CyL_Cly18 小时前
解决官网下载慢!Android Studio 最新版网盘下载
android·ide·android studio