EventBus-Kotlin

作者: 24K男 | 来源:发表于2018-02-06 16:04 被阅读98次

    作为一个Android开发者,如果你还没开始使用Kotlin,那么你可能要思考下自己的步伐是不是慢了。本文带着大家动手来写一个Kotlin版本的EventBus,一方面来看看Kotlin长什么样,一方面理解下EventBus的设计思想。

    EventBus原理

    EventBus 是基于观察者模式,核心是事件。通过事件的发布和订阅实现组件之间的通信,EventBus默认是一个单例存在,在Java中还需要使用Synchronized来保证线程安全。

    通俗来讲,EventBus通过注册将所有订阅事件的方法储存在集合中,当有事件发布的时候,根据某些规则,匹配出符合条件的方法,调用执行,从而实现组件间的通信。

    发布的事件相当于被观察者,注册的对象相当于观察者,被观察者和观察者是一对多的关系。当被观察者状态发生变化,即发布事件的时候,观察者对象将会得到通知并作出响应,即执行对应的方法。

    入口-EventBus

    还是直接粘贴上代码吧,毕竟这是最直观的。

    
    package core.zs.eventbus
    
    import java.util.concurrent.CopyOnWriteArrayList
    import java.util.concurrent.ExecutorService
    import java.util.concurrent.Executors
    
    /**
     * EventBus.
     *
     * Created by Jonash on 2018/2/5.
     */
    object EventBus {
    
        // 因为注册,取消可能耗时,因此使用多线程进行管理
        private val executorService: ExecutorService = Executors.newCachedThreadPool()
    
        // 默认的事件tag为""
        private const val TAG_DEFAULT = ""
        // 很据事件类型找到所有的注解方法信息
        private val subscriberMap = mutableMapOf<EventType, CopyOnWriteArrayList<Subscription>>()
        // Method find helper
        private val methodFinder = SubscriberMethodFinder(subscriberMap)
    
        /**
         * 注册观察者。
         * @param obj 观察者对象
         */
        fun register(obj: Any) = executorService.execute {
            methodFinder.findSubscribeMethods(obj)
        }
    
    
        /**
         * 发送事件通知。
         * @param event 事件
         * @param tag 事件标签,默认值为“”
         */
        @JvmOverloads
        fun post(event: IEvent, tag: String = TAG_DEFAULT) {
            val eventType = EventType(event.javaClass, tag)
            val list = methodFinder.getMatchEventType(eventType)
            list?.let {
                EventDispatcher.dispatchEvent(event, it)
            }
        }
    
    
        /**
         * 取消观察者。
         * @param obj 观察者对象
         */
        fun unregister(obj: Any) = executorService.execute {
            methodFinder.removeSubscriberMethod(obj)
        }
    
        fun getExecutorService() = executorService
    
    }
    
    

    设计要点:

    1. EventBus类型为object,实际上编译为Java后是一个单例的存在。
    2. 注册和取消注册使用线程,因为查找的的过程是一个耗时的存在。(你可以考虑使用编译时注解)
    3. post使用了默认参数,默认参数为"",你甚至可以忽略它的存在;但是当你需要特别定制接收同一事件类型的方法时,它会体现出用处。
    4. 为什么没使用Builder模式来实现EventBus?答:没事件。
    5. 根据事件的类型[EventType]来存储观察者信息。

    事件类型EventType

    
    package core.zs.eventbus
    
    /**
     * 记录事件类型信息。
     *
     * Created by Jonash on 2018/2/5.
     */
    class EventType(private val eventClass: Class<*>, private val tag: String) {
        override fun equals(other: Any?): Boolean {
            // 比较内存引用地址,相同则返回 true
            if (this === other) {
                return true
            }
            // 判断是否为空,是否属于同一种类型
            if (other == null || (other.javaClass.name !== this.javaClass.name)) {
                return false
            }
            // 能执行到这里,说明obj和this同类且非 null
            val eventType = other as EventType
            val tagJudge = tag == eventType.tag
            val eventJudge = eventClass.name == eventType.eventClass.name
            // tag相同
            // EventType是同一个类型
            return tagJudge && eventJudge
        }
    
        override fun hashCode(): Int {
            var hash = 7
            hash = hash * 31 + eventClass.hashCode()
            hash = hash * 31 + tag.hashCode()
            return hash
        }
    }
    
    

    设计要点:

    1. 此类的存在主要是为了存储事件类型信息。
    2. 因为此类是作为Map的key存在,所以eventClass和tag的类型均为val不可变。
    3. 同样是作为key的原因,我们重写了equals和hashCode方法。

    注解-Subscriber

    注解对于大家太常见了,在使用一些注入框架时经常见到,Subscriber注解用于标注订阅事件的方法。

    package core.zs.eventbus.annotation
    
    import core.zs.eventbus.ThreadMode
    
    /**
     *  事件接受方法的注解类
     *  tag:事件tag
     *  mode:观察者处理事件的线程,默认为post
     *
     * Created by Jonash on 2018/2/5.
     */
    @Target(AnnotationTarget.FUNCTION) // 在方法上使用
    @Retention(AnnotationRetention.RUNTIME) // 运行时注解,因为要使用反射
    annotation class Subscriber(val tag: String, val mode: ThreadMode = ThreadMode.POSTING)
    
    

    设计要点:

    1. 注解使用在方法上。
    2. 因为要反射解析注解,因此使用运行时注解。

    IEvent

    package core.zs.eventbus
    
    /**
     * 事件实现接口.
     *
     * Created by Jonash on 2018/2/5.
     */
    abstract class IEvent
    

    设计点:

    1. 仅仅是作为一个标示存在,检查订阅方法的参数必须继承了IEvent。
    2. 如果对事件有要求,可以修改该类。

    Subscription

    
    package core.zs.eventbus
    
    import java.lang.ref.WeakReference
    import java.lang.reflect.Method
    
    /**
     * 订阅方法的详细信息
     *
     * Created by Jonash on 2018/2/5.
     */
    class Subscription(val subscriber: WeakReference<Any>,
                       private val targetMethod: Method,
                       val threadMode: ThreadMode) {
    
        override fun equals(other: Any?): Boolean {
            if (this === other) {
                return true
            }
    
            if (other == null || (other::class !== this::class)) {
                return false
            }
    
            val subscription = other as Subscription
            val judgeSubscriber = this.subscriber.get() === subscription.subscriber.get()
            val judgeMethod = this.targetMethod.name == subscription.targetMethod.name
            return judgeSubscriber && judgeMethod
        }
    
        override fun hashCode(): Int {
            var hash = 7
            hash = hash * 31 + subscriber.hashCode()
            hash = hash * 31 + targetMethod.hashCode()
            hash = hash * 31 + threadMode.hashCode()
            return hash
        }
    
        /**
         * 根据传入的实例,反射调用实例方法。
         */
        internal fun invoke(event: IEvent) {
            targetMethod.invoke(subscriber.get(), event)
        }
    }
    
    

    设计要点:

    1. 该类用于存储订阅者的详细信息。
    2. 为了防止重复存储,重写了equals和hashCode方法。
    3. 为了后面的反射调用方法,设计了invoke方法。

    SubscriberMethodFinder-发现者

    详细如果你仔细阅读了EventBus的代码,已经对该类优点眼熟,它主要用于实际的查找观察者、移除观察者。

    package core.zs.eventbus
    
    import core.zs.eventbus.annotation.Subscriber
    import java.lang.ref.WeakReference
    import java.lang.reflect.Modifier
    import java.util.concurrent.CopyOnWriteArrayList
    
    /**
     * 订阅者辅助类
     *
     * Created by Jonash on 2018/2/5.
     */
    class SubscriberMethodFinder(private val subscriberMap: MutableMap<EventType, CopyOnWriteArrayList<Subscription>>) {
    
        companion object {
            private val BRIDGE = 0x40
            private val SYNTHETIC = 0x1000
            private val MODIFIERS_IGNORE = Modifier.ABSTRACT or Modifier.STATIC or BRIDGE or SYNTHETIC
        }
    
    
        /**
         * 查找订阅者中所有的注解信息。
         * @param subscriber 订阅者
         */
        @Synchronized
        fun findSubscribeMethods(subscriber: Any) {
            var clazz: Class<*>? = subscriber.javaClass
            while (clazz != null && !isSystemClass(clazz.name)) {
                var methods = try {
                    // 返回所有的方法,包括public/private/protected/default
                    clazz.declaredMethods
                } catch (e: Exception) {
                    // public方法
                    clazz.methods
                }
    
                for (method in methods) {
                    val modifiers = method.modifiers
                    // 过滤方法的修饰符
                    if (Modifier.PUBLIC and modifiers != 0 && modifiers and MODIFIERS_IGNORE == 0) {
                        // 获取到注解
                        val annotation = method.getAnnotation(Subscriber::class.java)
                        // 如果注解不为空
                        if (annotation != null) {
                            val parameterTypes = method.parameterTypes
                            // 方法只接收一个参数
                            parameterTypes?.let {
                                if (parameterTypes.size == 1) {
                                    var type = parameterTypes[0]
                                    if (isAssignableFrom(IEvent::class.java, type)) {
                                        val eventType = EventType(type as Class<IEvent>, annotation.tag)
                                        val subscription = Subscription(WeakReference(subscriber), method, annotation.mode)
                                        // 保存订阅信息
                                        subscribe(eventType, subscription)
                                    }
                                }
                            }
                        }
                    }
                }
                clazz = clazz.superclass
            }
        }
    
    
        @Synchronized
        private fun subscribe(type: EventType, subscription: Subscription) {
            var subscriptionLists: CopyOnWriteArrayList<Subscription>? = getMatchEventType(type)
            if (subscriptionLists == null) {
                subscriptionLists = CopyOnWriteArrayList()
            }
    
            // 这就是为什么我们要重写equals和hashCode方法的原因
            if (subscriptionLists.contains(subscription)) {
                return
            }
    
            subscriptionLists.add(subscription)
            // 将事件类型key和订阅者信息存储到map中
            subscriberMap.put(type, subscriptionLists)
        }
    
    
        internal fun getMatchEventType(type: EventType): CopyOnWriteArrayList<Subscription>? {
            val keys = subscriberMap.keys
            return keys.firstOrNull { it == type }?.let { subscriberMap[it] }
        }
    
    
        @Synchronized
        fun removeSubscriberMethod(subscriber: Any) {
            // 注意删除的时候要使用迭代器
            var iterator = subscriberMap.values.iterator()
            while (iterator.hasNext()) {
                val subscriptions: MutableList<Subscription>? = iterator.next()
                subscriptions?.let { it: MutableList<Subscription>? ->
                    val subIterator = it!!.iterator()
                    while (subIterator.hasNext()) {
                        val subscription = subIterator.next()
                        // 获取引用
                        val cacheObject = subscription.subscriber.get()
                        cacheObject?.let {
                            if (isSameObject(cacheObject, subscriber)) {
                                subscriptions.remove(subscription)
                            }
                        }
                    }
                }
    
                // 如果针对某个Event的订阅者数量为空了,那么需要从map中清除
                if (subscriptions == null || subscriptions.isEmpty()) {
                    iterator.remove()
                }
            }
        }
    
        /** 判断是否是同一个对象 */
        private fun isSameObject(subOne: Any, subTwo: Any) = subOne === subTwo
    
        // 判断类是否为系统类
        private fun isSystemClass(clazzName: String): Boolean {
            if (clazzName.startsWith("java.") || clazzName.startsWith("javax.") || clazzName.startsWith("android.")) {
                return true
            }
            return false
        }
    
        // 判断某个类是否实现了IEvent接口
        private fun isAssignableFrom(a: Class<*>, b: Class<*>): Boolean = a.isAssignableFrom(b)
    }
    
    

    EventDispatcher-事件分发者

    看下怎样发送一个事件:

        /**
         * 发送事件通知。
         * @param event 事件
         * @param tag 事件标签,默认值为“”
         */
        @JvmOverloads
        fun post(event: IEvent, tag: String = TAG_DEFAULT) {
            val eventType = EventType(event.javaClass, tag)
            // 找出所有的事件订阅者
            val list = methodFinder.getMatchEventType(eventType)
            list?.let {
                // 分发事件
                EventDispatcher.dispatchEvent(event, it)
            }
        }
    

    EventDispatcher的实现:

    
    
    package core.zs.eventbus
    
    import android.os.Looper
    import core.zs.eventbus.handler.*
    import java.util.concurrent.CopyOnWriteArrayList
    
    /**
     * 事件分发者
     *
     * Created by Jonash on 2018/2/5.
     */
    
    object EventDispatcher {
    
        private val postHandler = PostEventHandler()
        private val mainHandler = MainEventHandler(Looper.getMainLooper())
        private val asyncHandler = AsyncEventHandler()
        private val bgHandler = BackgroundHandler()
    
        fun dispatchEvent(event: IEvent, list: CopyOnWriteArrayList<Subscription>) =
                list.forEach { it: Subscription ->
                    it.let {
                        val subscriber = it.subscriber.get()
                        subscriber?.let { subscriber: Any ->
                            val eventHandler = getEventHandler(it.threadMode)
                            eventHandler.handleEvent(it, event)
                        }
                    }
                }
    
        // 很据ThreadMode获取对应的事件处理器
        private fun getEventHandler(mode: ThreadMode): EventHandler = when (mode) {
            ThreadMode.POSTING -> postHandler
            ThreadMode.ASYNC -> asyncHandler
            ThreadMode.MAIN -> mainHandler
            ThreadMode.BACKGROUND -> bgHandler
        }
    }
    
    
    

    实现的是如此的easy。

    EventHandler

    我们在设计注解的时候,定义了一个订阅者执行线程的模式。

    package core.zs.eventbus
    
    /**
     * ThreadMode枚举类
     *
     * Created by Jonash on 2018/2/5.
     */
    enum class ThreadMode {
        POSTING, MAIN, ASYNC, BACKGROUND
    }
    
    

    说实话这个类完全是看的Java EventBus的代码。

    EventHandler的实现:

    package core.zs.eventbus.handler
    
    import core.zs.eventbus.IEvent
    import core.zs.eventbus.Subscription
    
    
    interface EventHandler {
        fun handleEvent(subscription: Subscription, event: IEvent)
    }
    
    

    设计要点:

    1. EventHandler就是一个接口。
    2. 如果你需要,你完全可以实现该接口。
    3. 为了对应ThreadMode的几种模式,设计实现了四种类型的事件处理。

    PostEventHandler

    
    package core.zs.eventbus.handler
    
    import core.zs.eventbus.IEvent
    import core.zs.eventbus.Subscription
    
    
    class PostEventHandler : EventHandler {
        override fun handleEvent(subscription: Subscription, event: IEvent) =subscription.invoke(event)
    }
    

    package core.zs.eventbus.handler
    
    import android.os.Handler
    import android.os.Looper
    import android.os.Message
    import core.zs.eventbus.IEvent
    import core.zs.eventbus.PendingPost
    import core.zs.eventbus.PendingPostQueue
    import core.zs.eventbus.Subscription
    
    class MainEventHandler(looper: Looper) : Handler(looper), EventHandler {
        private val queue: PendingPostQueue = PendingPostQueue()
        private var handlerActive = false
        override fun handleMessage(msg: Message?) {
            while (true) {
                var pendingPost = queue.poll()
                if (pendingPost == null) {
                    synchronized(this) {
                        pendingPost = queue.poll()
                        if (pendingPost == null) {
                            handlerActive = false
                            return
                        }
                    }
                }
                pendingPost!!.subscription!!.invoke(pendingPost!!.event!!)
                PendingPost.releasePendingPost(pendingPost!!)
            }
        }
    
        override fun handleEvent(subscription: Subscription, event: IEvent) {
            val post = PendingPost.obtainPendingPost(subscription, event)
            synchronized(this) {
                queue.enqueue(post)
                if (!handlerActive) {
                    handlerActive = true
                    sendMessage(Message.obtain())
                }
    
            }
        }
    }
    
    

    AsyncEventHandler

    package core.zs.eventbus.handler
    
    import core.zs.eventbus.*
    
    class AsyncEventHandler : EventHandler {
        private val queue: PendingPostQueue = PendingPostQueue()
    
        override fun handleEvent(subscription: Subscription, event: IEvent) {
            val pendingPost = PendingPost.obtainPendingPost(subscription, event)
            queue.enqueue(pendingPost)
            EventBus.getExecutorService().execute {
                val pendingPost = queue.poll() ?: throw IllegalStateException("No pending post available")
                pendingPost.subscription!!.invoke(pendingPost!!.event!!)
                PendingPost.releasePendingPost(pendingPost)
            }
        }
    }
    
    

    BackgroundHandler

    
    package core.zs.eventbus.handler
    
    import core.zs.eventbus.*
    
    class BackgroundHandler : Runnable, EventHandler {
    
        private val queue: PendingPostQueue = PendingPostQueue()
    
        @Volatile
        private var executorRunning: Boolean = false
    
        override fun handleEvent(subscription: Subscription, event: IEvent) {
            val pendingPost = PendingPost.obtainPendingPost(subscription, event)
            synchronized(this) {
                queue.enqueue(pendingPost)
                if (!executorRunning) {
                    executorRunning = true
                    EventBus.getExecutorService().execute(this)
                }
            }
        }
    
        override fun run() = try {
            try {
                while (true) {
                    var pendingPost = queue.poll(1000)
                    if (pendingPost == null) {
                        synchronized(this) {
                            pendingPost = queue.poll()
                            if (pendingPost == null) {
                                executorRunning = false
                                return
                            }
                        }
                    }
                    pendingPost!!.subscription!!.invoke(pendingPost!!.event!!)
                    PendingPost.releasePendingPost(pendingPost!!)
                }
            } catch (e: InterruptedException) {
                e.printStackTrace()
            }
        } finally {
            executorRunning = false
        }
    }
    

    PendingPost缓存池

    缓存池的实现主要包含两块内容:PendingPostQueue和PendingPost,使用了常规的实现。

    PendingPostQueue

    package core.zs.eventbus
    
    /**
     * PendingPost队列
     *
     * Created by Jonash on 2018/2/6.
     */
    class PendingPostQueue {
        private var head: PendingPost? = null
        private var tail: PendingPost? = null
    
        @Synchronized
        fun enqueue(post: PendingPost) = when {
            tail != null -> {
                tail!!.next = post
                tail = post
            }
            head == null -> {
                head = post
                tail = post
            }
            else -> throw IllegalStateException("Head present, but no tail")
        }
    
        @Synchronized
        fun poll(): PendingPost? {
            val post = head
            if (head != null) {
                head = head!!.next
                if (head == null) {
                    tail = null
                }
            }
            return post
        }
    
        @Synchronized
        @Throws(InterruptedException::class)
        fun poll(maxMillisToWait: Int): PendingPost? {
            if (head == null) {
                Thread.sleep(maxMillisToWait.toLong())
            }
            return poll()
        }
    }
    
    
    

    PendingPost:

    package core.zs.eventbus
    
    /**
     * 待处理请求。<br/>
     * 使用缓存池进行管理请求。
     *
     * Created by Jonash on 2018/2/6.
     */
    class PendingPost(var event: IEvent?, var subscription: Subscription?, var next: PendingPost? = null) {
    
        companion object {
            private val pool = arrayListOf<PendingPost>()
    
            @JvmStatic
            fun obtainPendingPost(subscription: Subscription, event: IEvent): PendingPost {
                if (pool.size > 0) {
                    val pendingPost = pool.removeAt(pool.size - 1)
                    pendingPost.next = null
                    pendingPost.subscription = subscription
                    pendingPost.event = event
                    return pendingPost
                }
                return PendingPost(event, subscription)
            }
    
            @JvmStatic
            fun releasePendingPost(pendingPost: PendingPost) {
                pendingPost.event = null
                pendingPost.subscription = null
                pendingPost.next = null
                synchronized(pool) {
                    if (pool.size < 1000) {
                        pool.add(pendingPost)
                    }
                }
            }
        }
    }
    
    

    总结语

    EventBus在我们的开发中如此常见,以至于我们才动手实现了这个简化Kotlin版本,希望你看完有所收获。

    相关文章

      网友评论

        本文标题:EventBus-Kotlin

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