手把手教你如何用 100 行代碼實現一個有生命週期感知能力的 EventBus

事件總線是一個項目開發中必不可少的能力,市面上也有幾個很是有名的事件庫,好比 EventBus 以及基於 RxJava 的 RxBus 等java

可是他們在使用的時候都必需要手動註冊/反註冊監聽,咱們可否實現一個不須要手動反註冊的事件總線呢,換句話說,咱們如何實現一個可以在生命週期 destroy 的時候自定解綁監聽的事件總線呢android

1. 一個 EventBus 的簡易模型

首先簡單構思一下咱們的 EventBus —— 任意對象均可以做爲事件被髮送,經過對象的 Class 類型來對事件進行訂閱和分發git

咱們先定義一下 LifecycleEventBus 的幾個基礎方法(暫不考慮生命週期感知能力)github

object LifecycleEventBus {
    // 添加監聽
    fun <T : Any> observe(eventType: Class<T>, observer: EventObserver<T>) {}
  
     // 移除監聽
    fun <T : Any> removeObserver(observer: EventObserver<T>) {}
  
     // 發送事件
     fun <T: Any> sendEvent(event: T) {}
}
複製代碼

抽象的監聽者接口 —— EventObservermarkdown

interface EventObserver<T : Any> {
    // 收到事件的時候回調函數,業務方在這裏實現對事件的處理邏輯
    fun onEvent(event: T)
}
複製代碼

這樣一個簡易的 EventBus 就搭建好了,核心思路是:以事件的 Class 類型做爲 key,對應的 Observer 做爲 value,將此 key-value 存儲起來,在發送事件的時候,根據傳入的 event 的 Class 類型,找到對應的 Observer 而後調用其 onEvent() 方法來分發事件,實現代碼以下:app

private val observerMap =
        mutableMapOf<Class<*>, MutableList<EventObserver<*>>>()

private fun addObserver(eventType: Class<*>, observer: EventObserver<*>) {
    val observers = observerMap[eventType] ?: MutableList()
    if (observerMap[eventType] == null) {
        observerMap[eventType] = observers
    }
  	if (!observers.contains(observer)) {
      	observers.add(observer)
    }
}

fun <T: Any> sendEvent(event: T) {
  	val eventType = event::class.java
        observerMap[eventType]?.forEach { observer ->
    		observer.onEvent(event)
        }
}

複製代碼

2. 讓 Observer 具有生命週期感知能力

藉助 androidx-lifecycle 中的 LifecycleEventObserver 咱們可讓 EventObserver 具有生命週期感知能力ide

若是對 LifecycleEventObserver 還比較陌生的同窗,能夠看看個人另外一篇文章《自定義生命週期以及實現生命週期感知能力 》函數

爲了兼容無生命週期的組件,咱們同時也要保留非生命週期感知能力的 Observer,爲此,咱們能夠抽象一個 Observer 包裝器 —— ObserverWrapperoop

open class ObserverWrapper(val observer: EventObserver<*>) {
    // 用於解綁生命週期,後續介紹 
  open fun detachObserver() {}
}
複製代碼

實現一個生命週期感知能力的 Observer —— LifecycleBoundObserverpost

private class LifecycleBoundObserver(
        private val owner: LifecycleOwner,
        observer: EventObserver<*>
    ) : ObserverWrapper(observer), LifecycleEventObserver {

        init {
            // 監聽生命週期的變化
            owner.lifecycle.addObserver(this)
        }

        override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
            val currentState: Lifecycle.State = source.lifecycle.currentState
            // 當生命週期即將銷燬的時候,移除監聽器
            if (currentState == Lifecycle.State.DESTROYED) {
                removeObserver(observer)
                return
            }
        }

  	// 在移除監聽 removeObserver 中回調被移除的 observer 的
  	// detachObserver 方法來解綁對生命週期的監聽
        override fun detachObserver() {
            super.detachObserver()
            owner.lifecycle.removeObserver(this)
        }
    }
複製代碼

而後添加監聽的地方修改以下:

// 因爲咱們使用了包裝類 ObserverWrapper,可是對於外部而言該包裝類是不可見的,爲了可以
// 正常移除 observer,咱們須要創建原始 observer 和 ObserverWrapper 的映射關係
// 這裏表現爲一個 HashMap
private val observerMap =
        mutableMapOf<Class<*>, ConcurrentHashMap<EventObserver<*>, ObserverWrapper>>()

