Compose - 底层原理(五) - Compose中的Flow

491 阅读3分钟

Compose中的Flow的基本使用

常见Flow

1. StateFlow

就像一个公告板:

class 餐厅ViewModel : ViewModel() {
    // 公告板,显示当前状态
    private val _餐厅状态 = MutableStateFlow("营业中")
    val 餐厅状态 = _餐厅状态.asStateFlow()
    
    fun 更新状态(新状态: String) {
        _餐厅状态.value = 新状态  // 更新公告板
    }
}

@Composable
fun 餐厅界面(viewModel: 餐厅ViewModel) {
    // 读取公告板内容
    val 当前状态 by viewModel.餐厅状态.collectAsState()
    
    Text("餐厅现在: $当前状态")
}

工作原理:

graph TD
    A[StateFlow] -->|持有| B[当前值]
    B -->|更新| C[通知所有观察者]
    C -->|收集| D[UI更新]

2. SharedFlow

像是广播系统:

class 餐厅事件ViewModel : ViewModel() {
    // 广播系统
    private val _事件通知 = MutableSharedFlow<String>()
    val 事件通知 = _事件通知.asSharedFlow()
    
    fun 发送通知(消息: String) {
        viewModelScope.launch {
            _事件通知.emit(消息)  // 发送广播
        }
    }
}

@Composable
fun 通知监听() {
    LaunchedEffect(Unit) {
        viewModel.事件通知.collect { 消息 ->
            // 处理收到的广播
            显示提示(消息)
        }
    }
}

工作流程:

graph LR
    A[发送事件] -->|emit| B[SharedFlow]
    B -->|collect| C[订阅者1]
    B -->|collect| D[订阅者2]
    B -->|collect| E[订阅者3]

3. callbackFlow

像是把老式电话转换成现代通讯系统:

// 将传统回调转换为 Flow
val 位置更新 = callbackFlow {
    // 创建老式监听器
    val 位置监听器 = LocationListener { 位置 ->
        trySend(位置)  // 发送位置信息
    }
    
    // 注册监听
    locationManager.requestLocationUpdates(
        LocationManager.GPS_PROVIDER,
        1000L,
        1f,
        位置监听器
    )
    
    // 清理工作
    awaitClose {
        locationManager.removeUpdates(位置监听器)
    }
}

转换过程:

flowchart LR
    A[回调接口] -->|转换| B[callbackFlow]
    B -->|发送数据| C[Flow]
    C -->|收集数据| D[使用方]

4. Flow 组合使用

@Composable
fun 餐厅系统() {
    val scope = rememberCoroutineScope()
    
    // 1. 状态流 - 餐厅基本信息
    val 餐厅信息 by viewModel.餐厅状态.collectAsState()
    
    // 2. 共享流 - 实时订单通知
    LaunchedEffect(Unit) {
        viewModel.订单通知.collect { 新订单 ->
            显示通知(新订单)
        }
    }
    
    // 3. 回调流 - 处理支付回调
    val 支付状态 = remember {
        callbackFlow {
            val 支付监听器 = PaymentListener { 结果 ->
                trySend(结果)
            }
            awaitClose { 支付监听器.remove() }
        }
    }
}

实际应用场景

  1. 状态管理
class OrderViewModel : ViewModel() {
    // 订单状态
    private val _orderState = MutableStateFlow<OrderState>(OrderState.Initial)
    val orderState = _orderState.asStateFlow()
    
    // 更新订单状态
    fun updateOrder(newState: OrderState) {
        _orderState.value = newState
    }
}
  1. 事件处理
class EventViewModel : ViewModel() {
    // 一次性事件
    private val _events = MutableSharedFlow<UiEvent>()
    val events = _events.asSharedFlow()
    
    fun sendEvent(event: UiEvent) {
        viewModelScope.launch {
            _events.emit(event)
        }
    }
}
  1. 数据转换
@Composable
fun DataTransformation() {
    val data by remember {
        viewModel.dataFlow
            .map { it.process() }
            .flowOn(Dispatchers.Default)
            .collectAsState(initial = emptyList())
    }
}

