Android SharedFlow 详解

发布于:2025-06-05 ⋅ 阅读:(24) ⋅ 点赞:(0)

一、引言

在 Android 开发中,响应式编程是一种高效处理异步数据流的方式。Kotlin 协程提供的 SharedFlow 作为热流(Hot Flow)的代表,在事件广播和多订阅者场景中发挥着重要作用。本文将从概念、特性、使用场景及实践等方面全面解析 SharedFlow,帮助大家深入理解并灵活运用这一工具。

二、基本概念

2.1 定义与作用

SharedFlow 是 Kotlin 协程库中的一个核心组件,用于在多个订阅者之间共享事件或数据流。它属于热流,意味着数据一旦被发射,即使没有订阅者监听也会立即发送。这种特性使其特别适合处理一次性事件,如导航指令、弹窗通知、Toast 消息等。

2.2 与 StateFlow 的对比

特性

SharedFlow

StateFlow

本质

事件广播器,无初始值

状态持有者,必须有初始值

热流特性

数据立即发射,无需等待订阅者

数据立即发射,无需等待订阅者

数据存储

不存储状态,可配置历史数据回放

存储当前状态,自动缓存最新值

适用场景

事件通知、一次性操作

UI 状态管理、实时数据同步

构造参数

replay、extraBufferCapacity、onBufferOverflow

初始值 value

关键区别

  • StateFlow 是 SharedFlow 的特殊形式(相当于 replay=1 的 SharedFlow),强制要求初始值并自动缓存最新状态。
  • SharedFlow 更灵活,适合无状态的事件广播,而 StateFlow 专注于状态管理。

三、核心特性与参数配置

3.1 热流特性

SharedFlow 在创建后立即开始发射数据,即使没有订阅者。当订阅者加入时,可通过 replay 参数配置接收最近的历史数据。例如:

val sharedFlow = MutableSharedFlow<Int>(replay = 2) // 缓存最近 2 条数据

3.2 关键参数详解

3.2.1 replay
  • 作用:控制新订阅者可接收的历史数据数量。
  • 示例

val sharedFlow = MutableSharedFlow<Int>(replay = 1) // 新订阅者接收最近 1 条数据

若 replay = 0(默认值),新订阅者仅接收订阅后的新数据。

3.2.2 extraBufferCapacity
  • 作用:扩展缓冲区容量,用于存储订阅者未及时消费的数据。
  • 示例

val sharedFlow = MutableSharedFlow<Int>(extraBufferCapacity = 3) // 总缓冲区大小为 replay + 3

结合 replay,总缓冲区大小为 replay + extraBufferCapacity。

3.2.3 onBufferOverflow
  • 作用:定义缓冲区溢出时的处理策略。
  • 选项
    • SUSPEND(默认):挂起发射者,直到缓冲区有空间。
    • DROP_OLDEST:丢弃最旧数据,腾出空间。
    • DROP_LATEST:丢弃最新数据,保留旧数据。
  • 示例

val sharedFlow = MutableSharedFlow<Int>(

    onBufferOverflow = BufferOverflow.DROP_OLDEST

)

当缓冲区满时,丢弃最早的数据以接收新数据。

3.3 背压处理

SharedFlow 通过 onBufferOverflow 策略处理背压(Backpressure),即生产者速度超过消费者时的应对机制。例如:

// 当缓冲区满时,丢弃最新发射的数据

val sharedFlow = MutableSharedFlow<Int>(

    extraBufferCapacity = 2,

    onBufferOverflow = BufferOverflow.DROP_LATEST

)

此配置适用于实时数据更新场景,确保最新数据优先传递。

四、使用场景与实践

4.1 ViewModel 中的事件管理

在 ViewModel 中定义 SharedFlow 以发射 UI 事件(如网络请求结果、用户操作反馈):

class MainViewModel : ViewModel() {

    private val _eventFlow = MutableSharedFlow<String>()

