一文读懂Android 中的 MVC、MVP、MVVM

Android 三大架构:MVC、MVP、MVVM

一、MVC

核心思想

  • 核心:拆分 View,单向数据流向View → Controller → Model → View

分层说明

表格

分层 对应组件 核心职责
Model(M) 实体类 数据层,处理数据逻辑
View(V) XML 布局文件 视图层,仅负责基础布局展示(功能极弱)
Controller Activity/Fragment 控制层,处理用户交互;因 XML 功能不足,需同时承担 View 展示逻辑,职责混杂

核心缺点

  1. V 与 C 强耦合:XML 仅能做布局,Activity 需同时负责 View 展示和 Controller 控制逻辑
  2. M 与 V 耦合:Model 可能直接操作 View,破坏分层设计原则

二、MVP

核心思想

  • 核心:拆分 Controller,Presenter 作为中介转发 ,数据流向:View → Presenter → Model → Presenter → View
  • 关键:通过接口解耦 View 与 Model,Presenter 承担所有业务逻辑

分层说明

表格

分层 对应组件 核心职责
Model(M) 实体类 / 业务类 数据层,处理业务逻辑和数据操作
View(V) Activity/Fragment 视图层,实现接口,仅负责 UI 展示和触发用户交互(不处理业务逻辑)
Presenter(P) 独立 Presenter 类 核心控制层,通过接口与 View 通信,双向交互 Model 和 View,处理所有逻辑

代码示例(计数器场景)

1. Model:业务逻辑和数据

kotlin

kotlin 复制代码
class CounterModel {
    private var count = 0
    fun increment() {
        count++
    }

    fun getCount(): Int = count
}
2. Contract:定义 View/Presenter 交互接口(核心解耦点)

kotlin

kotlin 复制代码
interface CounterContract {
    // View 接口:仅定义 UI 更新方法
    interface View {
        fun showCount(count: Int)
    }

    // Presenter 接口:定义业务逻辑方法
    interface Presenter {
        fun loadInitialCount()
        fun incrementCount()
    }
}
3. Presenter:核心逻辑处理

kotlin

kotlin 复制代码
class CounterPresenter(
    private val view: CounterContract.View,
    private val model: CounterModel
) : CounterContract.Presenter {

    override fun loadInitialCount() {
        // 操作 Model 后通知 View 更新 UI
        view.showCount(model.getCount())
    }

    override fun incrementCount() {
        model.increment() // 操作 Model
        view.showCount(model.getCount()) // 通知 View 更新
    }
}
4. View(Activity):实现接口,委托逻辑

kotlin

kotlin 复制代码
class MVPActivity : AppCompatActivity(), CounterContract.View {
    private lateinit var presenter: CounterContract.Presenter
    private lateinit var countTextView: TextView
    private lateinit var incrementButton: Button

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_mvp)

        countTextView = findViewById(R.id.count_text_view)
        incrementButton = findViewById(R.id.increment_button)
        // 初始化 Presenter,传入 View 自身和 Model
        presenter = CounterPresenter(this, CounterModel())

        // 点击事件委托给 Presenter 处理
        incrementButton.setOnClickListener {
            presenter.incrementCount()
        }

        // 加载初始数据
        presenter.loadInitialCount()
    }

    // 实现 View 接口,仅负责 UI 更新
    override fun showCount(count: Int) {
        countTextView.text = count.toString()
    }
}

MVP vs MVC 核心差异

  • ✅ View 和 Model 完全解耦,Presenter 作为中转层

  • ✅ Activity 仅承担 View 职责,不再同时负责控制逻辑

  • ❌ 核心缺点:

    1. Presenter 持有 View 的引用,易引发内存泄漏
    2. 复杂页面导致 View 接口膨胀,接口方法庞杂

三、MVVM

核心思想

  • 核心:数据绑定 ,让 View 订阅数据自动更新;数据流向:View ↔ ViewModel → Model → ViewModel ↔ View
  • 关键:借助 ViewModel + LiveData 实现 V 和 VM 完全解耦

分层说明

表格

