Android如何使用Flow封装一个FlowBus工具类

 更新时间:2024年09月24日 12:02:59   作者:淡淡的香烟  
本文介绍了如何在Android中使用Flow封装一个FlowBus工具类,以替代EvenutBus、Rxbus、LiveDataBus、LiveData等第三方依赖包,作者提供了在Activity、Fragment、Service和Websock中使用FlowBus的具体代码,并解释了实现的效果,文章最后还提供了项目demo源码的下载链接

Android中使用Flow封装一个FlowBus工具类

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

1.FlowBus:

/**
 * @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:

/**
 * @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接收:

/**
 * 初始化
 */
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 发送数据

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)
                }
            }
        }
    }
}

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发送数据:

private fun initService() {
    val intent = Intent(this@MainActivity, FlowBusTestService::class.java)
    intent.putExtra("sockUrl","")
    startService(intent)
}

4.2接收数据:

/**
 * @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发送数据:

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)
                }
            }
            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接收数据:

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.项目demo源码如下:

https://gitee.com/jackning_admin/flowbus-demo

到此这篇关于Android使用Flow封装一个FlowBus工具类的文章就介绍到这了,更多相关Android FlowBus工具类内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • android studio集成unity导出工程的实现

    android studio集成unity导出工程的实现

    本文主要介绍了android studio集成unity导出工程的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-05-05
  • 5步学会使用VideoView播放视频

    5步学会使用VideoView播放视频

    这篇文章主要为大家详细介绍了5步学会使用VideoView播放视频的相关资料,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-07-07
  • Android Notification通知使用详解

    Android Notification通知使用详解

    消息通知(Notification)是Android系统中比较有特色的一个功能,当某个应用程序希望用户发出一些提示信息,而该应用又不在前台运行时,就可以借助通知来实现
    2022-09-09
  • Android 利用ViewPager+GridView实现首页导航栏布局分页效果

    Android 利用ViewPager+GridView实现首页导航栏布局分页效果

    用ViewPager+GridView实现首页导航栏布局分页效果来实现的效果,具有一定的参考价值,感兴趣的小伙伴们可以参考一下。
    2016-10-10
  • Android通过记住密码功能学习数据存储类SharedPreferences详解及实例

    Android通过记住密码功能学习数据存储类SharedPreferences详解及实例

    这篇文章主要通过“记住密码”实例功能学习为大家介绍了Android数据存储类SharedPreferences,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2016-03-03
  • MotionLayout自定义开关按钮实例详解

    MotionLayout自定义开关按钮实例详解

    这篇文章主要为大家介绍了MotionLayout自定义开关按钮实例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-11-11
  • Android 签名校验与绕过思路详解

    Android 签名校验与绕过思路详解

    在 Android 应用的安全体系中,签名机制是保障 APK 完整性与可信来源的关键手段,本篇文章将从签名校验的原理出发,介绍常见的绕过方式,并分析开发者与逆向工程师之间的“攻防博弈”,感兴趣的朋友一起看看吧
    2025-06-06
  • Android 跨进程模拟按键(KeyEvent )实例详解

    Android 跨进程模拟按键(KeyEvent )实例详解

    这篇文章主要介绍了Android 跨进程模拟按键(KeyEvent )实例详解的相关资料,类似手机遥控器的需求就可以这么做,需要的朋友可以参考下
    2016-11-11
  • 浅谈关于Android WebView上传文件的解决方案

    浅谈关于Android WebView上传文件的解决方案

    这篇文章主要介绍了浅谈关于Android WebView上传文件的解决方案 ,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-09-09
  • Android Service生命周期详解

    Android Service生命周期详解

    Android Service 生命周期可以促使移动设备的创新,让用户体验到最优越的移动服务,只有broadcast receivers执行此方法的时候才是激活的,当 onReceive()返回的时候,它就是非激活状态
    2015-11-11

最新评论