Flow 的优势

  1. 响应式
  • 自动更新
  • 数据流转清晰
  • 代码更简洁
  1. 生命周期管理
  • 自动取消订阅
  • 避免内存泄露
  • 更安全
  1. 线程控制
  • 灵活切换线程
  • 性能优化
  • 避免主线程阻塞

使用建议

  1. 选择合适的 Flow
  • 状态用 StateFlow
  • 事件用 SharedFlow
  • 回调转换用 callbackFlow
  1. 错误处理
flow.catch { error ->
    // 处理错误
}.collect { data ->
    // 处理数据
}
  1. 性能优化
flow.debounce(300L)  // 防抖
    .distinctUntilChanged()  // 去重
    .flowOn(Dispatchers.IO)  // 指定线程
    .collect { /* 处理数据 */ }

这样的设计让我们能够:

  • 更好地管理应用状态
  • 处理异步操作
  • 提供更好的用户体验
  • 写出更可维护的代码

Flow的补充

1. snapshotFlow

就像是给相机加了自动拍摄功能:

@Composable
fun 自动拍照示例() {
    var pose by remember { mutableStateOf("站立") }
    
    // 当姿势改变时自动拍照
    LaunchedEffect(Unit) {
        snapshotFlow { pose }  // 监控姿势变化
            .collect { 当前姿势 ->
                // 当姿势改变时,自动拍照
                拍照(当前姿势)
            }
    }
    
    Button(onClick = { pose = "微笑" }) {
        Text("摆pose")
    }
}

工作原理:

flowchart LR
    A[Compose State] -->|转换| B[snapshotFlow]
    B -->|监控变化| C[Flow]
    C -->|收集变化| D[处理逻辑]

2. stateIn

像是给快递设置定点配送:

class 快递ViewModel : ViewModel() {
    // 普通快递流
    private val 快递跟踪 = flow {
        while(true) {
            emit(获取快递位置())
            delay(1000)
        }
    }
    
    // 转换为定点配送
    val 快递状态 = 快递跟踪
        .stateIn(
            scope = viewModelScope,
            started = SharingStarted.WhileSubscribed(5000),
            initialValue = 初始位置
        )
}

3. flowWithLifecycle

像是根据营业时间自动营业:

@Composable
fun 营业监控() {
    val lifecycle = LocalLifecycleOwner.current.lifecycle
    
    LaunchedEffect(Unit) {
        viewModel.营业数据
            .flowWithLifecycle(lifecycle)  // 只在营业时间处理
            .collect { 数据 ->
                处理数据(数据)
            }
    }
}

4. 组合使用示例

@Composable
fun 综合监控系统() {
    var searchText by remember { mutableStateOf("") }
    val scope = rememberCoroutineScope()
    
    // 1. 使用 snapshotFlow 监控搜索
    LaunchedEffect(Unit) {
        snapshotFlow { searchText }
            .debounce(300L)
            .collect { text ->
                viewModel.search(text)
            }
    }
    
    // 2. 使用 stateIn 缓存结果
    val searchResults by remember {
        viewModel.searchFlow
            .stateIn(
                scope = scope,
                started = SharingStarted.WhileSubscribed(),
                initialValue = emptyList()
            )
    }.collectAsState()
    
    // 3. 使用 SharedFlow 处理事件
    LaunchedEffect(Unit) {
        viewModel.events
            .flowWithLifecycle(lifecycle)
            .collect { event ->
                handleEvent(event)
            }
    }
}

各种 Flow 的使用场景对比:

graph TD
    A[Flow 类型选择] --> B[状态类]
    A --> C[事件类]
    A --> D[转换类]
    
    B --> B1[StateFlow]
    B --> B2[stateIn]
    
    C --> C1[SharedFlow]
    C --> C2[callbackFlow]
    
    D --> D1[snapshotFlow]
    D --> D2[flowWithLifecycle]

性能优化建议

  1. 状态合并
// 合并多个状态
val combinedState = combine(
    userFlow,
    settingsFlow
) { user, settings ->
    UserWithSettings(user, settings)
}.stateIn(
    scope = viewModelScope,
    started = SharingStarted.WhileSubscribed(5000),
    initialValue = 初始状态
)
  1. 防抖和过滤
