Android 开发手册ANDROID DEV

CHAPTER 05代码组织 · ARCHITECTURE

应用
架构

当 UI 逻辑都在 Activity 里堆砌就成了"上帝 Activity"。本章讲解 MVVM、ViewModel、Repository、Hilt 依赖注入与模块拆分。

开始阅读难度进阶约2.5小时
05.1

为什么需要架构

架构让人改得动:可测试、可复用、可并行开发,需求变更时改动被关进最小边界。没有银弹,按需取舍。

官方推荐

Google 官方推荐 UI 层 → 领域层 → 数据层 的分层,UI 只渲染状态、数据层屏蔽来源(网络/本地)。

05.2

MVVM

Model-View-ViewModel:UI 观察 ViewModel 暴露的 StateFlow,事件回传给 VM,VM 不持有任何 View 引用,便于测试。

class LoginViewModel(private val repo: AuthRepository) : ViewModel() {
    private val _state = MutableStateFlow(LoginState())
    val state = _state.asStateFlow()
    fun login(u: String, p: String) = viewModelScope.launch {
        _state.value = _state.value.copy(loading = true)
        // 调用 repo,更新 state
    }
}
05.3

ViewModel 生命周期

ViewModel 存活于配置变更(旋转),屏幕销毁才清理。是保存 UI 状态、持有协程的最佳容器。

状态保存

复杂状态用 SavedStateHandle 跨进程重建持久化,避免被杀后台后状态丢失。

05.4

Repository 模式

Repository 统一数据来源(远程+本地),对外暴露领域模型,UI/VM 不关心数据来自网络还是数据库缓存。

class UserRepository(
    private val api: UserApi,
    private val dao: UserDao
) {
    fun users(): Flow<List<User>> =
        dao.observe().onEmpty { emit(api.fetch().also { dao.insert(it) }) }
}
05.5

依赖注入与 Hilt

Hilt(基于 Dagger)在编译期生成注入代码,用 @Inject 构造函数与 @Module 提供依赖,避免手动 new 与全局单例。

@HiltViewModel
class HomeViewModel @Inject constructor(
    private val repo: UserRepository
) : ViewModel() { /* ... */ }

@Module @InstallIn(SingletonComponent::class)
object NetworkModule {
    @Provides fun provideApi(): UserApi = Retrofit.Builder()...build().create()
}
05.6

单向数据流

UI 发出事件 → VM 处理 → 产生新状态 → UI 渲染。状态只读暴露,事件是唯一入口,逻辑可预测可测试。

方向载体
UI → VM(意图)方法调用 / Channel
VM → UI(状态)StateFlow / Compose 状态
05.7

模块拆分

用 Gradle 模块按特性/层拆分(:feature:home、:core:network),控制编译耦合、提升并行编译与复用。

依赖方向向下

上层依赖下层,下层不反向引用上层,否则循环依赖且无法单独测试。接口是跨层解耦的关键。

05.8

常见陷阱

ViewModel 里能持有 Context 吗?

不要持有 Activity Context(会泄漏)。需要 Context 时用 AndroidViewModel 的 Application Context,或注入轻量工具类。

为什么用 Hilt 而非手动单例?

手动单例难替换、难测试。Hilt 编译期生成、可轻松注入测试替身,且避免全局可变状态。

05.9

实战练习

延伸资源