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

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

​ 做过Android的同学应该都使用过EvenutBus、Rxbus、LiveDataBus、LiveData等,这些第三方不仅要导入依赖包,而且还要注册和取消注册,使用起来非常麻烦,稍不注意就导致内存泄漏,自从接触了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接收:

kotlin 复制代码
/**
 * 初始化
 */
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)
                }
            }
        }
    }
}

3.2 接收数据:

kotlin 复制代码
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发送数据:

kotlin 复制代码
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)
                }

            }

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

kotlin 复制代码
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

相关推荐
丘狸尾1 小时前
[cisco 模拟器] ftp服务器配置
android·运维·服务器
van叶~3 小时前
探索未来编程:仓颉语言的优雅设计与无限可能
android·java·数据库·仓颉
Crossoads7 小时前
【汇编语言】端口 —— 「从端口到时间:一文了解CMOS RAM与汇编指令的交汇」
android·java·汇编·深度学习·网络协议·机器学习·汇编语言
li_liuliu8 小时前
Android4.4 在系统中添加自己的System Service
android
C4rpeDime10 小时前
自建MD5解密平台-续
android
鲤籽鲲12 小时前
C# Random 随机数 全面解析
android·java·c#
m0_5485147716 小时前
2024.12.10——攻防世界Web_php_include
android·前端·php
凤邪摩羯16 小时前
Android-性能优化-03-启动优化-启动耗时
android
凤邪摩羯16 小时前
Android-性能优化-02-内存优化-LeakCanary原理解析
android
喀什酱豆腐17 小时前
Handle
android