分类: 架构设计

  • 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