一文读懂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 数量膨胀、交互分散
相关推荐
我不会插花弄玉6 小时前
7.函数【由浅入深-MySQL】
android·数据库·mysql
vilya7 小时前
手机端 Agent 怎么动起来:Kotlin 与 Python 之间的桥
android·agent
高晶7 小时前
Android 新手实战:从零搭建 WebRTC 在线直播系统
android·程序员
alexhilton7 小时前
为什么你的 LazyColumn 仍然卡顿
android·kotlin·android jetpack
>Andre<9 小时前
系统架构全景
android
hai_android13 小时前
Android 嵌套滑动冲突:内部拦截 vs 外部拦截实战
android·android studio·android jetpack
木子欢儿15 小时前
Zabbix 7.4 Alpine 版 Docker Compose 部署
android·运维·docker·容器·zabbix
素师良码15 小时前
第9篇:Android驱动踩坑实录:DSI RX FIFO前置脏0、数据错位复盘
android
夜雪一千16 小时前
如何在MySQL中实现字段级加密
android·数据库·mysql
夜雪一千16 小时前
MySQL 如何存储经纬度坐标:选型、建表、空间查询实战
android·mysql·adb