跳到主要内容

Android 架构模式(MVC / MVP / MVVM / MVI)

Android 架构演进

1. MVC

ModelControllerView
↑ ↓
└───────────────┘
  • Android 中 Activity 既是 Controller 又是 View,导致职责混乱
  • 随业务增长 Activity 代码膨胀,难以测试

2. MVP

View (Activity/Fragment)
↕ 接口
Presenter (业务逻辑)

Model (数据层)
  • View 和 Presenter 通过接口解耦,便于单元测试
  • 缺点:接口类爆炸,Presenter 持有 View 引用需注意内存泄漏

3. MVVM(Google 官方推荐)

View (Activity/Fragment)
↓ 观察
ViewModel (持有 LiveData/StateFlow)

Repository

Remote DataSource / Local DataSource

ViewModel 核心优势

  • 生命周期感知:屏幕旋转不丢失数据
  • 不持有 View 引用,无内存泄漏风险
  • SavedStateHandle:进程被杀后恢复状态
class UserViewModel(private val repo: UserRepository) : ViewModel() {
private val _uiState = MutableStateFlow(UiState())
val uiState: StateFlow<UiState> = _uiState.asStateFlow()

fun loadUser(id: String) {
viewModelScope.launch {
_uiState.update { it.copy(loading = true) }
val user = repo.getUser(id)
_uiState.update { it.copy(user = user, loading = false) }
}
}
}

4. MVI(单向数据流)

User Action (Intent)

ViewModel 处理 → 新 State

View 渲染
  • State:UI 的完整快照,单一数据源
  • Intent:用户操作,密封类描述所有可能事件
  • 单向数据流:状态不可变,每次更新生成新 State
  • 优点:状态可预测、易于调试和测试
  • 代表库:Orbit MVI、MVI Kotlin
sealed class UserIntent {
data class LoadUser(val id: String) : UserIntent()
object Logout : UserIntent()
}

data class UserState(
val user: User? = null,
val loading: Boolean = false,
val error: String? = null
)

5. Clean Architecture 分层

Presentation Layer (ViewModel + UI)

Domain Layer (UseCase + Entity) ← 纯 Kotlin,无 Android 依赖

Data Layer (Repository + DataSource)
  • UseCase(交互器):封装单一业务规则,可单独测试
  • Repository:抽象数据来源,协调本地缓存和远端数据
  • 依赖规则:内层不依赖外层

6. Hilt 依赖注入

@HiltAndroidApp class App : Application()

@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
@Inject lateinit var viewModel: UserViewModel
}

@Module @InstallIn(SingletonComponent::class)
object NetworkModule {
@Provides @Singleton
fun provideRetrofit(): Retrofit = Retrofit.Builder()...
}

7. 高频面试题

  • ViewModel 为什么能在屏幕旋转后存活? 存储在 ViewModelStore 中,与 Activity 生命周期解绑
  • LiveData 和 StateFlow 如何选择? 新项目推荐 StateFlow(更强大,支持初始值,协程原生)
  • Repository 的作用? 单一数据源,屏蔽本地/远端差异,统一数据格式
  • UseCase 一定需要吗? 简单场景可省略,复杂业务规则建议抽取便于测试