美文网首页
自己写一个 KEventBus(二)——线程切换

自己写一个 KEventBus(二)——线程切换

作者: Vic_wkx | 来源:发表于2021-03-23 17:16 被阅读0次

    一、新增接收线程枚举类

    enum class ThreadMode {
        MAIN,
        ASYNC
    }
    

    @Subscribe 注解中新增此变量:

    @Retention(AnnotationRetention.RUNTIME)
    @Target(AnnotationTarget.FUNCTION)
    annotation class Subscribe(val threadMode: ThreadMode = ThreadMode.MAIN)
    

    二、新增 SubcriberMethod 类

    在上一篇文章中,观察者是一个Pair<Any, Method> 列表,因为我们只需要保存对象实例和方法用于反射调用即可。但现在观察者多了一个参数,我们无法再使用 Pair 保存此观察者。所以我们为其创建一个对象:

    data class SubscriberMethod(val obj: Any, val method: Method, val threadMode: ThreadMode)
    

    KEventBus 中,用 SubscriberMethod 类替换 Pair:

    object KEventBus {
    
        private val subscriptionsByEventType = mutableMapOf<Class<*>, MutableList<SubscriberMethod>>()
    
        fun register(obj: Any) {
            // Reflect to get all subscribed methods
            obj.javaClass.declaredMethods.forEach {
                if (it.isAnnotationPresent(Subscribe::class.java)) {
                    if (it.parameterTypes.size == 1) {
                        val eventType = it.parameterTypes.first()
                        if (eventType !in subscriptionsByEventType) {
                            subscriptionsByEventType[eventType] = mutableListOf()
                        }
                        subscriptionsByEventType[eventType]!!.add(SubscriberMethod(obj, it, it.getAnnotation(Subscribe::class.java)!!.threadMode))
                    }
                }
            }
        }
    
        fun unregister(obj: Any) {
            obj.javaClass.declaredMethods.forEach {
                if (it.isAnnotationPresent(Subscribe::class.java)) {
                    if (it.parameterTypes.size == 1) {
                        val event = it.parameterTypes.first()
                        if (event in subscriptionsByEventType) {
                            subscriptionsByEventType[event]?.remove(SubscriberMethod(obj, it, it.getAnnotation(Subscribe::class.java)!!.threadMode))
                        }
                    }
                }
            }
        }
    
        fun post(event: Any) {
            subscriptionsByEventType[event.javaClass]?.forEach {
                it.method(it.obj, event)
            }
        }
    }
    

    接下来我们就开始为 KEventBus 添加线程切换逻辑。

    三、主线程消息

    想要在主线程接收消息,我们可以通过 Handler(Looper.getMainLooper()) 完成。

    创建 MainPoster:

    class MainPoster(private val eventBus: KEventBus) : Handler(Looper.getMainLooper()) {
        private val queue = mutableListOf<Pair<SubscriberMethod, Any>>()
    
        fun post(subscriberMethod: SubscriberMethod, event: Any) {
            queue.add(subscriberMethod to event)
            sendEmptyMessage(0)
        }
    
        override fun handleMessage(msg: Message) {
            super.handleMessage(msg)
            val pair = queue.removeFirst()
            eventBus.invokeSubscriber(pair.first, pair.second)
        }
    }
    

    需要注意的是,每一条消息都需要和 SubscriberMethod 绑定,因为 post 消息时,会为每一个观察者函数 post 一条消息,必须确保接收者只有一个,避免重复接收消息。

    四、子线程消息

    在子线程发送消息,可以通过线程池完成。

    创建 AsyncPoster:

    class AsyncPoster(private val eventBus: KEventBus) : Runnable {
        private val queue = mutableListOf<Pair<SubscriberMethod, Any>>()
        private val executors = Executors.newCachedThreadPool()
    
        fun post(subscriberMethod: SubscriberMethod, event: Any) {
            queue.add(subscriberMethod to event)
            executors.execute(this)
        }
    
        override fun run() {
            val pair = queue.removeFirst()
            eventBus.invokeSubscriber(pair.first, pair.second)
        }
    }
    

    五、添加线程切换逻辑

    在 KEventBus 中添加线程切换逻辑:

    object KEventBus {
        
        //...
        
        private val mainPoster = MainPoster(this)
        private val asyncPoster= AsyncPoster(this)
    
        fun post(event: Any) {
            subscriptionsByEventType[event.javaClass]?.forEach {
                when (it.threadMode) {
                    ThreadMode.MAIN -> {
                        mainPoster.post(it, event)
                    }
                    ThreadMode.ASYNC -> {
                        asyncPoster.post(it, event)
                    }
                }
            }
        }
    
        fun invokeSubscriber(subscriberMethod: SubscriberMethod, event: Any) {
            subscriberMethod.method(subscriberMethod.obj, event)
        }
    }
    

    六、测试

    @Subscribe(threadMode = ThreadMode.ASYNC)
    fun onAsyncEvent(message: String) {
        Log.d("~~~", "$message ${Thread.currentThread().name}")
    }
    

    将 threadMode 设置为 ASYNC,收到消息时,我们会在 Log 控制台中看到当前线程属于子线程。
    例如:

    ~~~: It's an async message. pool-1-thread-1
    

    这样我们就实现了 EventBus 线程切换的逻辑。实际上,EventBus 中一共有五种 ThreadMode:

    • POSTING:与消息发送时所在的线程相同(ThreadMode 的默认值)
    • MAIN:主线程。如果发消息时,已经在主线程,则直接发送消息,否则使用主线程的 Handler 发送消息。
    • MAIN_ORDERED:主线程且保证有序。使用主线程的 Handler 发送消息。
    • BACKGROUND:子线程。如果发消息时,已经在子线程,则直接发送消息,否则使用线程池发送消息。
    • ASYNC:子线程。使用线程池发送消息。

    相关文章

      网友评论

          本文标题:自己写一个 KEventBus(二)——线程切换

          本文链接:https://www.haomeiwen.com/subject/esdfhltx.html