其他
LightEventBus-轻量高效的事件总线
https://juejin.cn/user/2999123450531982/posts
https://github.com/BillyWei01/LightEventBus
https://juejin.cn/post/7379831020495749157
订阅方法的定义:EventBus 通过给类方法添加 @Subscribe 注解来标记“订阅方法”, 并在注解中传入参数。LightEventBus 订阅方法不需要声明为类的方法,不需要注解,只需要创建EventHandler实例。 register/unregister:EventBus 需要传入声明了订阅方法的“订阅者”对象。
class Event2
class EventHandler {
@Subscribe
fun onEvent1(event: Event1) {
}
@Subscribe(threadMode = ThreadMode.ASYNC, sticky = true, priority = 100)
fun onEvent2(event: Event2) {
}
}
class EventBusTest {
private val subscriber = EventHandler()
fun test() {
EventBus.getDefault().register(subscriber)
EventBus.getDefault().post(Event1())
EventBus.getDefault().postSticky(Event2())
EventBus.getDefault().unregister(subscriber)
}
}
class Event2
class LightEventBusTest {
private val handlers = listOf(
EventHandler.create<Event1> { event ->
},
EventHandler.create<Event2>(threadMode = ThreadMode.ASYNC, sticky = true, priority = 100) { event ->
}
)
fun test() {
EventBus.getDefault().register(handlers)
EventBus.getDefault().post(Event1())
EventBus.getDefault().postSticky(Event2())
EventBus.getDefault().unregister(handlers)
}
}
https://github.com/BillyWei01/LightEventBus/blob/main/app/src/main/java/io/github/lightevent/benchmark/Benchmark.kt
object IndexEventBusTest {
fun test(): String {
val t0 = System.nanoTime()
val handler1 = IndexEvent1Handler()
// 这里触发“添加索引”,涉及类加载和方法查找
val eventBus = EventBus.builder().addIndex(AppEventBusIndex()).build()
val t1 = System.nanoTime()
eventBus.register(handler1)
val t2 = System.nanoTime()
eventBus.post(Event1())
val t3 = System.nanoTime()
eventBus.unregister(handler1)
val t4 = System.nanoTime()
return "prepare:${formatTime(t1 - t0)}, register:${formatTime(t2 - t1)}, " +
"post${formatTime(t3 - t2)}, unregister:${formatTime(t4 - t3)}"
}
}
class IndexEvent1Handler {
@Subscribe(threadMode = ThreadMode.POSTING)
fun onEvent1(event: Event1) {
}
}
object ReflectionEventBusTest {
fun test(): String {
val t0 = System.nanoTime()
val handler1 = ReflectionEvent1Handler()
val t1 = System.nanoTime()
// 查找方法发生在注册阶段
EventBus.getDefault().register(handler1)
val t2 = System.nanoTime()
EventBus.getDefault().post(Event1())
val t3 = System.nanoTime()
EventBus.getDefault().unregister(handler1)
val t4 = System.nanoTime()
return "prepare:${formatTime(t1 - t0)}, register:${formatTime(t2 - t1)}, " +
"post${formatTime(t3 - t2)}, unregister:${formatTime(t4 - t3)}"
}
}
class ReflectionEvent1Handler {
@Subscribe(threadMode = ThreadMode.POSTING)
fun onEvent1(event: Event1) {
}
}
object LightEventTest {
fun test() : String {
val t0 = System.nanoTime()
val handler1 = listOf(EventHandler.create<Event1> { })
val t1 = System.nanoTime()
EventBus.getDefault().register(handler1)
val t2 = System.nanoTime()
EventBus.getDefault().post(Event1())
val t3 = System.nanoTime()
EventBus.getDefault().unregister(handler1)
val t4 = System.nanoTime()
return "prepare:${formatTime(t1 - t0)}, register:${formatTime(t2 - t1)}, " +
"post${formatTime(t3 - t2)}, unregister:${formatTime(t4 - t3)}"
}
}
EventBus使用“订阅索引”,注册时比用反射快一些,但是准备阶段则相对耗时。 LightEventBus的注册阶段不需要查找方法,所以比EventBus要快。 LightEventBus的发送默认不使用事件继承,所以发送速度也比EventBus快。
// 事件 -> 订阅方法列表
private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
// 订阅者 -> 关注的事件
private final Map<Object, List<Class<?>>> typesBySubscriber;
public void register(Object subscriber) {
}
public synchronized void unregister(Object subscriber) {
}
public void post(Object event) {
}
}
typesBySubscriber: 订阅者 -> 关注的事件 subscriptionsByEventType: 事件 -> 订阅方法列表
检索订阅者的方法,查找其中添加了@Subscribe注解的方法; 取方法参数的类型得到eventType,取注解参数得到threadMode,sticky等参数; 将“订阅者,事件,方法,以及其他参数”记录到 subscriptionsByEventType和 typesBySubscriber两个Map中。
索引订阅者关注的事件列表,遍历事件,移除subscriptionsByEventType中关于此事件的订阅方法; 从typesBySubscriber中移除订阅者以及所关联的事件列表。
在subscriptionsByEventType中索引Event所关联的Subscription列表; 遍历Subscription列表,执行其方法。 默认情况下,启用事件继承(eventInheritance) 。
final Object subscriber;
final SubscriberMethod subscriberMethod;
}
public class SubscriberMethod {
final Method method;
final ThreadMode threadMode;
final Class<?> eventType;
final int priority;
final boolean sticky;
}
订阅方法不需要定义成某个类的方法,可以一个方法接口(lambda形式)替代。 弱化了订阅者的概念(去掉subscriber),注册时只需要传入方法列表,也不用考虑继承等复杂因素。
typealias Action<T> = (event: T) -> Unit
class EventHandler<T>( // 对应 SubscriberMethod
val eventType: Class<*>,
val threadMode: ThreadMode,
val sticky: Boolean,
val priority: Int,
val action: Action<T> // 对应 Method
) {
companion object {
// 增加一个静态方法,方便构建实例 (Kotlin语法糖)
inline fun <reified T> create(
threadMode: ThreadMode = ThreadMode.POSTING,
sticky: Boolean = false,
priority: Int = 0,
noinline action: Action<T>
): EventHandler<T> {
return EventHandler(T::class.java, threadMode, sticky, priority, action)
}
}
}
API改变了,这一点 “用法” 一章已有说明,这里不再赘述; 实现上简化了很多,不再需要“查找方法”,性能提升了不少,代码也简化了一大半。
post(event, false)
}
fun post(event: Any, eventInheritance: Boolean) {
}
// 事件 -> 订阅方法列表
private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
public void register(Object subscriber) {
Class<?> subscriberClass = subscriber.getClass();
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
Class<?> eventType = subscriberMethod.eventType;
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
if (subscriptions == null) {
subscriptions = new CopyOnWriteArrayList<>();
subscriptionsByEventType.put(eventType, subscriptions);
}
int size = subscriptions.size();
for (int i = 0; i <= size; i++) {
if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
subscriptions.add(i, newSubscription);
break;
}
}
}
}
查找到订阅者的方法列表后,遍历订阅者方法列表; 在注册订阅方法时,根据方法的事件类型,从 subscriptionsByEventType 检索事件类型对应的方法列表; 从头开始比较,找到优先级小于当前订阅方法的位置,插入该方法的后面(使列表中的方法,按按优先级逆序排列)。
// 事件 -> 订阅者(集合)
private val subscriptions = mutableMapOf<Class<*>, ArrayList<EventHandler<*>>>()
// 正在发送事件的线程的数量
private val postingCount = AtomicInteger()
fun register(handlers: List<EventHandler<*>>) {
synchronized(this) {
handlers.forEach { handler ->
val eventType = handler.eventType
val list = subscriptions.getOrPut(eventType) { ArrayList(2) }
// 如果没有线程正在访问方法列表,则直接添加;
// 如果有,则执行 CopyOnWrite
if (postingCount.get() == 0) {
addHandler(list, handler)
} else {
subscriptions[eventType] = ArrayList(list).apply { addHandler(this, handler) }
}
}
}
}
// 按优先级逆序排列
private fun addHandler(list: ArrayList<EventHandler<*>>, handler: EventHandler<*>) {
val size = list.size
val priority = handler.priority
// 快速判断:列表为空,或者优先级小于等于列表末尾,则直接插入列表末尾
if (size == 0 || priority <= list[size - 1].priority) {
list.add(handler)
} else {
for (i in 0..<size) {
if (priority > list[i].priority ) {
list.add(i, handler)
return
}
}
list.add(size, handler)
}
}
}
优先级处理。由于大部分情况下,使用者不会特别去设置优先级,所有订阅方的优先级基本都是0。因此,插入列表时,可以直接和列表末尾的方法比较,如果小于或等于其优先级,则插入队列末尾。如此,就不需要遍历整个列表了。 CopyOnWrite LightEventBus 增加了一个 postingCount 变量,在发生事件时+1;在执行register和unregister时,如果 postingCount 为0,则说明没有任何线程在遍历订阅方法列表;这时候可以直接添加在当前的方法列表中,而不需要先Copy再Write。