Kotlin_Flow_完整使用指南

Kotlin Flow 完整使用指南

目录

  1. [Flow 基础](#Flow 基础)
  2. [Flow 操作符详解](#Flow 操作符详解)
  3. [StateFlow vs SharedFlow](#StateFlow vs SharedFlow)
  4. [Compose 中的 Flow 使用](#Compose 中的 Flow 使用)
  5. 状态与流的转换
  6. 实战最佳实践

Flow 基础

什么是 Flow?

Flow 是 Kotlin 协程库中的冷流(Cold Stream),用于异步返回多个值的序列。

kotlin 复制代码
// 创建一个简单的 Flow
val simpleFlow = flow {
    emit(1)
    delay(100)
    emit(2)
    delay(100)
    emit(3)
}

// 收集 Flow
simpleFlow.collect { value ->
    println(value)  // 输出: 1, 2, 3
}

Flow 的特点

  • 冷流:只有被收集时才会执行
  • 异步:支持挂起函数
  • 顺序:按顺序发射值
  • 可取消:支持协程取消
  • 异常安全:内置异常处理

Flow 的三个角色

kotlin 复制代码
flow {           // 1️⃣ 生产者(Builder)
    emit(1)
}
.map { it * 2 }  // 2️⃣ 中间操作符(Intermediate)
.collect {       // 3️⃣ 消费者(Terminal)
    println(it)
}

Flow 操作符详解

1. 创建操作符

flow { } - 基础构建器
kotlin 复制代码
val myFlow = flow {
    emit(1)
    delay(100)
    emit(2)
}
flowOf() - 固定值
kotlin 复制代码
val fixedFlow = flowOf(1, 2, 3, 4, 5)
asFlow() - 集合转 Flow
kotlin 复制代码
val listFlow = listOf(1, 2, 3).asFlow()
val rangeFlow = (1..5).asFlow()
channelFlow { } - 并发发射
kotlin 复制代码
val channelFlow = channelFlow {
    launch { send(1) }
    launch { send(2) }
}
callbackFlow { } - 回调转 Flow
kotlin 复制代码
fun listenToSensor() = callbackFlow {
    val listener = object : SensorListener {
        override fun onData(data: Int) {
            trySend(data)
        }
    }
    sensor.registerListener(listener)
    awaitClose { sensor.unregisterListener(listener) }
}

2. 转换操作符

map - 一对一转换
kotlin 复制代码
flowOf(1, 2, 3)
    .map { it * 2 }
    .collect { println(it) }  // 2, 4, 6
mapNotNull - 转换并过滤 null
kotlin 复制代码
flowOf(1, null, 3)
    .mapNotNull { it }
    .collect { println(it) }  // 1, 3
transform - 自定义转换(可发射多个值)
kotlin 复制代码
flowOf(1, 2)
    .transform { value ->
        emit(value)       // 发射原值
        emit(value * 10)  // 再发射一个转换值
    }
    .collect { println(it) }  // 1, 10, 2, 20
scan - 累积转换
kotlin 复制代码
flowOf(1, 2, 3)
    .scan(0) { acc, value -> acc + value }
    .collect { println(it) }  // 0, 1, 3, 6

3. flatMap 系列(流的转换)

flatMapConcat - 顺序展平
kotlin 复制代码
flowOf(1, 2)
    .flatMapConcat { value ->
        flow {
            emit("A$value")
            delay(100)
            emit("B$value")
        }
    }
    .collect { println(it) }  // A1, B1, A2, B2
flatMapMerge - 并发展平
kotlin 复制代码
flowOf(1, 2)
    .flatMapMerge { value ->
        flow {
            delay(100 - value * 10)
            emit("A$value")
        }
    }
    .collect { println(it) }  // A2, A1(并发)
flatMapLatest - 保留最新(⭐ 常用)
kotlin 复制代码
flow {
    emit(1)
    delay(50)
    emit(2)  // 取消前一个
}
.flatMapLatest { value ->
    flow {
        delay(100)
        emit("A$value")
    }
}
.collect { println(it) }  // 只有 A2

使用场景:

  • 🔍 搜索(取消旧的搜索请求)
  • 🔄 实时数据刷新
  • 📱 页面切换(取消旧页面的数据加载)

4. 过滤操作符

filter - 条件过滤
kotlin 复制代码
flowOf(1, 2, 3, 4, 5)
    .filter { it % 2 == 0 }
    .collect { println(it) }  // 2, 4
filterNot - 反向过滤
kotlin 复制代码
flowOf(1, 2, 3, 4, 5)
    .filterNot { it % 2 == 0 }
    .collect { println(it) }  // 1, 3, 5
filterNotNull - 过滤 null
kotlin 复制代码
flowOf(1, null, 3)
    .filterNotNull()
    .collect { println(it) }  // 1, 3
distinctUntilChanged - 去除连续重复
kotlin 复制代码
flowOf(1, 1, 2, 2, 3, 1)
    .distinctUntilChanged()
    .collect { println(it) }  // 1, 2, 3, 1
debounce - 防抖(⭐ 常用于搜索)
kotlin 复制代码
searchQueryFlow
    .debounce(300)  // 等待 300ms 无新输入
    .collect { query ->
        performSearch(query)
    }
sample - 采样(固定时间间隔取最新值)
kotlin 复制代码
flow {
    repeat(10) {
        emit(it)
        delay(50)
    }
}
.sample(150)  // 每 150ms 采样一次
.collect { println(it) }  // 2, 5, 8

5. 组合操作符

combine - 合并多个流(⭐ 最常用)
kotlin 复制代码
val flow1 = flowOf(1, 2, 3)
val flow2 = flowOf("A", "B", "C")

combine(flow1, flow2) { num, str ->
    "$str$num"
}
.collect { println(it) }  // 任意一个流发射都会触发组合

// 多个流的组合
combine(userFlow, settingsFlow, notificationsFlow) { user, settings, notifications ->
    HomeScreenState(user, settings, notifications)
}
zip - 配对(一对一)
kotlin 复制代码
val flow1 = flowOf(1, 2, 3)
val flow2 = flowOf("A", "B")

flow1.zip(flow2) { num, str ->
    "$str$num"
}
.collect { println(it) }  // A1, B2(只配对两次)
merge - 合并(先到先得)
kotlin 复制代码
val flow1 = flow { delay(100); emit(1) }
val flow2 = flow { delay(50); emit(2) }

merge(flow1, flow2)
    .collect { println(it) }  // 2, 1(按时间顺序)

6. 限制操作符

take - 只取前 N 个
kotlin 复制代码
flowOf(1, 2, 3, 4, 5)
    .take(3)
    .collect { println(it) }  // 1, 2, 3
takeWhile - 条件满足时继续取
kotlin 复制代码
flowOf(1, 2, 3, 4, 5)
    .takeWhile { it < 4 }
    .collect { println(it) }  // 1, 2, 3
drop - 跳过前 N 个
kotlin 复制代码
flowOf(1, 2, 3, 4, 5)
    .drop(2)
    .collect { println(it) }  // 3, 4, 5
dropWhile - 条件满足时跳过
kotlin 复制代码
flowOf(1, 2, 3, 4, 5)
    .dropWhile { it < 3 }
    .collect { println(it) }  // 3, 4, 5

7. 异常处理

catch - 捕获上游异常(⭐ 重要)
kotlin 复制代码
flow {
    emit(1)
    throw Exception("Error!")
}
.catch { e ->
    println("Caught: ${e.message}")
    emit(-1)  // 可以发射默认值
}
.collect { println(it) }  // 1, -1

注意: catch 只能捕获上游的异常!

kotlin 复制代码
flow { emit(1) }
    .map { throw Exception("Error in map") }
    .catch { emit(-1) }  // ✅ 可以捕获
    .collect { throw Exception("Error in collect") }  // ❌ 无法捕获
retry - 重试
kotlin 复制代码
var attempt = 0
flow {
    attempt++
    if (attempt < 3) throw Exception("Fail $attempt")
    emit("Success")
}
.retry(3)  // 最多重试 3 次
.collect { println(it) }
retryWhen - 条件重试
kotlin 复制代码
flow { 
    throw IOException("Network error")
}
.retryWhen { cause, attempt ->
    if (cause is IOException && attempt < 3) {
        delay(1000 * attempt)  // 指数退避
        true  // 重试
    } else {
        false  // 不重试
    }
}
.collect()

8. 生命周期操作符

onStart - 流开始时
kotlin 复制代码
flowOf(1, 2, 3)
    .onStart { 
        println("Flow started")
        emit(0)  // 可以在开始前发射值
    }
    .collect { println(it) }  // 0, 1, 2, 3
onCompletion - 流完成时
kotlin 复制代码
flowOf(1, 2, 3)
    .onCompletion { cause ->
        if (cause == null) println("Completed successfully")
        else println("Completed with error: $cause")
    }
    .collect { println(it) }
onEach - 每个元素发射前
kotlin 复制代码
flowOf(1, 2, 3)
    .onEach { println("About to emit: $it") }
    .collect { println("Collected: $it") }
onEmpty - 流为空时
kotlin 复制代码
emptyFlow<Int>()
    .onEmpty { emit(-1) }
    .collect { println(it) }  // -1

9. 终端操作符(收集)

collect - 基础收集
kotlin 复制代码
flow.collect { value ->
    println(value)
}
collectLatest - 只处理最新值
kotlin 复制代码
flow {
    emit(1)
    delay(100)
    emit(2)
}
.collectLatest { value ->
    delay(200)  // 处理时间
    println(value)  // 只打印 2(1 被取消)
}
toList / toSet - 转换为集合
kotlin 复制代码
val list = flowOf(1, 2, 3).toList()  // [1, 2, 3]
val set = flowOf(1, 1, 2).toSet()    // {1, 2}
first / firstOrNull - 获取第一个
kotlin 复制代码
val first = flowOf(1, 2, 3).first()  // 1
val firstOrNull = emptyFlow<Int>().firstOrNull()  // null
single - 获取唯一值
kotlin 复制代码
val single = flowOf(1).single()  // 1
// flowOf(1, 2).single()  // 抛出异常
reduce / fold - 归约
kotlin 复制代码
val sum = flowOf(1, 2, 3).reduce { acc, value -> acc + value }  // 6
val product = flowOf(1, 2, 3).fold(10) { acc, value -> acc * value }  // 60

10. 上下文切换

flowOn - 更改上游的调度器(⭐ 重要)
kotlin 复制代码
flow {
    println("Emitting on: ${Thread.currentThread().name}")
    emit(1)
}
.flowOn(Dispatchers.IO)  // 上游在 IO 线程
.map {
    println("Mapping on: ${Thread.currentThread().name}")
    it * 2
}
.collect {  // 在调用者的上下文
    println("Collecting on: ${Thread.currentThread().name}")
}
buffer - 缓冲(提高性能)
kotlin 复制代码
flow {
    repeat(3) {
        delay(100)
        emit(it)
    }
}
.buffer()  // 生产者不等待消费者
.collect {
    delay(300)
    println(it)
}
conflate - 合并(跳过中间值)
kotlin 复制代码
flow {
    repeat(10) {
        delay(100)
        emit(it)
    }
}
.conflate()  // 消费慢时跳过中间值
.collect {
    delay(300)
    println(it)  // 可能只打印 0, 3, 6, 9
}

StateFlow vs SharedFlow

对比表

特性 StateFlow SharedFlow
类型 热流 热流
初始值 必须有 可选
重放 永远是 1 可配置(0-N)
去重 ✅ 自动去重 ❌ 不去重
用途 状态管理 事件传递
Compose collectAsState() collectAsState()

StateFlow - 状态管理

kotlin 复制代码
// 1️⃣ 创建 StateFlow
class MyViewModel : ViewModel() {
    private val _uiState = MutableStateFlow(UiState())
    val uiState: StateFlow<UiState> = _uiState.asStateFlow()
    
    fun updateState() {
        _uiState.value = UiState(data = "New data")
        // 或使用 update
        _uiState.update { it.copy(data = "New data") }
    }
}

// 2️⃣ 在 Compose 中使用
@Composable
fun MyScreen(viewModel: MyViewModel = hiltViewModel()) {
    val uiState by viewModel.uiState.collectAsState()
    
    Text(text = uiState.data)
}

特点:

  • ✅ 永远有值(初始值)
  • ✅ 自动去重(相同值不会通知)
  • ✅ 新订阅者立即获得当前值
  • ✅ 适合 UI 状态管理

SharedFlow - 事件传递

kotlin 复制代码
// 1️⃣ 创建 SharedFlow
class MyViewModel : ViewModel() {
    private val _events = MutableSharedFlow<UiEvent>()
    val events: SharedFlow<UiEvent> = _events.asSharedFlow()
    
    fun sendEvent() {
        viewModelScope.launch {
            _events.emit(UiEvent.ShowToast("Hello"))
        }
    }
}

// 2️⃣ 在 Compose 中使用
@Composable
fun MyScreen(viewModel: MyViewModel = hiltViewModel()) {
    val context = LocalContext.current
    
    LaunchedEffect(Unit) {
        viewModel.events.collect { event ->
            when (event) {
                is UiEvent.ShowToast -> {
                    Toast.makeText(context, event.message, Toast.LENGTH_SHORT).show()
                }
            }
        }
    }
}

特点:

  • ✅ 可配置重放数量
  • ✅ 不去重(相同事件也会发送)
  • ✅ 适合一次性事件(导航、Toast)
  • ⚠️ 新订阅者不会获得历史值(除非配置 replay)

配置 SharedFlow

kotlin 复制代码
MutableSharedFlow<Event>(
    replay = 1,        // 重放最近 1 个事件给新订阅者
    extraBufferCapacity = 64,  // 额外缓冲区
    onBufferOverflow = BufferOverflow.DROP_OLDEST  // 缓冲区满时策略
)

Compose 中的 Flow 使用

1. collectAsState - 转换为 State

kotlin 复制代码
@Composable
fun MyScreen(viewModel: MyViewModel = hiltViewModel()) {
    // 基础用法
    val uiState by viewModel.uiState.collectAsState()
    
    // 指定初始值
    val count by viewModel.countFlow.collectAsState(initial = 0)
    
    Text(text = "Count: $count")
}

2. collectAsStateWithLifecycle - 生命周期感知(⭐ 推荐)

kotlin 复制代码
@Composable
fun MyScreen(viewModel: MyViewModel = hiltViewModel()) {
    // 自动在 STARTED 时开始收集,STOPPED 时停止
    val uiState by viewModel.uiState.collectAsStateWithLifecycle()
    
    // 自定义生命周期
    val data by viewModel.dataFlow.collectAsStateWithLifecycle(
        minActiveState = Lifecycle.State.RESUMED
    )
    
    Text(text = uiState.data)
}

优势:

  • ✅ 节省资源(后台时停止收集)
  • ✅ 避免后台崩溃
  • ✅ 更好的性能

3. LaunchedEffect - 副作用收集

kotlin 复制代码
@Composable
fun MyScreen(viewModel: MyViewModel = hiltViewModel()) {
    val context = LocalContext.current
    
    // 收集一次性事件
    LaunchedEffect(Unit) {
        viewModel.events.collect { event ->
            when (event) {
                is Event.Navigate -> {
                    // 导航
                }
                is Event.ShowToast -> {
                    Toast.makeText(context, event.message, Toast.LENGTH_SHORT).show()
                }
            }
        }
    }
}

4. produceState - 转换任意数据源为 State

kotlin 复制代码
@Composable
fun loadNetworkImage(url: String): State<Resource<Image>> {
    return produceState<Resource<Image>>(initialValue = Resource.Loading) {
        value = try {
            Resource.Success(loadImage(url))
        } catch (e: Exception) {
            Resource.Error(e)
        }
    }
}

// 使用
@Composable
fun ImageScreen() {
    val imageResource by loadNetworkImage("https://example.com/image.jpg")
    
    when (imageResource) {
        is Resource.Loading -> CircularProgressIndicator()
        is Resource.Success -> Image(imageResource.data)
        is Resource.Error -> Text("Error: ${imageResource.error}")
    }
}

5. snapshotFlow - Compose State 转 Flow

kotlin 复制代码
@Composable
fun SearchScreen() {
    var searchQuery by remember { mutableStateOf("") }
    
    LaunchedEffect(Unit) {
        snapshotFlow { searchQuery }
            .debounce(300)
            .filter { it.isNotEmpty() }
            .collectLatest { query ->
                // 执行搜索
            }
    }
    
    TextField(
        value = searchQuery,
        onValueChange = { searchQuery = it }
    )
}

6. derivedStateOf - 派生状态

kotlin 复制代码
@Composable
fun ListScreen(items: List<Item>) {
    val listState = rememberLazyListState()
    
    // 派生状态,只在需要时重组
    val isScrollingUp by remember {
        derivedStateOf {
            listState.firstVisibleItemIndex > 0
        }
    }
    
    AnimatedVisibility(visible = !isScrollingUp) {
        FloatingActionButton(onClick = { }) {
            Icon(Icons.Default.Add, null)
        }
    }
}

状态与流的转换

Flow → StateFlow

kotlin 复制代码
// 方法1: stateIn(推荐)
val stateFlow: StateFlow<Int> = flow { emit(1) }
    .stateIn(
        scope = viewModelScope,
        started = SharingStarted.WhileSubscribed(5000),
        initialValue = 0
    )

// 方法2: MutableStateFlow 手动收集
class MyViewModel : ViewModel() {
    private val _state = MutableStateFlow(0)
    val state: StateFlow<Int> = _state
    
    init {
        viewModelScope.launch {
            flow { emit(1) }.collect { value ->
                _state.value = value
            }
        }
    }
}

Flow → SharedFlow

kotlin 复制代码
val sharedFlow: SharedFlow<Int> = flow { emit(1) }
    .shareIn(
        scope = viewModelScope,
        started = SharingStarted.WhileSubscribed(5000),
        replay = 1
    )

StateFlow / SharedFlow → Flow

kotlin 复制代码
// StateFlow 和 SharedFlow 本身就是 Flow
val stateFlow: StateFlow<Int> = MutableStateFlow(0)
val flow: Flow<Int> = stateFlow  // 自动转换

Compose State → Flow

kotlin 复制代码
@Composable
fun MyScreen() {
    var text by remember { mutableStateOf("") }
    
    // 方法1: snapshotFlow
    LaunchedEffect(Unit) {
        snapshotFlow { text }
            .debounce(300)
            .collect { /* ... */ }
    }
    
    // 方法2: derivedStateOf + snapshotFlow
    val textLength by remember {
        derivedStateOf { text.length }
    }
}

Flow → Compose State

kotlin 复制代码
@Composable
fun MyScreen(viewModel: MyViewModel) {
    // 方法1: collectAsState
    val state by viewModel.stateFlow.collectAsState()
    
    // 方法2: collectAsStateWithLifecycle(推荐)
    val state by viewModel.stateFlow.collectAsStateWithLifecycle()
    
    // 方法3: produceState
    val state by produceState(initialValue = 0) {
        viewModel.flow.collect { value = it }
    }
}

SharingStarted 策略详解

WhileSubscribed - 有订阅者时活跃(⭐ 最常用)

kotlin 复制代码
SharingStarted.WhileSubscribed(
    stopTimeoutMillis = 5000,  // 最后一个订阅者离开后等待 5 秒
    replayExpirationMillis = 0  // 重放过期时间
)

场景: UI 状态管理,节省资源

Eagerly - 立即开始

kotlin 复制代码
SharingStarted.Eagerly

场景: 需要预加载的数据

Lazily - 第一个订阅者到来时开始

kotlin 复制代码
SharingStarted.Lazily

场景: 延迟初始化,但希望保留值


实战最佳实践

1. ViewModel 中的状态管理

kotlin 复制代码
@HiltViewModel
class MyViewModel @Inject constructor(
    private val repository: MyRepository
) : ViewModel() {
    
    // ✅ 使用 StateFlow 管理 UI 状态
    private val _uiState = MutableStateFlow(UiState())
    val uiState: StateFlow<UiState> = _uiState.asStateFlow()
    
    // ✅ 使用 SharedFlow 管理一次性事件
    private val _events = MutableSharedFlow<UiEvent>()
    val events: SharedFlow<UiEvent> = _events.asSharedFlow()
    
    // ✅ 使用 stateIn 转换 Flow
    val dataList: StateFlow<List<Data>> = repository.getDataFlow()
        .stateIn(
            scope = viewModelScope,
            started = SharingStarted.WhileSubscribed(5000),
            initialValue = emptyList()
        )
    
    fun loadData() {
        viewModelScope.launch {
            repository.fetchData()
                .catch { e ->
                    _events.emit(UiEvent.ShowError(e.message))
                }
                .collect { data ->
                    _uiState.update { it.copy(data = data) }
                }
        }
    }
}

2. Repository 中的数据流

kotlin 复制代码
class MyRepository @Inject constructor(
    private val apiService: ApiService,
    private val database: MyDatabase
) {
    
    // ✅ 单一数据源
    fun getDataFlow(): Flow<List<Data>> = database.dataDao()
        .getAllFlow()
        .map { entities -> entities.map { it.toDomain() } }
    
    // ✅ 网络请求 + 本地缓存
    suspend fun refreshData(): Result<Unit> = try {
        val response = apiService.getData()
        database.dataDao().insertAll(response.toEntities())
        Result.success(Unit)
    } catch (e: Exception) {
        Result.failure(e)
    }
    
    // ✅ Flow 封装网络请求
    fun getDataWithRefresh(): Flow<Resource<List<Data>>> = flow {
        emit(Resource.Loading)
        
        // 先发射缓存数据
        val cachedData = database.dataDao().getAll()
        if (cachedData.isNotEmpty()) {
            emit(Resource.Success(cachedData.map { it.toDomain() }))
        }
        
        // 再请求网络
        try {
            val response = apiService.getData()
            database.dataDao().insertAll(response.toEntities())
            emit(Resource.Success(response.map { it.toDomain() }))
        } catch (e: Exception) {
            emit(Resource.Error(e))
        }
    }.flowOn(Dispatchers.IO)
}

3. 搜索功能完整实现

kotlin 复制代码
@HiltViewModel
class SearchViewModel @Inject constructor(
    private val repository: SearchRepository
) : ViewModel() {
    
    private val _searchQuery = MutableStateFlow("")
    val searchQuery: StateFlow<String> = _searchQuery.asStateFlow()
    
    val searchResults: StateFlow<List<SearchResult>> = searchQuery
        .debounce(300)                      // 防抖
        .distinctUntilChanged()             // 去重
        .filter { it.length >= 2 }          // 最少2个字符
        .flatMapLatest { query ->           // 取消之前的搜索
            repository.search(query)
                .catch { emit(emptyList()) } // 错误时返回空列表
        }
        .stateIn(
            scope = viewModelScope,
            started = SharingStarted.WhileSubscribed(5000),
            initialValue = emptyList()
        )
    
    fun updateSearchQuery(query: String) {
        _searchQuery.value = query
    }
}

// 在 Compose 中使用
@Composable
fun SearchScreen(viewModel: SearchViewModel = hiltViewModel()) {
    val query by viewModel.searchQuery.collectAsStateWithLifecycle()
    val results by viewModel.searchResults.collectAsStateWithLifecycle()
    
    Column {
        TextField(
            value = query,
            onValueChange = { viewModel.updateSearchQuery(it) },
            placeholder = { Text("Search...") }
        )
        
        LazyColumn {
            items(results) { result ->
                SearchResultItem(result)
            }
        }
    }
}

4. 多数据源组合

kotlin 复制代码
@HiltViewModel
class DashboardViewModel @Inject constructor(
    userRepository: UserRepository,
    settingsRepository: SettingsRepository,
    notificationsRepository: NotificationsRepository
) : ViewModel() {
    
    // ✅ 组合多个数据源
    val dashboardState: StateFlow<DashboardState> = combine(
        userRepository.getUserFlow(),
        settingsRepository.getSettingsFlow(),
        notificationsRepository.getNotificationsFlow()
    ) { user, settings, notifications ->
        DashboardState(
            user = user,
            settings = settings,
            unreadCount = notifications.count { !it.isRead },
            notifications = notifications
        )
    }
    .stateIn(
        scope = viewModelScope,
        started = SharingStarted.WhileSubscribed(5000),
        initialValue = DashboardState()
    )
}

5. 分页加载

kotlin 复制代码
@HiltViewModel
class ListViewModel @Inject constructor(
    private val repository: ListRepository
) : ViewModel() {
    
    private val _page = MutableStateFlow(0)
    
    private val _items = MutableStateFlow<List<Item>>(emptyList())
    val items: StateFlow<List<Item>> = _items.asStateFlow()
    
    private val _isLoading = MutableStateFlow(false)
    val isLoading: StateFlow<Boolean> = _isLoading.asStateFlow()
    
    init {
        viewModelScope.launch {
            _page
                .flatMapLatest { page ->
                    flow {
                        _isLoading.value = true
                        emit(repository.getItems(page))
                    }.onCompletion {
                        _isLoading.value = false
                    }
                }
                .catch { /* 处理错误 */ }
                .collect { newItems ->
                    _items.update { it + newItems }
                }
        }
    }
    
    fun loadNextPage() {
        if (!_isLoading.value) {
            _page.value++
        }
    }
}

性能优化技巧

1. 使用 conflate 跳过中间值

kotlin 复制代码
// 当生产者快,消费者慢时
dataFlow
    .conflate()  // 跳过中间值,只处理最新的
    .collect { /* 处理 */ }

2. 使用 buffer 提高并发

kotlin 复制代码
flow {
    repeat(10) {
        delay(100)
        emit(it)
    }
}
.buffer()  // 生产者和消费者并发执行
.collect {
    delay(300)
    println(it)
}

3. 使用 distinctUntilChanged 避免重复计算

kotlin 复制代码
stateFlow
    .distinctUntilChanged()  // 值相同时不触发
    .collect { /* 更新 UI */ }

4. 选择合适的 SharingStarted 策略

kotlin 复制代码
// ✅ 推荐:UI 不可见时停止收集
SharingStarted.WhileSubscribed(5000)

// ⚠️ 注意:一直活跃,消耗资源
SharingStarted.Eagerly

常见错误和解决方案

❌ 错误1:在 collect 中直接更新 UI

kotlin 复制代码
// ❌ 错误
viewModelScope.launch {
    flow.collect { value ->
        // 不要在这里直接更新 UI State
        _uiState.value = value
    }
}

// ✅ 正确:使用 stateIn
val uiState = flow
    .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), initialValue)

❌ 错误2:忘记使用 flowOn

kotlin 复制代码
// ❌ 可能阻塞主线程
flow {
    val data = loadDataFromNetwork()  // 网络请求
    emit(data)
}.collect { /* UI 更新 */ }

// ✅ 正确
flow {
    val data = loadDataFromNetwork()
    emit(data)
}
.flowOn(Dispatchers.IO)  // 在 IO 线程执行
.collect { /* UI 更新 */ }

❌ 错误3:在 Compose 中不使用 collectAsStateWithLifecycle

kotlin 复制代码
// ❌ 后台仍在收集
val state by viewModel.stateFlow.collectAsState()

// ✅ 后台自动停止
val state by viewModel.stateFlow.collectAsStateWithLifecycle()

❌ 错误4:SharedFlow 用于状态管理

kotlin 复制代码
// ❌ 不适合状态管理
private val _uiState = MutableSharedFlow<UiState>()

// ✅ 使用 StateFlow
private val _uiState = MutableStateFlow(UiState())

调试技巧

1. 打印流的值

kotlin 复制代码
flow
    .onEach { println("Value: $it") }
    .collect()

2. 监控流的生命周期

kotlin 复制代码
flow
    .onStart { println("Flow started") }
    .onCompletion { println("Flow completed") }
    .onEach { println("Value: $it") }
    .collect()

3. 捕获并打印异常

kotlin 复制代码
flow
    .catch { e ->
        println("Error: ${e.message}")
        e.printStackTrace()
    }
    .collect()

总结

Flow 操作符选择指南

需求 使用的操作符
简单值转换 map
流的转换 flatMapLatest
过滤值 filter
防抖搜索 debounce
组合多个流 combine
异常处理 catch
切换线程 flowOn
转为状态 stateIn

StateFlow vs SharedFlow 选择

  • StateFlow:UI 状态、配置、用户信息
  • SharedFlow:一次性事件、导航、Toast

Compose 中收集 Flow

  • 推荐collectAsStateWithLifecycle()
  • ⚠️ 备选collectAsState() (简单场景)
  • ⚠️ 事件LaunchedEffect + collect

编写日期: 2025-01-19
版本: 1.0
适用于: Kotlin 1.9+, Compose 1.5+


参考资源

相关推荐
心之伊始2 小时前
Java synchronized 锁升级全过程深度解析:从 Mark Word 到偏向锁、轻量级锁与重量级锁的 HotSpot 实现
java·开发语言·word
j***82702 小时前
【玩转全栈】----Django连接MySQL
android·mysql·django
q***06292 小时前
搭建Golang gRPC环境:protoc、protoc-gen-go 和 protoc-gen-go-grpc 工具安装教程
开发语言·后端·golang
布丁写代码3 小时前
GESP C++ 一级 2025年09月真题解析
开发语言·c++·程序人生·学习方法
GOTXX3 小时前
用Rust实现一个简易的rsync(远程文件同步)工具
开发语言·后端·rust
雨白3 小时前
深入理解 Android DocumentFile:性能陷阱与最佳实践
android
诸葛亮的芭蕉扇3 小时前
抓图巡检-底图支持绘制
开发语言·前端·javascript
inputA3 小时前
【LwIP源码学习8】netbuf源码分析
android·c语言·笔记·嵌入式硬件·学习
CHINAHEAO3 小时前
FlyEnv+Bagisto安装遇到的一些问题
android