snapshotFlow { searchText }
    .debounce(300L)  // 防抖
    .filter { it.length >= 2 }  // 过滤
    .distinctUntilChanged()  // 去重
    .collect { /* 处理 */ }
  1. 生命周期优化
flow.flowWithLifecycle(lifecycle, Lifecycle.State.STARTED)
    .collect { /* 只在 STARTED 状态处理 */ }

错误处理最佳实践

flow.catch { error ->
    // 错误处理
    _uiState.value = ErrorState(error)
}.retryWhen { cause, attempt ->
    // 重试逻辑
    attempt < 3 && cause is NetworkException
}.collect { data ->
    // 正常处理
    _uiState.value = SuccessState(data)
}

关键优化点:

  1. 选择合适的 Flow 类型
  2. 正确处理生命周期
  3. 合理使用操作符
  4. 做好错误处理
  5. 注意性能优化

这样可以:

  • 提高代码质量
  • 优化性能
  • 提供更好的用户体验
  • 减少 bug

Flow的底层实现原理

让我用生活例子解释 Flow 在 Compose 和 ViewModel 之间传递状态的底层原理:

1. 基本架构图

graph TD
    A[ViewModel] -->|StateFlow/SharedFlow| B[State Holder]
    B -->|State| C[Compose UI]
    C -->|Event| A
    
    subgraph "状态管理"
        B -->|Snapshot| D[State Registry]
        D -->|Updates| B
    end

让我用餐厅点餐的例子来解释这个架构图:

餐厅运作流程图

graph TD
    A["厨房(ViewModel)"] -->|菜品状态| B["服务员(State Holder)"]
    B -->|端菜上桌| C["顾客(Compose UI)"]
    C -->|点餐需求| A
    
    subgraph "后厨管理"
        B -->|记录订单| D["订单本(State Registry)"]
        D -->|更新状态| B
    end

通俗解释:

  1. 厨房(ViewModel)
  • 负责准备菜品
  • 管理菜品状态
  • 就像餐厅的后厨
  1. 服务员(State Holder)
  • 连接厨房和顾客
  • 记录和传递订单
  • 就像餐厅的服务员
  1. 顾客(Compose UI)
  • 点餐和享用美食
  • 提出新的需求
  • 就像餐厅的顾客
  1. 订单本(State Registry)
  • 记录所有订单信息
  • 追踪订单状态
  • 就像服务员的记录本

工作流程:

  1. 顾客点餐 → 服务员记录 → 传给厨房
  2. 厨房准备好 → 通知服务员 → 端给顾客
  3. 服务员随时记录订单状态,确保不会搞混

这样的设计确保了:

  • 订单不会搞错
  • 服务流程清晰
  • 各司其职,配合默契

就像一个运作良好的餐厅,每个角色都知道自己该做什么,整体配合默契。

2. 具体实现示例

想象一个餐厅点餐系统:

// 餐厅 ViewModel
class RestaurantViewModel : ViewModel() {
    // 1. 菜单状态(像是实时更新的菜单板)
    private val _menuState = MutableStateFlow(MenuState())
    val menuState = _menuState.asStateFlow()

    // 2. 订单事件(像是服务员传递的订单)
    private val _orderEvents = MutableSharedFlow<OrderEvent>()
    val orderEvents = _orderEvents.asSharedFlow()

    // 3. 处理点餐
    fun placeOrder(order: Order) {
        viewModelScope.launch {
            _orderEvents.emit(OrderEvent.New(order))
            updateMenu(order)
        }
    }
}

3. 状态收集过程

sequenceDiagram
    participant VM as ViewModel
    participant SH as StateHolder
    participant UI as Compose UI
    
    VM->>SH: 发送状态更新
    SH->>SH: 创建新的 Snapshot
    SH->>UI: 触发重组
    UI->>SH: 读取最新状态
    UI->>VM: 发送用户事件

4. 底层实现原理

// 1. StateFlow 的底层实现
internal class StateFlowImpl<T>(
    private var value: T
) : StateFlow<T> {
    // 存储所有订阅者
    private val subscribers = mutableListOf<FlowCollector<T>>()
    
    // 更新值并通知订阅者
    fun setValue(newValue: T) {
        value = newValue
        notifySubscribers()
    }
    
    private fun notifySubscribers() {
        subscribers.forEach { collector ->
            collector.emit(value)
        }
    }
}