分层 对应组件 核心职责
Model(M) 实体类 / 业务类 业务逻辑和数据处理
View(V) Activity/Fragment 持有 ViewModel,调用其方法,订阅 LiveData 自动更新 UI
ViewModel(VM) 独立 ViewModel 类 创建 / 管理 Model,暴露业务方法,持有 LiveData/StateFlow 存储状态(不持有 View 引用)

代码示例(计数器场景)

1. Model:业务逻辑和数据

kotlin

kotlin 复制代码
class CounterModel {
    private var count = 0
    fun increment() {
        count++
    }

    fun getCount(): Int = count
}
2. ViewModel:持有 Model + 暴露 LiveData

kotlin

kotlin 复制代码
class CounterViewModel : ViewModel() {
    // ViewModel 内部创建和管理 Model(解耦 Model 与 View)
    private val model = CounterModel()

    // 核心设计:私有可变 LiveData(内部修改),公开不可变 LiveData(外部只读)
    private var _count = MutableLiveData<Int>()
    val count: LiveData<Int> = _count

    init {
        // 初始化数据
        _count.value = model.getCount()
    }

    // 对外暴露业务方法,内部操作 Model 并更新 LiveData
    fun incrementCount() {
        model.increment()
        _count.value = model.getCount() // 更新 LiveData,自动通知 View
    }
}
3. View(Activity):订阅 LiveData 自动更新 UI

kotlin

kotlin 复制代码
class MVVMActivity : AppCompatActivity() {
    private lateinit var countTextView: TextView
    private lateinit var incrementButton: Button
    // 借助 by viewModels() 自动管理 ViewModel 生命周期
    private val viewModel: CounterViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_mvvm)

        countTextView = findViewById(R.id.count_text_view)
        incrementButton = findViewById(R.id.increment_button)

        // 点击事件仅调用 ViewModel 方法,不处理业务逻辑
        incrementButton.setOnClickListener {
            viewModel.incrementCount()
        }

        // 订阅 LiveData,数据变化时自动更新 UI(无需手动调用)
        viewModel.count.observe(this) { newCount ->
            countTextView.text = newCount.toString()
        }
    }
}

核心优势 & 缺点

优势
  • ✅ V 和 VM 完全解耦:Activity 无需实现接口,ViewModel 不持有 Activity 引用
  • ✅ 数据驱动 UI:LiveData 感知生命周期,数据变化自动刷新 UI
缺点
  1. View 与 ViewModel 交互分散,逻辑碎片化
  2. 复杂页面需定义大量 LiveData(可变 + 不可变),导致类膨胀

核心对比总结

表格

架构 核心解耦方式 核心依赖 最大痛点
MVC 无实质解耦 XML + Activity V/C 耦合严重,职责混乱
MVP Presenter 接口中介 接口通信 Presenter 持有 View 引用、接口膨胀
MVVM 数据绑定 + ViewModel LiveData/StateFlow LiveData 数量膨胀、交互分散
相关推荐
BoomHe8 小时前
Android Framework 文件应用移植到 AndroidStudio
android
传奇开心果编程9 小时前
【Jetpack Compose基础语法学与练】第8课 rememberSaveable,页面旋转/系统重建保留状态
android·学习·ui·kotlin·android jetpack
>Andre<10 小时前
UFS5.0标准中文全译·卷一:范围、术语与架构
android·linux·嵌入式硬件
事圆则缓11 小时前
Java 8 Lambda、Stream、Optional 与 Android 边界:从回调语法到运行时兼容
android·java
mmsx11 小时前
Android 地图十万要素不卡顿:空间网格 + 渐进式加载的移动端实践
android·大数据·opengl
Godikov12 小时前
Android 工业终端保活实战:前台服务 + 开机自启 + 更新自启的三重保障
android
字节暗面12 小时前
SO加固强度怎么量化?腾讯ACE与FairGuard静态分析实测
android·逆向
终端安全笔记14 小时前
描述文件、企业证书、MDM 不是三个名字,是三层
android·ios·智能手机
字节暗面14 小时前
一次游戏安全SO静态分析记录:腾讯ACE与FairGuard加固强度对比
android·逆向工程·游戏加固
传奇开心果编程15 小时前
【Jetpack Compose基础语法学与练】第9课 SideEffect副作用 + LaunchedEffect,处理异步任务和生命周期
android·学习·ui·kotlin·android jetpack