Android 三大架构:MVC、MVP、MVVM
一、MVC
核心思想
- 核心:拆分 View,单向数据流向 :
View → Controller → Model → View
分层说明
表格
| 分层 | 对应组件 | 核心职责 |
|---|---|---|
| Model(M) | 实体类 | 数据层,处理数据逻辑 |
| View(V) | XML 布局文件 | 视图层,仅负责基础布局展示(功能极弱) |
| Controller | Activity/Fragment | 控制层,处理用户交互;因 XML 功能不足,需同时承担 View 展示逻辑,职责混杂 |
核心缺点
- V 与 C 强耦合:XML 仅能做布局,Activity 需同时负责 View 展示和 Controller 控制逻辑
- 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 职责,不再同时负责控制逻辑
-
❌ 核心缺点:
- Presenter 持有 View 的引用,易引发内存泄漏
- 复杂页面导致 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
缺点
- View 与 ViewModel 交互分散,逻辑碎片化
- 复杂页面需定义大量 LiveData(可变 + 不可变),导致类膨胀
核心对比总结
表格
| 架构 | 核心解耦方式 | 核心依赖 | 最大痛点 |
|---|---|---|---|
| MVC | 无实质解耦 | XML + Activity | V/C 耦合严重,职责混乱 |
| MVP | Presenter 接口中介 | 接口通信 | Presenter 持有 View 引用、接口膨胀 |
| MVVM | 数据绑定 + ViewModel | LiveData/StateFlow | LiveData 数量膨胀、交互分散 |