// 2. Compose 中的状态收集
@Composable
fun <T> StateFlow<T>.collectAsState(): State<T> {
    // 创建 Compose 状态
    val state = remember { mutableStateOf(value) }
    
    // 在协程中收集更新
    LaunchedEffect(this) {
        collect { newValue ->
            state.value = newValue
        }
    }
    
    return state
}

5. 实际应用示例

// 1. ViewModel 部分
class OrderViewModel : ViewModel() {
    private val _orderState = MutableStateFlow(OrderState())
    val orderState = _orderState.asStateFlow()

    fun updateOrder(newOrder: Order) {
        viewModelScope.launch {
            // 1. 更新状态
            _orderState.update { currentState ->
                currentState.copy(
                    orders = currentState.orders + newOrder
                )
            }
        }
    }
}

// 2. Compose UI 部分
@Composable
fun OrderScreen(viewModel: OrderViewModel) {
    // 收集状态
    val orderState by viewModel.orderState.collectAsState()

    // UI 更新
    LazyColumn {
        items(orderState.orders) { order ->
            OrderItem(
                order = order,
                onOrderClick = { viewModel.updateOrder(it) }
            )
        }
    }
}

6. 状态传递流程

flowchart LR
    A[ViewModel State] -->|StateFlow| B[Compose State]
    B -->|Recomposition| C[UI Updates]
    C -->|User Action| D[ViewModel Event]
    D -->|State Update| A

7. 性能优化机制

class OptimizedViewModel : ViewModel() {
    // 1. 状态缓存
    private val stateCache = StateCache()

    // 2. 状态合并
    val combinedState = combine(
        dataFlow,
        configFlow
    ) { data, config ->
        DataWithConfig(data, config)
    }.stateIn(
        scope = viewModelScope,
        started = SharingStarted.WhileSubscribed(5000),
        initialValue = InitialState
    )
}

8. 状态管理最佳实践

// 1. 单一数据源
data class UiState(
    val data: List<Item> = emptyList(),
    val isLoading: Boolean = false,
    val error: String? = null
)

class BestPracticeViewModel : ViewModel() {
    private val _uiState = MutableStateFlow(UiState())
    val uiState = _uiState.asStateFlow()

    // 2. 状态更新
    fun updateState(update: (UiState) -> UiState) {
        _uiState.update(update)
    }
}

关键点总结:

  1. 状态流转过程
  • ViewModel 发出状态
  • StateHolder 管理状态
  • Compose UI 响应更新
  1. 优化机制
  • 状态快照
  • 智能重组
  • 状态合并
  1. 性能考虑
  • 避免不必要的状态更新
  • 合理使用状态缓存
  • 正确处理生命周期

这种机制确保了:

  • 单向数据流
  • 状态一致性
  • 高效的UI更新
  • 良好的性能

State状态传递的底层实现

基本流程

1.StateHolder 是什么?

StateHolder 就像餐厅里的"服务台",它:

  1. 接收厨房(ViewModel)的状态更新
  2. 暂存当前的状态(像是记录当前菜品状态的白板)
  3. 管理状态的变化(像是更新白板上的信息)

2.具体实现示例:

// StateHolder 的简化实现
class OrderStateHolder {
    // 当前状态(像是白板上的信息)
    private val _state = mutableStateOf(OrderState())
    val state: State<OrderState> = _state

    // 更新状态(像是更新白板)
    fun updateState(newState: OrderState) {
        _state.value = newState
    }
}

// 在 Compose 中使用
@Composable
fun OrderScreen(viewModel: OrderViewModel) {
    // StateHolder 在这里体现为 collectAsState() 创建的 State 对象
    val orderState by viewModel.orderState.collectAsState()

    // 使用状态
    OrderContent(
        state = orderState,
        onEvent = { viewModel.handleEvent(it) }
    )
}

3.collectAsState()的具体实现

让我详细解释 collectAsState() 的实现和作用:

1. collectAsState 的简化实现
@Composable
fun <T> Flow<T>.collectAsState(
    initial: T,
    context: CoroutineContext = EmptyCoroutineContext
): State<T> {
    // 1. 创建可变状态
    val state = remember { mutableStateOf(initial) }
    
    // 2. 在协程中收集 Flow
    LaunchedEffect(this) {
        // 切换到指定上下文
        withContext(context) {
            // 收集 Flow 的值并更新状态
            collect { value ->
                state.value = value
            }
        }
    }
    
    // 3. 返回不可变的 State
    return state
}
2. 实际使用示例
class OrderViewModel : ViewModel() {
    // StateFlow 在 ViewModel 中
    private val _orderState = MutableStateFlow(OrderState())
    val orderState = _orderState.asStateFlow()
}

@Composable
fun OrderScreen(viewModel: OrderViewModel) {
    // 1. 将 Flow 转换为 Compose 的 State
    val state by viewModel.orderState.collectAsState()
    
    // 2. 使用状态
    OrderContent(state = state)
}
3. 工作原理
graph TD
    A[Flow/StateFlow] -->|collectAsState| B[Compose State]
    B -->|重组| C[UI 更新]
    D[LaunchedEffect] -->|收集| A
    D -->|更新| B
4. collectAsState 的作用
  1. 转换器作用
// 将 Flow 转换为 Compose 可用的 State
val flowState: Flow<Int> = flow { emit(1) }
val composeState: State<Int> = flowState.collectAsState(initial = 0)
  1. 生命周期管理
LaunchedEffect(flowKey) {
    // 自动跟随 Compose 生命周期
    // 组件销毁时自动取消收集
    flow.collect { /* ... */ }
}
  1. 状态更新
// 当 Flow 发出新值时,自动触发重组
val state by flow.collectAsState(initial = "")
Text("Current value: $state")  // 自动更新 UI
5. 完整实现示例
@Composable
fun <T> Flow<T>.collectAsState(
    initial: T,
    context: CoroutineContext = EmptyCoroutineContext
): State<T> {
    // 1. 创建状态持有者
    val state = remember { mutableStateOf(initial) }
    
    // 2. 创建收集器
    LaunchedEffect(this, context) {
        // 切换上下文
        withContext(context) {
            try {
                // 收集 Flow
                collect { value ->
                    // 更新状态
                    state.value = value
                }
            } catch (e: CancellationException) {
                // 处理取消
                throw e
            } catch (e: Exception) {
                // 处理错误
                e.printStackTrace()
            }
        }
    }
    
    // 3. 返回状态
    return state
}
6. 使用建议
@Composable
fun BestPracticeExample(viewModel: MyViewModel) {
    // 1. 提供初始值
    val state by viewModel.stateFlow.collectAsState(initial = InitialState)
    
    // 2. 错误处理
    val errorState by viewModel.errorFlow
        .catch { e -> 
            // 处理错误
        }
        .collectAsState(initial = null)
    
    // 3. 生命周期感知
    val lifecycleAwareState by viewModel.dataFlow
        .flowWithLifecycle(lifecycle)
        .collectAsState(initial = emptyList())
}

collectAsState 的主要功能是:

  1. 将 Flow 转换为 Compose 的 State
  2. 自动管理订阅的生命周期
  3. 在值变化时触发重组
  4. 确保状态更新的线程安全

优点:

  • 简化了 Flow 到 Compose 的集成
  • 自动处理生命周期
  • 确保状态一致性
  • 提供线程安全的更新

这使得我们可以:

  • 方便地在 Compose 中使用 Flow
  • 无需手动管理订阅
  • 保持代码简洁清晰
  1. ViewModel 发送新状态:
viewModel.updateOrder(newOrder)  // 厨房:新订单来了
  1. StateHolder 接收并存储:
// 服务台:记录新订单状态
val state by orderFlow.collectAsState()
  1. Compose 读取状态:
// 前台:查看最新订单状态
OrderContent(state = state)

简单来说,StateHolder 就是:

  • 状态的临时存储者
  • 状态变化的管理者
  • ViewModel 和 UI 之间的桥梁