private fun addObserver(eventType: Class<*>, observerWrapper: ObserverWrapper) {
        val observers = observerMap[eventType] ?: ConcurrentHashMap()
        if (observerMap[eventType] == null) {
            observerMap[eventType] = observers
        }
        observers.putIfAbsent(observerWrapper.observer, observerWrapper)
    }
複製代碼

同時在移除監聽器的時候也要把對生命週期的監聽移除:

fun <T : Any> removeObserver(observer: EventObserver<T>) {
        observerMap.forEach { (_, observers) ->
        val wrapper = observers.remove(observer)
        // 移除監聽的時候,同時也須要移除 lifecycle 的監聽
        wrapper?.detachObserver()
        }
    }
複製代碼

如此,咱們就實現了一個具有生命週期感知能力的 Observer,在使用的時候傳入對應的 LifecycleOwner 就可實現自動解綁監聽

LifecycleEventBus.observe(lifecycleOwner, MyEvent::class.java, observer)
複製代碼

而對於不須要生命週期感知能力的 Observer,咱們直接使用 ObserverWrapper 就能夠了,代碼很簡單,這裏再也不贅述

3. 支持線程切換

當前的實現 observer 將運行在 sendEvent() 所在的線程,不少時候,咱們可能在子線程發送事件,可是指望在主線程監聽,那麼咱們就須要實現線程切換能力,讓 Observer 能夠運行在指定的線程上

定義 Enum 線程模式 —— ThreadMode

/** * ORIGIN: Observer 將運行在發送事件所在的線程 * MAIN: Observer 將運行在主線程 */
enum class ThreadMode {ORIGIN, MAIN}
複製代碼

observe() 方法增長參數,默認是 ThreadMode.ORIGIN

fun <T : EVENT> observe( owner: LifecycleOwner, eventType: Class<T>, observer: EventObserver<T>, threadMode: ThreadMode = ThreadMode.ORIGIN ) {
        addObserver(eventType, LifecycleBoundObserver(owner, observer, threadMode))  
    }
複製代碼

threadMode 傳遞到 observer 中,當分發事件的時候,判斷若是 threadModeThreadMode.MAIN 切換到主線程便可

if (threadMode == ThreadMode.MAIN) {
            ThreadManager.runOnMainThread {
                onEvent(it)
            }
        } else {
            onEvent(it)
        }
複製代碼
object ThreadManager {

    private val mainHandler by lazy {
        Handler(Looper.getMainLooper())
    }

    fun runOnMainThread(block: () -> Unit) {
        if (isMainThread()) {
            block()
        } else {
            mainHandler.post(block)
        }
    }

    private fun isMainThread(): Boolean {
        return Looper.myLooper() == Looper.getMainLooper()
    }
}
複製代碼

至此,一個完整的具有生命週期感知能力的 EventBus 就完成了,總體代碼 100 左右,十分精簡。源碼詳情請到 GitHub 自行查看:LifecycleEventBus

Overview

  • 支持綁定 Lifecycle,可以在生命週期 onDestroy 的時候自動移除監聽,可與 Android Jetpack 中的 Lifecycle 組件無縫銜接
  • 支持監聽者線程切換
  • 支持手動註冊/反註冊監聽器
  • 代碼精簡,只有 100 行左右

相比 EventBus/RxBus 優點:

  • EventBus 中事件分發採用反射,LifecycleEventBus 以接口形式回調,不存在反射
  • RxBus 依賴 RxJava,對包大小有影響,LifecycleEventBus 代碼精簡,只有 100 行左右
  • LifecycleEventBus 具有 EventBus 和 RxBus 沒有的「生命週期感知能力」

Sample

// difine an event
data class LoginEvent(val userId: String)
// define an observer 
val observer = object : EventObserver<LoginEvent> {
            override fun onEvent(event: LoginEvent) {
                println("onEvent --> $event")
            }
        }
// add observer with lifecycleOwner.
// the observer will be removed when lifecycle goes to destroy
LifecycleEventBus.observe(this, LoginEvent::class.java, observer)
// send event
LifecycleEventBus.sendEvent(LoginEvent("12345"))
複製代碼

相關文章:

自定義生命週期以及實現生命週期感知能力

相關文章
相關標籤/搜索