扩展 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,平台特定功能用原生实现
相关推荐
夏沫琅琊1 小时前
Android 各类日志全面解析(含特点、分析方法、实战案例)
android
程序员JerrySUN1 小时前
OP-TEE + YOLOv8:从“加密权重”到“内存中解密并推理”的完整实战记录
android·java·开发语言·redis·yolo·架构
TeleostNaCl2 小时前
Android | 启用 TextView 跑马灯效果的方法
android·经验分享·android runtime
TheNextByte13 小时前
Android USB文件传输无法使用?5种解决方法
android
quanyechacsdn4 小时前
Android Studio创建库文件用jitpack构建后使用implementation方式引用
android·ide·kotlin·android studio·implementation·android 库文件·使用jitpack
程序员陆业聪5 小时前
聊聊2026年Android开发会是什么样
android
编程大师哥5 小时前
Android分层
android
极客小云7 小时前
【深入理解 Android 中的 build.gradle 文件】
android·安卓·安全架构·安全性测试
Juskey iii7 小时前
Android Studio Electric Eel | 2022.1.1 Patch 2 版本下载
android·ide·android studio
Android技术之家7 小时前
2025年度Android行业总结:AI驱动生态重构,跨端融合开启新篇
android·人工智能·重构