一个 Android 项目刚开始写的时候,代码量小,逻辑集中在 Activity 或 Fragment 里也还能接受。但随着功能增长,界面复杂,网络请求、数据缓存、页面状态、业务判断全部堆在一起,修改一个功能往往牵连一片代码,测试也变得困难。
架构的核心目标不是让代码"看起来更高级",而是让代码更容易维护、更容易测试、更容易协作。它解决的是"代码放在哪里"和"模块之间怎么通信"这两个问题。
MVC(Model-View-Controller)把应用分为三层:
用户操作 → Controller → 更新 Model → 通知 View 刷新Android 框架本身带有 MVC 的影子。Activity/Fragment 承担了 Controller 和 View 的双重职责:
class UserActivity : AppCompatActivity() {
// 相当于 Controller
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_user)
// 发起请求
UserRepository.getUsers { users ->
// 直接操作 View
val adapter = UserAdapter(users)
recyclerView.adapter = adapter
}
}
}在 Android 实际开发中,Activity 同时扮演了 Controller 和 View,导致:
这不是经典 MVC 的错,而是 Android 的组件模型让 Controller 和 View 很难彻底分离。
MVP(Model-View-Presenter)引入了 Presenter 层:
用户操作 → View 接口 → Presenter → 更新 Model
↓
通过 View 接口刷新 UI首先定义 View 接口:
interface UserView {
fun showLoading()
fun hideLoading()
fun showUsers(users: List)
fun showError(message: String)
}Presenter 持有 View 接口引用:
class UserPresenter(private val view: UserView) {
private val repository = UserRepository()
fun loadUsers() {
view.showLoading()
repository.getUsers(
onSuccess = { users ->
view.hideLoading()
view.showUsers(users)
},
onError = { error ->
view.hideLoading()
view.showError(error.message ?: "加载失败")
}
)
}
}Activity 实现 View 接口:
class UserActivity : AppCompatActivity(), UserView {
private val presenter = UserPresenter(this)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_user)
presenter.loadUsers()
}
override fun showLoading() { progressBar.visibility = View.VISIBLE }
override fun hideLoading() { progressBar.visibility = View.GONE }
override fun showUsers(users: List) {
recyclerView.adapter = UserAdapter(users)
}
override fun showError(message: String) {
Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
}
}**优势:**
**不足:**
MVVM(Model-View-ViewModel)借助 Jetpack 组件实现响应式数据绑定:
用户操作 → View → ViewModel → Repository
↑
LiveData / StateFlow 自动推送新数据ViewModel 负责维护状态和调用 Repository:
class UserViewModel(
private val repository: UserRepository
) : ViewModel() {
private val _uiState = MutableStateFlow(UserUiState.Loading)
val uiState: StateFlow = _uiState
fun loadUsers() {
viewModelScope.launch {
_uiState.value = UserUiState.Loading
try {
val users = repository.getUsers()
_uiState.value = UserUiState.Success(users)
} catch (e: Exception) {
_uiState.value = UserUiState.Error(e.message ?: "加载失败")
}
}
}
}
sealed class UserUiState {
data object Loading : UserUiState()
data class Success(val users: List) : UserUiState()
data class Error(val message: String) : UserUiState()
}View 观察状态变化:
class UserActivity : AppCompatActivity() {
private val viewModel: UserViewModel by viewModels {
UserViewModelFactory(UserRepository())
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_user)
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.uiState.collect { state ->
when (state) {
is UserUiState.Loading -> {
progressBar.visibility = View.VISIBLE
}
is UserUiState.Success -> {
progressBar.visibility = View.GONE
recyclerView.adapter = UserAdapter(state.users)
}
is UserUiState.Error -> {
progressBar.visibility = View.GONE
Toast.makeText(this@UserActivity, state.message, Toast.LENGTH_SHORT).show()
}
}
}
}
}
viewModel.loadUsers()
}
}MVI(Model-View-Intent)把交互进一步规范化:
用户操作 → Intent → Model 处理 → 新 State → View 渲染核心约束是:View 只能发出 Intent,Model 只能产出 State,数据单向流动。
定义 Intent 和 State:
sealed class UserIntent {
data object LoadUsers : UserIntent()
data class RefreshUser(val userId: String) : UserIntent()
}
data class UserState(
val isLoading: Boolean = false,
val users: List = emptyList(),
val error: String? = null
)ViewModel 统一处理 Intent:
class UserViewModel(
private val repository: UserRepository
) : ViewModel() {
private val _state = MutableStateFlow(UserState())
val state: StateFlow = _state
fun dispatch(intent: UserIntent) {
when (intent) {
is UserIntent.LoadUsers -> loadUsers()
is UserIntent.RefreshUser -> refreshUser(intent.userId)
}
}
private fun loadUsers() {
viewModelScope.launch {
_state.update { it.copy(isLoading = true, error = null) }
try {
val users = repository.getUsers()
_state.update { it.copy(isLoading = false, users = users) }
} catch (e: Exception) {
_state.update { it.copy(isLoading = false, error = e.message) }
}
}
}
private fun refreshUser(userId: String) {
viewModelScope.launch {
try {
val refreshed = repository.refreshUser(userId)
_state.update { state ->
state.copy(users = state.users.map {
if (it.id == userId) refreshed else it
})
}
} catch (e: Exception) {
_state.update { it.copy(error = e.message) }
}
}
}
}View 只发送 Intent,只观察 State:
class UserActivity : AppCompatActivity() {
private val viewModel: UserViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_user)
// 发起加载意图
viewModel.dispatch(UserIntent.LoadUsers)
// 刷新按钮点击 → 发送意图
refreshButton.setOnClickListener {
viewModel.dispatch(UserIntent.RefreshUser(currentUserId))
}
// 观察状态
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.state.collect { state ->
progressBar.visibility = if (state.isLoading) View.VISIBLE else View.GONE
if (state.users.isNotEmpty()) {
recyclerView.adapter = UserAdapter(state.users)
}
state.error?.let { Toast.makeText(this@UserActivity, it, Toast.LENGTH_SHORT).show() }
}
}
}
}
}| 维度 | MVC | MVP | MVVM | MVI |
|------|-----|-----|------|-----|
| 关注点分离 | 一般 | 好 | 好 | 好 |
| 可测试性 | 差 | 好 | 好 | 好 |
| 代码量 | 少 | 多 | 中等 | 多 |
| 数据流方向 | 不明确 | 双向 | 单向(推荐) | 严格单向 |
| 生命周期处理 | 手动 | 手动 | 自动 | 自动 |
| 适用场景 | 简单项目 | 中等项目 | 大多数项目 | 复杂交互页面 |
没有一种架构适合所有场景。实际项目中可以混合使用:
架构选型的关键不在于"哪种更先进",而在于"哪种能解决当前的问题"。团队规模、项目阶段、维护成本都是需要考虑的因素。
如果一个项目还在用 MVC 甚至没有架构,直接跳到 MVI 会引发大量重构。可以分步进行:先把网络请求和数据操作收到 Repository,再引入 ViewModel 管理状态,最后在复杂页面引入 Intent 机制。
同一个项目或同一个模块内,架构风格要统一。如果一部分用 MVVM、一部分用 MVI、一部分直接写在 Activity 里,维护成本会比不用架构更高。
不要为了分层而分层。如果一个功能很简单,三层代码都是空壳转发,那不如简化。架构是手段,不是目的。
ViewModel、LiveData、StateFlow、Navigation、Room 这些组件本身就是为架构设计的。充分利用它们可以减少自建轮子的成本,也能让团队更快达成共识。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。