    val eventFlow: SharedFlow<String> = _eventFlow.asSharedFlow()

    fun fetchData() {

        viewModelScope.launch {

            // 模拟耗时操作

            delay(1000)

            _eventFlow.emit("Data fetched successfully")

        }

    }

}

// 在 Activity 中监听事件

lifecycleScope.launch {

    viewModel.eventFlow.collect { event ->

        Toast.makeText(this@MainActivity, event, Toast.LENGTH_SHORT).show()

    }

}

4.2 Fragment 间通信

通过共享 ViewModel 和 SharedFlow 在 Fragment 间传递数据:

// SharedViewModel.kt

class SharedViewModel : ViewModel() {

    private val _sharedFlow = MutableSharedFlow<String>()

    val sharedFlow: SharedFlow<String> = _sharedFlow

    fun sendData(data: String) {

        viewModelScope.launch {

            _sharedFlow.emit(data)

        }

    }

}

// 发送 Fragment

class SendingFragment : Fragment() {

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {

        super.onViewCreated(view, savedInstanceState)

        val sharedViewModel = ViewModelProvider(requireActivity()).get(SharedViewModel::class.java)

        sharedViewModel.sendData("Hello from SendingFragment")

    }

}

// 接收 Fragment

class ReceivingFragment : Fragment() {

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {

        super.onViewCreated(view, savedInstanceState)

        val sharedViewModel = ViewModelProvider(requireActivity()).get(SharedViewModel::class.java)

        lifecycleScope.launch {

            sharedViewModel.sharedFlow.collect { data ->

                textView.text = data

            }

        }

    }

}

4.3 事件总线实现

构建全局事件总线,支持多组件订阅:

class EventBus {

    private val _events = MutableSharedFlow<Event>(replay = 1)

    val events: SharedFlow<Event> = _events.asSharedFlow()

    suspend fun send(event: Event) {

        _events.emit(event)

    }

}

sealed class Event {

    object NetworkConnected : Event()

    data class Error(val message: String) : Event()

}

// 使用示例

val eventBus = EventBus()

// 订阅事件

lifecycleScope.launch {

    eventBus.events.collect { event ->

        when (event) {

            is Event.NetworkConnected -> showToast("Network connected")

            is Event.Error -> showErrorDialog(event.message)

        }

    }

}

// 发送事件

viewModelScope.launch {

    eventBus.send(Event.NetworkConnected)

}

五、注意事项与最佳实践

5.1 生命周期管理

  • 使用 lifecycleScope.launch 启动收集协程,确保与宿主生命周期同步。
  • 在 ViewModel 中使用 viewModelScope,避免内存泄漏。

5.2 错误处理

  • 添加 catch 操作符处理流中的异常:

lifecycleScope.launch {

    viewModel.eventFlow

        .catch { e -> handleError(e) }

        .collect { event -> updateUI(event) }

}

5.3 性能优化

  • 合理配置 replay 和 extraBufferCapacity,避免内存浪费。
  • 根据场景选择背压策略:
    • SUSPEND 适用于不能丢失数据的场景(如金融交易)。
    • DROP_OLDEST 或 DROP_LATEST 适用于实时更新(如聊天消息)。

5.4 避免粘性事件

  • 若不需要历史数据,设置 replay = 0,确保新订阅者仅接收订阅后的事件。

六、总结

SharedFlow 作为 Kotlin 协程中的热流工具,在事件广播和多订阅者场景中表现出色。通过灵活配置 replay、extraBufferCapacity 和 onBufferOverflow,开发者可以高效管理数据流,避免背压问题。结合 ViewModel 和生命周期感知组件,SharedFlow 能显著提升代码的可维护性和响应性能。在实际开发中,需根据具体场景选择 SharedFlow 或 StateFlow,并遵循最佳实践以确保稳定性和效率。


网站公告

今日签到

点亮在社区的每一天
去签到