Android使用Flow+协程封装一个FlowBus
前言:
做过Android的同学应该都使用过EvenutBus、Rxbus、LiveDataBus等,这些第三方不仅要导入依赖包,而且还要注册和取消注册,使用起来非常麻烦,稍不注意就导致内存泄漏,自从接触了Flow、SharedFlow之后感觉使用起来方便多了,于是产生了一个封装通用事件工具类的想法,直接上代码.

1.FlowBus工具类:
kotlin
/**
* @auth: njb
* @date: 2024/7/18 10:17
* @desc: 基于Flow封装的FlowBus
*/
object FlowBus {
private const val TAG = "FlowBus"
private val busMap = mutableMapOf<String, FlowEventBus<*>>()
private val busStickMap = mutableMapOf<String, FlowStickEventBus<*>>()
@Synchronized
fun <T> with(key: String): FlowEventBus<T> {
var flowEventBus = busMap[key]
if (flowEventBus == null) {
flowEventBus = FlowEventBus<T>(key)
busMap[key] = flowEventBus
}
return flowEventBus as FlowEventBus<T>
}
@Synchronized
fun <T> withStick(key: String): FlowStickEventBus<T> {
var stickEventBus = busStickMap[key]
if (stickEventBus == null) {
stickEventBus = FlowStickEventBus<T>(key)
busStickMap[key] = stickEventBus
}
return stickEventBus as FlowStickEventBus<T>
}
open class FlowEventBus<T>(private val key: String) : DefaultLifecycleObserver {
//私有对象用于发送消息
private val _events: MutableSharedFlow<T> by lazy {
obtainEvent()
}
//暴露的公有对象用于接收消息
private val events = _events.asSharedFlow()
open fun obtainEvent(): MutableSharedFlow<T> =
MutableSharedFlow(0, 1, BufferOverflow.DROP_OLDEST)
//在主线程中接收数据
fun register(lifecycleOwner: LifecycleOwner,action: (t: T) -> Unit){
lifecycleOwner.lifecycleScope.launch {
events.collect {
try {
action(it)
}catch (e:Exception){
e.printStackTrace()
Log.e(TAG, "FlowBus - Error:$e")
}
}
}
}
//在协程中接收数据
fun register(scope: CoroutineScope,action: (t: T) -> Unit){
scope.launch {
events.collect{
try {
action(it)
}catch (e:Exception){
e.printStackTrace()
Log.e(TAG, "FlowBus - Error:$e")
}
}
}
}
//在协程中发送数据
suspend fun post(event: T){
_events.emit(event)
}
//在主线程中发送数据
fun post(scope: CoroutineScope,event: T){
scope.launch {
_events.emit(event)
}
}
override fun onDestroy(owner: LifecycleOwner) {
super.onDestroy(owner)
Log.w(TAG, "FlowBus ==== 自动onDestroy")
val subscriptCount = _events.subscriptionCount.value
if (subscriptCount <= 0)
busMap.remove(key)
}
// 手动调用的销毁方法,用于Service、广播等
fun destroy() {
Log.w(TAG, "FlowBus ==== 手动销毁")
val subscriptionCount = _events.subscriptionCount.value
if (subscriptionCount <= 0) {
busMap.remove(key)
}
}
}
class FlowStickEventBus<T>(key: String) : FlowEventBus<T>(key) {
override fun obtainEvent(): MutableSharedFlow<T> =
MutableSharedFlow(1, 1, BufferOverflow.DROP_OLDEST)
}
}
2.在Activity中的使用:
2.1传递参数给主界面Activity:
kotlin
/**
* @auth: njb
* @date: 2024/9/10 23:49
* @desc: 描述
*/
class TestActivity :AppCompatActivity(){
private val textView:TextView by lazy { findViewById(R.id.tv_test) }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_test)
initFlowBus()
}
private fun initFlowBus() {
val messageEvent = MessageEvent()
messageEvent.message = "stop"
messageEvent.state = false
textView.setOnClickListener {
lifecycleScope.launch {
FlowBus.with<MessageEvent>("test").post(this, messageEvent)
finish()
}
}
}
}

2.2 MainActivity接收:
less
/**
* 初始化
*/
private fun initView() {
binding.rvWallpaper.apply {
layoutManager = GridLayoutManager(this@MainActivity, 2)
adapter = wallPaperAdapter
}
binding.btnGetWallpaper.setOnClickListener {
lifecycleScope.launch {
mainViewModel.mainIntentChannel.send(MainIntent.GetWallpaper)
}
val intent = Intent(this@MainActivity,TestActivity::class.java)
startActivity(intent)
}
FlowBus.with<MessageEvent>("test").register(this@MainActivity) {
LogUtils.d(TAG,it.toString())
if(it.message == "stop"){
LogUtils.d(TAG,"===接收到的消息为==="+it.message)
}
}
FlowBus.with<MessageEvent>("mineFragment").register(this@MainActivity) {
LogUtils.d(TAG,it.toString())
if(it.message == "onMine"){
LogUtils.d(TAG,"===接收到的消息为1111==="+it.message)
}
}
}

