跳转到正文
YGJoon
返回

Android Kotlin Flow 数据流与状态管理

编辑文章

冷流与热流

类型代表实现特点
冷流 Coldflow {}、asFlow()每次订阅重新执行,惰性、无粘性
热流 HotStateFlow、SharedFlow持续存在,订阅者共享数据
val coldFlow = flow {
    emit(api.fetch())
}

val hotState = MutableStateFlow(HomeUiState.Loading)

Flow 基础操作

val result = flowOf(1, 2, 3)
    .map { it * 2 }
    .filter { it > 2 }
    .onEach { Timber.d("value=$it") }
    .toList()

StateFlow 与 UI 状态

sealed interface HomeUiState {
    data object Loading : HomeUiState
    data class Success(val data: List<Article>) : HomeUiState
    data class Error(val error: Throwable) : HomeUiState
}

class HomeViewModel @Inject constructor(
    private val repository: HomeRepository
) : ViewModel() {

    private val _uiState = MutableStateFlow<HomeUiState>(HomeUiState.Loading)
    val uiState: StateFlow<HomeUiState> = _uiState.asStateFlow()

    fun refresh() = viewModelScope.launch {
        repository.observeArticles()
            .map<HomeResult, HomeUiState> { HomeUiState.Success(it.list) }
            .catch { emit(HomeUiState.Error(it)) }
            .collect { _uiState.value = it }
    }
}

SharedFlow 与事件总线

class EventBus {
    private val _events = MutableSharedFlow<AppEvent>(extraBufferCapacity = 64)
    val events: SharedFlow<AppEvent> = _events.asSharedFlow()

    suspend fun emit(event: AppEvent) {
        _events.emit(event)
    }
}

viewModelScope.launch {
    eventBus.events.filterIsInstance<AppEvent.Toast>().collect { event ->
        _toastState.value = event.message
    }
}

背压策略与节流

searchQuery
    .debounce(300)
    .distinctUntilChanged()
    .flatMapLatest { keyword -> repository.search(keyword) }
    .conflate() // 仅保留最新值,丢弃旧值
    .collect { result -> render(result) }

Flow 与 Compose

@Composable
fun HomeScreen(viewModel: HomeViewModel = hiltViewModel()) {
    val state by viewModel.uiState.collectAsStateWithLifecycle()
    when (state) {
        is HomeUiState.Loading -> LoadingView()
        is HomeUiState.Success -> ArticleList(state.data)
        is HomeUiState.Error -> ErrorView(state.error)
    }
}

多数据源合并

val uiState = combine(
    userRepository.observeUser(),
    configRepository.observeConfig()
) { user, config ->
    UiState(user = user, config = config)
}.stateIn(
    scope = viewModelScope,
    started = SharingStarted.WhileSubscribed(5000),
    initialValue = UiState()
)

测试 Flow

@Test
fun `emit states`() = runTest {
    val flow = repository.observeArticles()
    val collectJob = launch(UnconfinedTestDispatcher()) {
        flow.test {
            assertEquals(HomeUiState.Loading, awaitItem())
            cancelAndIgnoreRemainingEvents()
        }
    }
    collectJob.join()
}

常见问题

问题原因解决方案
shareIn 内存泄漏scope 生命周期过长使用 viewModelScope 或手动取消
collect 阻塞主线程在主线程执行了耗时操作添加 flowOn(Dispatchers.IO) 或 withContext
重复请求未使用 distinctUntilChanged比较数据差异,或在 Repository 层缓存

总结

  1. Flow 提供统一的响应式数据流模型,结合 StateFlow/SharedFlow 可覆盖大部分 UI 状态与事件需求。
  2. 借助 combine、flatMapLatest 等操作符,能够灵活拼装多数据源。
  3. 管理好生命周期、背压策略与测试,是 Flow 在实际项目中稳定运行的关键。

下一篇将探讨 Kotlin 在 Jetpack Compose 中的应用与最佳实战。


编辑文章
分享这篇文章:

上一篇
Android Kotlin Flow 高级操作符与背压控制
下一篇
Android Kotlin DataStore 配置与迁移