Android MVVM 架构实战:从理论到代码

为什么要用架构?

没有良好架构的 Android 应用,业务逻辑往往散布在 Activity/Fragment 中,导致这些类变成数千行的”上帝类”,难以测试和维护。MVVM 是 Google 官方推荐的架构模式,配合 Architecture Components 可以大幅提升代码质量。

MVVM 三层结构

  • Model:数据层,负责从网络、数据库等数据源获取数据。通常包含 Repository 模式。
  • View:UI 层,由 Activity/Fragment/Composable 组成,负责展示数据和接收用户输入。
  • ViewModel:持有 UI 状态,处理业务逻辑,作为 View 和 Model 之间的桥梁。

实战代码

Model 层

data class User(val id: Int, val name: String, val avatar: String)

class UserRepository {
    private val api = RetrofitClient.userApi
    private val dao = AppDatabase.instance.userDao()

    suspend fun getUser(id: Int): User {
        // 先返回缓存,再请求网络
        val cached = dao.getUser(id)
        return try {
            val remote = api.getUser(id)
            dao.insertUser(remote)
            remote
        } catch (e: Exception) {
            cached ?: throw e
        }
    }
}

ViewModel 层

class UserViewModel(
    private val repository: UserRepository
) : ViewModel() {

    private val _uiState = MutableStateFlow(UserUiState())
    val uiState: StateFlow = _uiState.asStateFlow()

    fun loadUser(id: Int) {
        viewModelScope.launch {
            _uiState.update { it.copy(isLoading = true) }
            try {
                val user = repository.getUser(id)
                _uiState.update { it.copy(user = user, isLoading = false) }
            } catch (e: Exception) {
                _uiState.update { it.copy(error = e.message, isLoading = false) }
            }
        }
    }
}

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

View 层

@AndroidEntryPoint
class UserFragment : Fragment() {
    private val viewModel: UserViewModel by viewModels()

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        viewModel.uiState
            .flowWithLifecycle(viewLifecycleOwner.lifecycle)
            .onEach { state -> render(state) }
            .launchIn(viewLifecycleOwner.lifecycleScope)

        viewModel.loadUser(1)
    }

    private fun render(state: UserUiState) {
        binding.progressBar.isVisible = state.isLoading
        state.user?.let { binding.nameText.text = it.name }
        state.error?.let { showToast(it) }
    }
}

核心原则

  1. 单一数据源:每种数据类型只有一个可信来源(通常是 Repository)。
  2. 单向数据流 (UDF):数据从 ViewModel 流向 View,事件从 View 流向 ViewModel。
  3. ViewModel 不持有 View 引用:避免内存泄漏,使用 StateFlow/LiveData 通信。
  4. 依赖注入:使用 Hilt/Koin 管理依赖,方便测试。

推荐技术栈

  • 网络:Retrofit + OkHttp + Kotlin Coroutines
  • 数据库:Room
  • DI:Hilt(基于 Dagger)
  • 状态:StateFlow / LiveData
  • 测试:JUnit + MockK + Turbine
沪ICP备2026038898号-1