在 Android 开发中,Kotlin 协程与 ViewModel 的结合是现代应用架构的核心。这种组合提供了高效、简洁的异步处理解决方案,同时保持代码的清晰和可维护性。
生命周期感知:协程在 ViewModel 作用域内自动取消,避免内存泄漏
简化异步代码:用同步方式编写异步逻辑
错误处理:统一异常处理机制
线程管理:轻松切换线程上下文
dependencies {
// ViewModel 和协程支持
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2"
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.6.2"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3"
}
class UserViewModel : ViewModel() {
private val _users = MutableStateFlow>(emptyList())
val users: StateFlow> = _users
private val _loading = MutableStateFlow(false)
val loading: StateFlow = _loading
private val _error = MutableSharedFlow()
val error: SharedFlow = _error
// 使用 viewModelScope 启动协程
fun loadUsers() {
viewModelScope.launch {
try {
_loading.value = true
val result = userRepository.getUsers() // 挂起函数
_users.value = result
} catch (e: Exception) {
_error.emit("加载失败: ${e.message}")
} finally {
_loading.value = false
}
}
}
}
class DataViewModel : ViewModel() {
private val _searchQuery = MutableStateFlow("")
val searchResults: StateFlow> = _searchQuery
.debounce(300) // 防抖处理
.distinctUntilChanged()
.filter { it.length > 2 }
.flatMapLatest { query ->
flow {
emit(emptyList()) // 显示加载状态
emit(repository.searchItems(query))
}
}
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5000),
initialValue = emptyList()
)
fun search(query: String) {
_searchQuery.value = query
}
}
fun loadUserDetails(userId: String) {
viewModelScope.launch {
_loading.value = true
// 并行执行多个请求
val userDeferred = async { userRepo.getUser(userId) }
val postsDeferred = async { postRepo.getPosts(userId) }
val friendsDeferred = async { friendRepo.getFriends(userId) }
try {
val user = userDeferred.await()
val posts = postsDeferred.await()
val friends = friendsDeferred.await()
_userDetails.value = UserDetails(user, posts, friends)
} catch (e: Exception) {
_error.emit("加载详情失败")
} finally {
_loading.value = false
}
}
}
kotlin
fun fetchDataWithTimeout() {
viewModelScope.launch {
try {
val result = withTimeout(10_000) { // 10秒超时
apiService.fetchData()
}
_data.value = result
} catch (e: TimeoutCancellationException) {
_error.emit("请求超时")
}
}
}
// UserViewModel.kt
class UserViewModel(private val userRepo: UserRepository) : ViewModel() {
private val _users = MutableStateFlow>(emptyList())
val users: StateFlow> = _users
fun loadUsers() {
viewModelScope.launch {
_users.value = userRepo.getUsers()
}
}
}
// UserRepository.kt
class UserRepository(private val api: UserApi) {
suspend fun getUsers(): List {
return api.getUsers().mapToDomain()
}
}
// 扩展函数简化错误处理
fun ViewModel.launchWithErrorHandling(
onError: (Throwable) -> Unit = { _ -> },
block: suspend () -> T
) {
viewModelScope.launch {
try {
block()
} catch (e: Exception) {
onError(e)
}
}
}
// 使用示例
fun loadData() {
launchWithErrorHandling(
onError = { e -> _error.emit("Error: ${e.message}") }
) {
_data.value = repository.fetchData()
}
}
@OptIn(ExperimentalCoroutinesApi::class)
class UserViewModelTest {
@get:Rule
val rule = InstantTaskExecutorRule()
private lateinit var viewModel: UserViewModel
private val testDispatcher = StandardTestDispatcher()
@Before
fun setup() {
Dispatchers.setMain(testDispatcher)
viewModel = UserViewModel(FakeUserRepository())
}
@After
fun tearDown() {
Dispatchers.resetMain()
}
@Test
fun `loadUsers should update state`() = runTest {
viewModel.loadUsers()
testDispatcher.scheduler.advanceUntilIdle()
assertEquals(3, viewModel.users.value.size)
}
}
fun startLongRunningTask() {
viewModelScope.launch {
// 定期检查协程是否活跃
for (i in 1..100) {
ensureActive() // 如果协程被取消则抛出异常
// 或者手动检查
if (!isActive) return@launch
// 执行任务
delay(1000)
}
}
}
场景 | 推荐选择 | 说明 |
---|---|---|
一次性数据 | StateFlow | 简单状态管理 |
事件处理 | SharedFlow | 避免事件丢失 |
复杂数据流 | Flow | 冷流,按需执行 |
响应式UI | StateFlow/Flow | 结合Lifecycle |
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
lifecycleScope.launch {
// 当Activity在后台时暂停收集
repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.users.collect { users ->
updateUI(users)
}
}
}
}
}
使用缓冲:
val events = MutableSharedFlow(
extraBufferCapacity = 64,
onBufferOverflow = BufferOverflow.DROP_OLDEST
)
避免重复启动协程:
private var searchJob: Job? = null
fun search(query: String) {
searchJob?.cancel()
searchJob = viewModelScope.launch {
// 执行搜索
}
}
使用协程调度器优化:
viewModelScope.launch(Dispatchers.Default) {
// CPU密集型操作
}
viewModelScope.launch(Dispatchers.IO) {
// IO操作
}
viewModelScope.launch(Dispatchers.Main.immediate) {
// UI更新
}
Kotlin 协程与 ViewModel 的结合为 Android 开发提供了强大的异步处理能力:
使用 viewModelScope
确保协程生命周期与 ViewModel 一致
结合 StateFlow
/SharedFlow
实现响应式 UI
遵循单一职责原则,分离业务逻辑
采用结构化并发简化错误处理和资源管理
掌握这些技术将使你能够构建更加健壮、高效且易于维护的 Android 应用程序。