EventBus-Kotlin

안 드 로 이 드 개발 자로 서 아직 Kotlin 을 사용 하지 않 았 다 면 걸음 이 느 린 지 생각해 봐 야 할 것 입 니 다.본 고 는 여러분 을 데 리 고 Kotlin 버 전의 EventBus 를 작성 하고 한편 으로 는 Kotlin 이 어떻게 생 겼 는 지,한편 으로 는 EventBus 의 디자인 사상 을 이해 하고 자 합 니 다.
EventBus 원리
EventBus 는 관찰자 모드 를 기반 으로 하고 핵심 은 사건 이다.이벤트 게시 와 구독 을 통 해 구성 요소 간 의 통신 을 실현 합 니 다.EventBus 는 기본적으로 하나의 사례 로 존재 합 니 다.자바 에 서 는 Synchronized 를 사용 하여 스 레 드 안전 을 확보 해 야 합 니 다.
일반적으로 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>()
    // 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

}


디자인 요점:
  • EventBus 유형 은 object 이 고 실제 자바 로 컴 파일 된 후 하나의 사례 가 존재 합 니 다

  • 4.567917.등록 과 취소 사용 스 레 드 는 찾 는 과정 이 시간 이 걸 리 기 때 문 입 니 다.(컴 파일 할 때 주석 을 사용 하 는 것 을 고려 할 수 있 습 니 다)4.567917.post 는 기본 매개 변 수 를 사 용 했 습 니 다.기본 매개 변 수 는''이 고 그 존 재 를 무시 할 수 있 습 니 다.그러나 같은 이벤트 유형 을 받 아들 이 는 특별한 맞 춤 형 방법 이 필요 할 때 유용 하 다
    왜 Builder 모드 를 사용 하여 EventBus 를 실현 하지 않 았 습 니까?답:사건 없어 요
    4.567917.사건 의 유형[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
        }
    }
    
    

    디자인 요점:
    4.567917.이런 존 재 는 주로 사건 유형 정 보 를 저장 하기 위 한 것 이다
    4.567917.이것 은 Map 의 key 로 존재 하기 때문에 eventClass 와 tag 의 유형 은 모두 val 로 변 할 수 없습니다
    4.567917.똑 같이 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)
    
    

    디자인 요점:
    주 해 는 방법 에 사용 된다
    4.567917.주 해 를 반사 적 으로 해석 해 야 하기 때문에 실행 시 주 해 를 사용 합 니 다
    IEvent
    package core.zs.eventbus
    
    /**
     *       .
     *
     * Created by Jonash on 2018/2/5.
     */
    abstract class IEvent
    

    디자인 포인트:
    4.567917.표시 로 만 존재 하 며 구독 방법 을 검사 하 는 매개 변 수 는 IEvent 를 계승 해 야 합 니 다
    4.567917.사건 에 대한 요구 가 있 으 면 이런 종 류 를 수정 할 수 있다
    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,
                       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)
        }
    }
    
    

    디자인 요점:
    이 종 류 는 구독 자의 상세 한 정 보 를 저장 하 는 데 사용 된다
    4.567917.중복 저장 을 방지 하기 위해 equals 와 hashCode 방법 을 다시 썼 습 니 다
    4.567917.뒤의 반사 호출 방법 을 위해 invoke 방법 을 설계 했다
    Subscriber MethodFinder-발견 자
    상세 하 게 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>) {
    
        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, 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? = 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? {
            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? = iterator.next()
                subscriptions?.let { it: MutableList? ->
                    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)
    }
    
    

    이벤트 Dispatcher-이벤트 배포 자
    이벤트 발송 방법 보기:
        /**
         *       。
         * @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) =
                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
        }
    }
    
    
    

    이렇게 쉽게 이 루어 졌 다.
    EventHandler
    우 리 는 주 해 를 설계 할 때 구독 자가 스 레 드 를 실행 하 는 모델 을 정의 했다.
    package core.zs.eventbus
    
    /**
     * ThreadMode   
     *
     * Created by Jonash on 2018/2/5.
     */
    enum class ThreadMode {
        POSTING, MAIN, ASYNC, BACKGROUND
    }
    
    

    솔직히 이 종 류 는 완전히 보 는 자바 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)
    }
    
    

    디자인 요점:
    4.567917.EventHandler 는 하나의 인터페이스 이다
    4.567917.필요 하 다 면 이 인 터 페 이 스 를 충분히 실현 할 수 있 습 니 다
    4.567917.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 캐 시 풀
    캐 시 풀 의 실현 은 주로 두 가지 내용 을 포함 합 니 다.PendingPost Queue 와 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
    
    /**
     *      。
    * 。 * * 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() @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 버 전 을 실현 하기 시 작 했 습 니 다.보시 고 얻 은 것 이 있 기 를 바 랍 니 다.

    좋은 웹페이지 즐겨찾기