扩展 ViewModel 知识体系:进阶架构组件深度解析

扩展 ViewModel 知识体系:进阶架构组件深度解析

除了标准 ViewModel 和 SharedViewModel,Android Jetpack 提供了一系列强大的架构组件。以下是完整解析:

1. AndroidViewModel:访问应用上下文的特殊 ViewModel

核心特性和使用场景

kotlin 复制代码
class SystemInfoViewModel(application: Application) : AndroidViewModel(application) {
    // 安全访问 Application Context
    private val appContext = getApplication<Application>().applicationContext
    
    // 示例:获取设备信息
    fun getDeviceInfo(): String {
        val pm = appContext.packageManager
        return "Model: ${Build.MODEL}\nOS: ${Build.VERSION.RELEASE}"
    }
    
    // 示例:访问资源
    fun getAppName(): String {
        return appContext.resources.getString(R.string.app_name)
    }
}

使用规范

操作 安全做法 危险做法
获取上下文 getApplication<Application>() 持有 Activity/Fragment 引用
资源访问 仅访问应用级资源 尝试访问视图相关资源
系统服务 getSystemService(Class<T>) 持有服务长引用

2. SavedStateViewModel:配置更改时保留状态

架构原理

graph LR A[Activity销毁] --> B[保存状态到Bundle] C[重建Activity] --> D[恢复ViewModel状态] B -->|SavedStateHandle| E[ViewModel] D --> E

实现示例

kotlin 复制代码
class UserFormViewModel(
    private val savedStateHandle: SavedStateHandle
) : ViewModel() {

    // 使用状态键值定义
    companion object {
        private const val USERNAME_KEY = "username"
        private const val EMAIL_KEY = "email"
    }

    var username: String
        get() = savedStateHandle.get(USERNAME_KEY) ?: ""
        set(value) = savedStateHandle.set(USERNAME_KEY, value)

    var email: String
        get() = savedStateHandle.get(EMAIL_KEY) ?: ""
        set(value) = savedStateHandle.set(EMAIL_KEY, value)
        
    // 重置表单状态
    fun clearForm() {
        savedStateHandle.remove<Any>(USERNAME_KEY)
        savedStateHandle.remove<Any>(EMAIL_KEY)
    }
}

3. HiltViewModel:依赖注入的最佳实践

依赖注入架构

graph TD A[Database] --> B[Repository] C[Network API] --> B B --> D[ViewModel] D --> E[Activity/Fragment] F[Hilt 模块] -->|提供依赖| D

实现示例

kotlin 复制代码
// 定义依赖项
@Module
@InstallIn(ViewModelComponent::class)
object UserModule {
    @Provides
    fun provideUserRepo(db: AppDatabase): UserRepository {
        return UserRepository(db.userDao())
    }
}

// 使用 HiltViewModel
@HiltViewModel
class UserProfileViewModel @Inject constructor(
    private val userRepo: UserRepository
) : ViewModel() {
    
    private val _user = MutableLiveData<User>()
    val user: LiveData<User> = _user
    
    fun loadUser(userId: String) {
        viewModelScope.launch {
            _user.value = userRepo.getUser(userId)
        }
    }
}

多模块架构实现

kotlin 复制代码
// 注册导航图作用域的 ViewModel
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/main_nav"
    app:viewModelScope="graph_view_model">
    
    <fragment android:id="@+id/userList" ... />
    <fragment android:id="@+id/userDetail" ... />
</navigation>

// 在片段中获取
class UserListFragment : Fragment() {
    private val navViewModel: SharedUserViewModel by navGraphViewModels(R.id.main_nav)
    
    fun onUserSelected(user: User) {
        navViewModel.setSelectedUser(user)
        findNavController().navigate(R.id.action_to_detail)
    }
}

class UserDetailFragment : Fragment() {
    private val navViewModel: SharedUserViewModel by navGraphViewModels(R.id.main_nav)
    
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        navViewModel.selectedUser.observe(viewLifecycleOwner) { user ->
            // 显示用户详情
        }
    }
}

5. Compose 专属 ViewModel:现代 UI 架构

Composable 中的状态管理

kotlin 复制代码
@Composable
fun UserProfileScreen(
    userId: String,
    viewModel: UserProfileViewModel = viewModel(
        factory = UserProfileViewModel.provideFactory(userId)
    )
) {
    val userState by viewModel.userState.collectAsState()
    
    when(val state = userState) {
        is UserState.Loading -> LoadingSpinner()
        is UserState.Success -> UserProfile(state.user)
        is UserState.Error -> ErrorView(state.message)
    }
}

