一个超级简洁的事件分发器

339 阅读2分钟

首先说为什么不用EventBus,是因为他用到了反射,而这么简单的功能我觉得是没必要用反射的,其次就是为了一个事件要写很多方法,而如果要达到EventBus的效果其实代码是很简单的,所以我就自己写了一个事件分发器。代码如下:

package com.liuhc.library.event

import androidx.annotation.UiThread
import org.jetbrains.annotations.TestOnly
import java.lang.ref.ReferenceQueue
import java.lang.ref.WeakReference

typealias  CallBack<T> = (T) -> Unit

/// author:liuhaichao
/// description: 数据监听类
/// create date: 2020-10-12 on 5:08 PM
object DataListener {

    //事件,"监听者和该事件对应的回调"列表
    private val map: MutableMap<Class<*>, MutableList<CallbackSoftReference>> = mutableMapOf()
    private val queue = ReferenceQueue<Any>()

    /**
     * @param listener 监听者
     * @param eventClass 要监听的类型
     * @param callback 收到事件后会回调该方法
     */
    @Suppress("UNCHECKED_CAST")
    @UiThread
    @JvmStatic
    fun <T> listen(listener: Any, eventClass: Class<T>, callback: CallBack<T>) {
        clearQueue()
        if (!map.containsKey(eventClass)) {
            map[eventClass] = mutableListOf()
        }
        val list = map[eventClass]!!
        val transformCallback: (Any) -> Unit = { callback(it as T) }
        if (!list.contains(listener)) {
            list.add(CallbackSoftReference(listener, transformCallback, queue))
        }
    }

    /**
     * @param event 要发送的事件
     */
    @UiThread
    @JvmStatic
    fun publish(event: Any) {
        clearQueue()
        val eventToCallbackList = map[event::class.java]
        eventToCallbackList?.forEach {
            it.callBack(event)
        }
    }

    /**
     * @param listener 监听者
     * 在页面销毁的时候必须调用,防止内存泄漏,建议写到Activity或者Fragment的onDestroy方法里
     */
    @UiThread
    @JvmStatic
    fun destroy(listener: Any) {
        destroy(listener.hashCode())
    }

    @UiThread
    @JvmStatic
    private fun destroy(listenerHashCode: Int) {
        clearQueue()
        val entryIterator = map.entries.iterator()
        while (entryIterator.hasNext()) {
            val entry = entryIterator.next()
            val list = entry.value
            val listIterator = list.iterator()
            while (listIterator.hasNext()) {
                val callbackSoftReference = listIterator.next()
                if (callbackSoftReference.listenerHashcode == listenerHashCode) {
                    listIterator.remove()
                }
                if (list.isEmpty()) {
                    entryIterator.remove()
                }
            }
        }
    }

    private fun clearQueue() {
        var callbackSoftReference: CallbackSoftReference?
        while (true) {
            callbackSoftReference = queue.poll() as? CallbackSoftReference
            if (callbackSoftReference == null) {
                break
            }
            destroy(callbackSoftReference.listenerHashcode)
        }
    }

    private class CallbackSoftReference(listener: Any, val callBack: CallBack<Any>, queue: ReferenceQueue<Any>) :
        WeakReference<Any>(listener, queue) {
        val listenerHashcode = listener.hashCode()
    }

    @TestOnly
    fun isEmpty(): Boolean {
        clearQueue()
        return map.isEmpty()
    }
}

使用方法

//在onCreate或onStart里注册
DataListener.listen(this, String::class.java) {
    println(it)
}
//发布事件
DataListener.publish("abc")
//在onDestroy或onStop里解绑
DataListener.destroy(this)

代码其实是很简单的,主要是用到了软引用。使用方法可以在onCreate里注册,在onDestroy里解绑,或者在onStart里注册,在onStop里解绑。

如果在onStart和onStop里分别注册和解绑的话,那我们在页面onStop后就接收不到事件了,如果在onCreate里注册在onDestroy里解绑的话,我们知道Activity在被系统回收的时候onDestroy方法是不一定会调用的,所以像EventBus解绑不及时就会导致内存泄漏。所以我这里增加了软引用,即使没有解绑成功,在内存不足的时候页面也可以被正常回收,但是这只是兜底方案,正常使用还是应该在父类的onDestroy里调用DataListener.destroy(this),不要等到内存不足才被系统回收。