3.在Fragment中的使用:
3.1 发送数据
kotlin
package com.cloud.flowbusdemo.fragment
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import com.cloud.flowbusdemo.databinding.FragmentMineBinding
import com.cloud.flowbusdemo.flow.FlowBus
import com.cloud.flowbusdemo.model.MessageEvent
import kotlinx.coroutines.launch
private const val ARG_PARAM_NAME = "name"
private const val ARG_PARAM_AGE = "age"
/**
* @auth: njb
* @date: 2024/9/17 19:43
* @desc: 描述
*/
class MineFragment :Fragment(){
private lateinit var binding: FragmentMineBinding
private val TAG = "MineFragment"
private var name: String? = null
private var age: Int? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
arguments?.let {
name = it.getString(ARG_PARAM_NAME)
age = it.getInt(ARG_PARAM_AGE)
}
Log.i(TAG, "MainFragment 传递到 MineFragment 的参数为 name = $name , age = $age")
Log.d(TAG, "姓名:" + name + "年龄:" + age)
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
binding = FragmentMineBinding.inflate(layoutInflater)
initView()
return binding.root
}
private fun initView() {
val messageEvent = MessageEvent()
messageEvent.message = "onMine"
messageEvent.state = false
binding.let {
it.tvTitle.text = name
it.tvAge.text = age.toString()
it.tvTitle.setOnClickListener {
lifecycleScope.launch {
FlowBus.with<MessageEvent>("mineFragment").post(this, messageEvent)
}
}
}
}
}
less

## 3.2 接收数据:
private fun initView() {
binding.rvWallpaper.apply {
layoutManager = GridLayoutManager(this@MainActivity, 2)
adapter = wallPaperAdapter
}
binding.btnGetWallpaper.setOnClickListener {
lifecycleScope.launch {
mainViewModel.mainIntentChannel.send(MainIntent.GetWallpaper)
}
val intent = Intent(this@MainActivity,TestActivity::class.java)
startActivity(intent)
}
FlowBus.with<MessageEvent>("test").register(this@MainActivity) {
LogUtils.d(TAG,it.toString())
if(it.message == "stop"){
LogUtils.d(TAG,"===接收到的消息为==="+it.message)
}
}
FlowBus.with<MessageEvent>("mineFragment").register(this@MainActivity) {
LogUtils.d(TAG,it.toString())
if(it.message == "onMine"){
LogUtils.d(TAG,"===接收到的消息为1111==="+it.message)
}
}
}

4.在Service中的使用:
4.1发送数据:
less
private fun initService() {
val intent = Intent(this@MainActivity, FlowBusTestService::class.java)
intent.putExtra("sockUrl","")
startService(intent)
}