就像餐厅服务台,负责协调厨房(ViewModel)和顾客(UI)之间的信息传递。

具体代码实现

让我用具体代码解释这个状态收集过程:

1. ViewModel 发送状态更新

class OrderViewModel : ViewModel() {
    // 菜单状态
    private val _menuState = MutableStateFlow(MenuState())
    val menuState = _menuState.asStateFlow()
    
    // 更新菜单
    fun updateMenu(dish: Dish) {
        viewModelScope.launch {
            // 发送状态更新
            _menuState.update { currentState ->
                currentState.copy(
                    dishes = currentState.dishes + dish
                )
            }
        }
    }
}

2. StateHolder 创建新的 Snapshot

// Compose 内部实现的简化版本
internal class StateHolder {
    fun createSnapshot(newValue: Any) {
        // 创建状态快照
        Snapshot.withMutableSnapshot {
            // 记录新状态
            currentState.value = newValue
            // 应用快照
        }
    }
}

// 在 Compose 中使用
@Composable
fun MenuScreen(viewModel: OrderViewModel) {
    // collectAsState 内部会创建 StateHolder
    val menuState by viewModel.menuState.collectAsState()
}

3. 触发重组和读取状态

@Composable
fun MenuContent(
    viewModel: OrderViewModel
) {
    // 收集状态,触发重组
    val menuState by viewModel.menuState.collectAsState()
    
    // 读取最新状态并显示
    LazyColumn {
        items(menuState.dishes) { dish ->
            DishItem(
                dish = dish,
                onDishClick = { 
                    // 发送用户事件
                    viewModel.handleDishSelection(it)
                }
            )
        }
    }
}

4. 完整的状态流转示例

class RestaurantViewModel : ViewModel() {
    // 1. 状态定义
    private val _uiState = MutableStateFlow(RestaurantState())
    val uiState = _uiState.asStateFlow()

    // 2. 事件处理
    sealed class UiEvent {
        data class OrderDish(val dish: Dish) : UiEvent()
        object ClearOrder : UiEvent()
    }

    // 3. 处理用户事件
    fun handleEvent(event: UiEvent) {
        when (event) {
            is UiEvent.OrderDish -> {
                viewModelScope.launch {
                    // 更新状态
                    _uiState.update { currentState ->
                        currentState.copy(
                            orders = currentState.orders + event.dish
                        )
                    }
                }
            }
            is UiEvent.ClearOrder -> {
                viewModelScope.launch {
                    _uiState.update { it.copy(orders = emptyList()) }
                }
            }
        }
    }
}

@Composable
fun RestaurantScreen(
    viewModel: RestaurantViewModel
) {
    // 1. 收集状态
    val state by viewModel.uiState.collectAsState()

    // 2. UI 展示
    Column {
        // 显示菜单
        MenuList(
            dishes = state.dishes,
            onDishClick = { dish ->
                // 3. 发送用户事件
                viewModel.handleEvent(UiEvent.OrderDish(dish))
            }
        )

        // 显示订单
        OrderList(
            orders = state.orders,
            onClearClick = {
                viewModel.handleEvent(UiEvent.ClearOrder)
            }
        )
    }
}

// UI 组件
@Composable
private fun MenuList(
    dishes: List<Dish>,
    onDishClick: (Dish) -> Unit
) {
    LazyColumn {
        items(dishes) { dish ->
            DishItem(
                dish = dish,
                onClick = { onDishClick(dish) }
            )
        }
    }
}

@Composable
private fun OrderList(
    orders: List<Dish>,
    onClearClick: () -> Unit
) {
    Column {
        Text("当前订单:${orders.size} 个菜品")
        Button(onClick = onClearClick) {
            Text("清空订单")
        }
    }
}

这个实现展示了:

  1. ViewModel 如何发送状态更新
  2. StateHolder (通过 collectAsState) 如何管理状态
  3. UI 如何读取状态并响应用户事件
  4. 整个状态收集和更新的完整流程

关键点:

  • 单向数据流
  • 状态不可变性
  • 事件驱动更新
  • 响应式 UI 更新

这样的架构确保了:

  • 状态管理的可预测性
  • UI 更新的一致性
  • 代码的可维护性