一文读懂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 数量膨胀、交互分散
相关推荐
漏刻有时2 小时前
CentOS 不定时 OOM 根治方案:PHP-FPM 进程管控 + Swap 扩容 + 全维度监控
android·centos·php
恋猫de小郭3 小时前
Android 性能迎来提升:内核引入 AutoFDO 普惠所有 15-16 设备
android·前端·flutter
CS_Zero3 小时前
Android ADB调试工具使用简记
android·adb
牢七4 小时前
Slim-4.x php审计 报错分析
android·开发语言·ide·安全·php
xiegwei4 小时前
Android 调起第三方导航
android
AirDroid_cn4 小时前
Android 15 :如何让特定应用通知仅在锁屏显示横幅?
android·智能手机
zh_xuan4 小时前
android ARouter配置降级服务
android·arouter
常利兵4 小时前
Android开发秘籍:接口加解密全解析
android
xuboyok25 小时前
MySQL中ON DUPLICATE KEY UPDATE的介绍与使用、批量更新、存在即更新不存在则插入
android·数据库·mysql