// ViewModel 工厂
class UserProfileViewModel @AssistedInject constructor(
    @Assisted private val userId: String,
    private val repo: UserRepository
) : ViewModel() {
    
    val userState = MutableStateFlow<UserState>(UserState.Loading)
    
    init {
        loadUser()
    }
    
    private fun loadUser() {
        viewModelScope.launch {
            userState.value = try {
                UserState.Success(repo.getUser(userId))
            } catch (e: Exception) {
                UserState.Error(e.message ?: "Unknown error")
            }
        }
    }
    
    @AssistedFactory
    interface Factory {
        fun create(userId: String): UserProfileViewModel
    }
    
    companion object {
        fun provideFactory(userId: String): Factory {
            return object : Factory {
                override fun create(userId: String): UserProfileViewModel {
                    val app = LocalContext.current.applicationContext as Application
                    val repo = (app as AppDependencies).provideUserRepo()
                    return UserProfileViewModel(userId, repo)
                }
            }
        }
    }
}

6. ViewModel 领域扩展:解决复杂场景

模式 1:自动加载状态管理

kotlin 复制代码
abstract class LoadableViewModel<T> : ViewModel() {
    private val _loading = MutableStateFlow(false)
    val loading: StateFlow<Boolean> = _loading.asStateFlow()
    
    private val _data = MutableStateFlow<T?>(null)
    val data: StateFlow<T?> = _data.asStateFlow()
    
    private val _error = MutableStateFlow<Throwable?>(null)
    val error: StateFlow<Throwable?> = _error.asStateFlow()
    
    protected suspend fun executeLoad(block: suspend () -> T) {
        _loading.value = true
        _error.value = null
        
        try {
            _data.value = block()
        } catch (e: Exception) {
            _error.value = e
        } finally {
            _loading.value = false
        }
    }
}

class ProductViewModel : LoadableViewModel<List<Product>>() {
    fun loadProducts() {
        viewModelScope.launch {
            executeLoad {
                productRepo.getFeaturedProducts()
            }
        }
    }
}

模式 2:时间旅行调试(测试专用)

kotlin 复制代码
class DebuggableViewModel : ViewModel() {
    private val stateHistory = mutableListOf<AppState>()
    private val _currentState = MutableLiveData<AppState>()
    val currentState: LiveData<AppState> = _currentState
    
    fun updateState(newState: AppState) {
        stateHistory.add(newState)
        _currentState.value = newState
    }
    
    // 测试环境专用方法
    @VisibleForTesting
    fun rewindState(step: Int) {
        if (step < stateHistory.size) {
            _currentState.value = stateHistory[stateHistory.size - step - 1]
        }
    }
    
    // 重置时间线
    @VisibleForTesting
    fun resetHistory() {
        stateHistory.clear()
    }
}

7. 性能优化策略:高级技巧

内存优化表

模式 内存占用 响应速度 适用场景
StateFlow 频繁更新状态
SharedFlow(replay=1)​ 单事件系统
LiveData 最低 简单 UI 更新
ConflatedBroadcastChannel 最高 高频事件
ObservableField 最低 数据绑定简单属性

惰性初始化技术

kotlin 复制代码
class OptimizedViewModel : ViewModel() {
    private val _heavyData by lazy {
        val data = HeavyDataSet()
        initializeHeavyData(data)
        MutableStateFlow(data)
    }
    val heavyData: StateFlow<HeavyDataSet> = _heavyData
    
    private val _resourceCache = mutableMapOf<String, Resource>()
    
    fun getResource(resourceId: String): Resource {
        return _resourceCache.getOrPut(resourceId) {
            // 模拟耗时加载
            Thread.sleep(50)
            Resource(resourceId)
        }
    }
    
    override fun onCleared() {
        // 清理缓存资源
        _resourceCache.clear()
        super.onCleared()
    }
}

8. 测试策略:JUnit5 + Turbine 高级测试

ViewModel 测试套件

kotlin 复制代码
@ExperimentalCoroutinesApi
@ExtendWith(MockKExtension::class)
class ProductViewModelTest {
    @MockK
    lateinit var mockRepo: ProductRepository
    
    private lateinit var viewModel: ProductViewModel
    
    @BeforeEach
    fun setup() {
        MockKAnnotations.init(this)
        Dispatchers.setMain(StandardTestDispatcher())
    }
    
    @AfterEach
    fun tearDown() {
        Dispatchers.resetMain()
    }
    