4.2接收数据:
kotlin
/**
* @auth: njb
* @date: 2024/9/22 23:32
* @desc: 描述
*/
class FlowBusTestService:Service() {
private var sock5Url:String ?= null
private val TAG = "FlowBusTestService"
override fun onBind(intent: Intent?): IBinder? {
return null
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
intent?.let {
this.sock5Url = intent.getStringExtra("sockUrl")
LogUtils.d(TAG,"====收到的ip为==="+this.sock5Url)
}
return if (intent?.action == Constants.ACTION_DISCONNECT) {
disconnect()
START_NOT_STICKY
} else {
connect()
START_STICKY
}
}
private fun connect() {
}
private fun disconnect() {
}
}
5.在Websock中的使用:
5.1发送数据:
kotlin
private fun connectWebSocket() {
LogUtils.e(TAG, "===connectUrl===$currentWebSocketUrl")
try {
if (mWebSocketManager == null) {
return
}
mWebSocketManager?.addListener(object : SocketListener {
override fun onConnected() {
LogUtils.e(TAG, "===连接成功====")
val messageEvent = MessageEvent()
messageEvent.message = "socket连接成功"
FloatWindowManager.log("socket连接成功")
CoroutineScope(Dispatchers.Main).launch{
FlowBus.with<MessageEvent>("onConnected").post(this,messageEvent)
}
//val msg1 = "{"clientUA":"uwWFPvfkyY","errorMsg":"该手机号无法办理业务","event":"after_order_check","eventOrderSeq":"nXgBlfCuFruOhwOIvpkK","eventTime":1721270790010,"id":"c8a8fabdd7fb4f0c987b6c8634276cbc","mock":true,"orderSn":"CHJdxETRqORZxkckZMTa","phone":"neO714HKa+So1pmhQ0ZEfg==","switchIp":true,"pageViewUrl":"https://hippoglo.com/yidong/test/fu21/index.html"}"
}
override fun onConnectFailed(throwable: Throwable) {
LogUtils.e(TAG, "===连接失败====")
val messageEvent = MessageEvent()
messageEvent.message = "socket连接失败:$currentWebSocketUrl"
FloatWindowManager.log("socket连接失败")
}
override fun onDisconnect() {
LogUtils.e(TAG, "===断开连接====")
val messageEvent = MessageEvent()
messageEvent.message = "socket断开连接"
FloatWindowManager.log("socket断开连接")
}
override fun onSendDataError(errorResponse: ErrorResponse) {
LogUtils.e(TAG + "===发送数据失败====" + errorResponse.description)
val messageEvent = MessageEvent()
messageEvent.message = "发送数据失败--->" + errorResponse.description
FloatWindowManager.log("发送数据失败")
}
override fun <T> onMessage(msg: String, t: T) {
LogUtils.e(TAG,"===接收到消息 String===$msg")
val messageEvent = MessageEvent()
messageEvent.message = msg
FloatWindowManager.log("===接收到消息===$msg")
taskManager?.onHandleMsg(msg)
}
override fun <T> onMessage(bytes: ByteBuffer, t: T) {
LogUtils.e(TAG, "===接收到消息byteBuffer===="+GsonUtils.toJson(bytes))
val rBuffer = ByteBuffer.allocate(1024)
val charset = Charset.forName("UTF-8")
try {
val receiveText =
charset.newDecoder().decode(rBuffer.asReadOnlyBuffer()).toString()
LogUtils.e(TAG, "===接收到消息byteBuffer====$receiveText")
val messageEvent = MessageEvent()
messageEvent.message = receiveText
// FloatWindowManager.log("===收到消息 byte===$receiveText")
} catch (e: CharacterCodingException) {
throw RuntimeException(e)
}
}
override fun onPing(pingData: Framedata) {
LogUtils.e(TAG, "===心跳onPing===$pingData")
}
override fun onPong(framedata: Framedata) {
LogUtils.e(TAG, "===心跳onPong===$framedata")
val messageEvent = MessageEvent()
messageEvent.message = format.format(Date()) + " | 心跳onPong->"
FloatWindowManager.log("===心跳onPong===${format.format(Date())}${"->"}$currentWebSocketUrl")
}
})
mWebSocketManager?.start()
} catch (e: Exception) {
e.printStackTrace()
}
}

5.2接收数据:
less
private fun initFlowBus() {
FlowBus.with<MessageEvent>("onConnected").register(this@MainActivity) {
LogUtils.d(TAG, "收到消息为:$it")
}
FlowBus.with<MessageEvent>("onStartVpn").register(this@MainActivity) {
LogUtils.d(TAG, "收到vpn消息为:$it")
CoroutineScope(Dispatchers.Main).launch {
if (it.message == "start" && it.state && Constants.SWITCH_IP) {
this@MainActivity.sockUrl = it.sockUrl
LogUtils.d(TAG, "收到代理地址为:${it.sockUrl}")
AppUtils.prepareVpn(this@MainActivity,it.sockUrl)
// prepareVpn()
}
}
}
FlowBus.with<MessageEvent>("onStopVpn").register(this@MainActivity) {
LogUtils.d(TAG, "收到vpn消息为:$it")
if (it.message == "stop" && !it.state) {
AppUtils.stopVpn(this@MainActivity)
}
}
}

6.实现的效果如下:



7.FlowBus、EventBus 与 RxBus对比
对比维度 | FlowBus | EventBus | RxBus |
---|---|---|---|
底层依赖 | 基于 Android 官方 Kotlin Flow (协程生态核心组件) |
无第三方依赖,基于自定义观察者模式实现 | 基于 RxJava (响应式编程库,需引入 RxJava 依赖) |
支持语言 | 仅支持 Kotlin(依赖协程与 Flow 的 Kotlin 语法特性) | 支持 Java 和 Kotlin(兼容 Java 语法,无语言绑定) | 支持 Java 和 Kotlin(RxJava 本身兼容两种语言) |
线程切换 | 天然支持协程线程切换(通过 flowOn /launchIn 灵活指定发送 / 接收线程,无需手动处理线程安全) |
需通过 ThreadMode 显式指定线程(如 MAIN /BACKGROUND ,底层通过 Handler / 线程池实现) |
依赖 RxJava 操作符(如 observeOn /subscribeOn )切换线程,支持更细粒度的线程控制 |
生命周期感知 | 支持(需配合 LifecycleOwner ,通过 repeatOnLifecycle 自动绑定页面生命周期,避免内存泄漏) |
不原生支持(需手动注册 / 解注册,若忘记解注册易导致 Activity/Fragment 内存泄漏,需通过 EventBus 3.x 注解或封装类优化) |
不原生支持(需手动管理订阅 / 取消订阅,或通过 RxLifecycle 等扩展库绑定生命周期,否则易引发内存泄漏) |
事件类型 | 基于泛型严格区分事件类型(发送 / 接收需匹配泛型,编译期可检查类型错误,减少运行时异常) | 基于事件类实例区分(通过 post(Object event) 发送,接收时需强转,存在运行时类型转换风险) |
基于泛型区分事件类型(类似 FlowBus,编译期可检查类型,但需依赖 RxJava 的 Observable 泛型定义) |
背压处理 | 原生支持背压(Flow 本身是背压感知流,可通过 buffer /conflate /collectLatest 处理高频率事件堆积) |
不支持背压(事件发送无缓冲控制,高频率发送时易导致接收端处理不及时,引发 ANR 或数据丢失) | 支持背压(依赖 RxJava 的背压策略,如 BACKPRESSURE_BUFFER /BACKPRESSURE_DROP ,但配置较复杂) |
异常处理 | 基于协程异常处理机制(通过 catch 操作符捕获异常,或在 collect 块中 try-catch,异常不崩溃主线程) |
异常需手动捕获(若接收事件的方法抛出未捕获异常,会直接崩溃应用,需在订阅者方法内显式处理) | 依赖 RxJava 异常处理(通过 onError 回调捕获异常,若未处理 onError 会触发 OnErrorNotImplementedException 崩溃) |
使用复杂度 | 中(需理解协程与 Flow 基础概念,上手成本依赖 Kotlin 协程熟练度) | 低(API 简洁,通过 @Subscribe 注解 +register /unregister 即可使用,学习成本低) |
高(需掌握 RxJava 操作符、线程模型、背压策略等,API 较复杂,上手门槛高) |
性能 | 高(Flow 是轻量级流,基于协程挂起机制,无额外线程切换开销,内存占用低) | 中(自定义观察者模式实现,线程切换依赖 Handler / 线程池,存在少量开销,整体性能稳定) | 中(RxJava 底层封装较复杂,操作符链可能引入额外对象创建开销,高频率场景下性能略逊于 FlowBus) |
优缺点总结 | 优点 : 1. 官方生态,兼容性强(随 Kotlin 协程迭代更新) 2. 生命周期感知,自动防内存泄漏 3. 支持背压,高频率事件处理更安全 4. 编译期类型检查,减少运行时错误 缺点: 1. 仅支持 Kotlin,无法兼容 Java 项目 2. 依赖协程知识,上手成本高于 EventBus | 优点 : 1. 无依赖,接入简单,学习成本低 2. 兼容性好,支持 Java/Kotlin 项目 3. 社区成熟,文档丰富,问题易排查 缺点: 1. 不原生支持生命周期,需手动管理注册 / 解注册 2. 无背压,高频率事件易引发性能问题 3. 事件接收需强转,存在运行时类型风险 | 优点 : 1. 支持丰富的操作符(如过滤、变换、合并),复杂事件流处理能力强 2. 线程控制灵活,支持多线程场景 3. 支持背压,可应对高频率事件 缺点: 1. 依赖 RxJava,增加 APK 体积(约 1-2MB) 2. 上手门槛高,需掌握响应式编程思想 3. 不原生支持生命周期,需额外集成 RxLifecycle |
适用场景 | 1. Kotlin 语言的 Android 项目 2. 需生命周期感知、避免内存泄漏的场景(如 Activity/Fragment 通信) 3. 高频率事件场景(如实时数据刷新) | 1. Java/Kotlin 混合项目或纯 Java 项目 2. 简单事件通信场景(如页面跳转、通知更新) 3. 对学习成本敏感、追求轻量接入的项目 | 1. 已引入 RxJava 生态的项目(避免重复依赖) 2. 复杂事件流处理场景(如多事件合并、过滤) 3. 需精细控制线程与背压的场景 |
8.总结:
- 通过上面的实例和使用测试,可以发现使用非常简单方便
- 不需要导入任何第三方依赖库,都是原生自带的
- 在Activity、Fragment和ViewModel中不需要关注生命周期和解绑问题
- 在Service或者广播等其他场景都可以使用,只要你想用,基本上都能
- 比起EventBus、Rxbus使用都要简单方便,可扩展性更好
- 后面如果有时间的话考虑加入跨进程通信方案
- 如果小伙伴们有更好的方案或者扩展欢迎讨论,毕竟一个人精力有限