    @Test
    fun `当加载产品时 应显示加载状态`() = runTest {
        // 模拟延时
        coEvery { mockRepo.getFeaturedProducts() } coAnswers {
            delay(1000)
            listOf(Product("1", "Sample"))
        }
        
        viewModel = ProductViewModel(mockRepo)
        viewModel.loadProducts()
        
        // 使用Turbine测试流
        viewModel.loading.test {
            // 验证初始状态
            assertEquals(false, awaitItem())
            // 验证加载状态
            assertEquals(true, awaitItem())
            // 验证完成状态
            assertEquals(false, awaitItem())
            cancelAndConsumeRemainingEvents()
        }
        
        viewModel.data.test {
            // 验证初始状态
            assertEquals(null, awaitItem())
            // 验证产品数据
            assertEquals(1, awaitItem()?.size)
            cancelAndConsumeRemainingEvents()
        }
    }
    
    @Test
    fun `加载失败时应正确处理错误`() = runTest {
        val testException = RuntimeException("Network error")
        coEvery { mockRepo.getFeaturedProducts() } throws testException
        
        viewModel = ProductViewModel(mockRepo)
        viewModel.loadProducts()
        
        viewModel.error.test {
            // 验证初始状态
            assertEquals(null, awaitItem())
            // 验证错误状态
            assertSame(testException, awaitItem())
            cancelAndConsumeRemainingEvents()
        }
    }
}

9. 跨平台方案:KMM 中的 ViewModel

共享业务逻辑实现

kotlin 复制代码
// 通用模块 - commonMain
expect abstract class KMMViewModel() {
    val viewModelScope: CoroutineScope
    
    protected open fun onCleared()
}

// Android 实现 - androidMain
actual abstract class KMMViewModel actual constructor() : ViewModel() {
    actual override val viewModelScope: CoroutineScope = this.viewModelScope
    
    actual override fun onCleared() {
        super.onCleared()
    }
}

// iOS 实现 - iosMain
actual abstract class KMMViewModel actual constructor() {
    private val coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Main)
    actual val viewModelScope: CoroutineScope = coroutineScope
    
    actual open fun onCleared() {
        coroutineScope.cancel()
    }
}

// 使用示例
class SharedProductViewModel : KMMViewModel() {
    private val _products = MutableStateFlow<List<Product>>(emptyList())
    val products: StateFlow<List<Product>> = _products.asStateFlow()
    
    fun loadProducts() {
        viewModelScope.launch {
            val result = repository.getProducts()
            _products.value = result
        }
    }
    
    override fun onCleared() {
        repository.cancelRequests()
        super.onCleared()
    }
}

架构选择决策树

graph TD A[需求场景] --> B{需要跨组件共享?} B -->|是| C[使用SharedViewModel] B -->|否| D{需要访问应用上下文?} D -->|是| E[使用AndroidViewModel] D -->|否| F{需要保存状态?} F -->|是| G[使用SavedStateViewModel] F -->|否| H{需要依赖注入?} H -->|是| I[使用HiltViewModel] H -->|否| J{Jetpack Compose项目?} J -->|是| K[使用Compose viewModel] J -->|否| L[使用普通ViewModel] C --> M{在导航图中共享?} M -->|是| N[使用navGraphViewModels] M -->|否| O[使用activityViewModels] H --> P{需要多模块依赖?} P -->|是| Q[使用Hilt自定义组件]

通过掌握这些进阶 ViewModel 模式,可以构建:

  • 复杂状态管理:处理多步骤流程和事务性操作
  • 跨平台架构:在 Android、iOS 和 Web 上共享核心业务逻辑
  • 高性能应用:优化资源使用和响应速度
  • 可测试系统:支持高级调试和时间旅行测试
  • 现代 UI 架构:无缝集成 Compose 和 Fragments

实际应用时根据项目需求混合使用这些模式,例如:

  • 电商应用 :主界面用 SharedViewModel,支付流程用 SavedStateViewModel
  • 社交媒体 :用户数据用 HiltViewModel,实时消息用 Flow 驱动
  • 跨平台应用 :核心逻辑用 KMMViewModel,平台特定功能用原生实现
相关推荐
开发之奋斗人生3 小时前
android关于pthread的使用过程
android·pthread
wu_android4 小时前
Android 视图系统入门指南
android
淡淡的香烟4 小时前
Android11 Launcher3实现去掉抽屉改为单层
android
火柴就是我4 小时前
每日见闻之THREE.PerspectiveCamera的含义
android
小书房5 小时前
Android的Dalvik和ART
android·aot·jit·art·dalvik
夏日玲子5 小时前
Monkey 测试的基本概念及常用命令(Android )
android
whysqwhw6 小时前
Transcoder代码学习-项目构建
android
夕泠爱吃糖6 小时前
Linux 文件内容的查询与统计
android·linux·c#
yzpyzp6 小时前
Kotlin的MutableList和ArrayList区别
android·kotlin
用户2018792831677 小时前
故事:《安卓公司的消息快递系统》
android