首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >Android EventBus 源码解析:设计模式、原理与实现

Android EventBus 源码解析:设计模式、原理与实现

作者头像
木易士心
发布2025-11-30 08:56:23
发布2025-11-30 08:56:23
2490
举报

一、概述

EventBus 是一个基于 发布-订阅(Publish-Subscribe)模式 的事件总线框架,广泛应用于 Android 组件间的通信。它通过解耦组件之间的依赖关系,提升代码的可维护性和灵活性。


二、 EventBus 整体架构

1. 核心设计模式

设计模式

作用说明

观察者模式

实现事件的发布与订阅机制,核心逻辑所在

单例模式

默认提供全局唯一的 EventBus 实例

建造者模式

用于灵活配置自定义 EventBus 实例

策略模式

不同 ThreadMode 对应不同的线程调度策略

2. 架构概览

代码语言:javascript
复制
发布者 (Publisher) → EventBus → 订阅者 (Subscriber)
      ↓                      ↓
    post(event)          @Subscribe方法

二、 核心源码解析

1. 注册过程分析

代码语言:javascript
复制
// EventBus.java
public void register(Object subscriber) {
    Class<?> subscriberClass = subscriber.getClass();
    List<SubscriberMethod> subscriberMethods = 
        subscriberMethodFinder.findSubscriberMethods(subscriberClass);

    synchronized (this) {
        for (SubscriberMethod subscriberMethod : subscriberMethods) {
            subscribe(subscriber, subscriberMethod);
        }
    }
}
subscribe() 方法详解:
代码语言:javascript
复制
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);
    } else {
        if (subscriptions.contains(newSubscription)) {
            throw new EventBusException("Already registered to event " + eventType);
        }
    }

    // 按优先级插入
    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;
        }
    }

    // 记录订阅者关注的所有事件类型
    List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
    if (subscribedEvents == null) {
        subscribedEvents = new ArrayList<>();
        typesBySubscriber.put(subscriber, subscribedEvents);
    }
    subscribedEvents.add(eventType);

    // 处理粘性事件
    if (subscriberMethod.sticky) {
        Object stickyEvent = stickyEvents.get(eventType);
        if (stickyEvent != null) {
            postToSubscription(newSubscription, stickyEvent, 
                isMainThread() && subscriberMethod.threadMode == ThreadMode.MAIN);
        }
    }
}

关键点

  • 使用 CopyOnWriteArrayList 保证并发安全。
  • 支持按优先级排序,高优先级先接收事件。
  • 粘性事件在注册后立即触发。

2. 事件发布流程

代码语言:javascript
复制
public void post(Object event) {
    PostingThreadState postingState = currentPostingThreadState.get();
    List<Object> eventQueue = postingState.eventQueue;
    eventQueue.add(event);

    if (!postingState.isPosting) {
        postingState.isMainThread = isMainThread();
        postingState.isPosting = true;

        try {
            while (!eventQueue.isEmpty()) {
                postSingleEvent(eventQueue.remove(0), postingState);
            }
        } finally {
            postingState.isPosting = false;
            postingState.isMainThread = false;
        }
    }
}
分发单个事件:
代码语言:javascript
复制
private void postSingleEvent(Object event, PostingThreadState postingState) {
    Class<?> eventClass = event.getClass();
    boolean subscriptionFound = false;

    if (eventInheritance) {
        List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
        for (Class<?> clazz : eventTypes) {
            subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);
        }
    } else {
        subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);
    }

    if (!subscriptionFound) {
        if (logNoSubscriberMessages) {
            Log.d(TAG, "No subscribers registered for event " + eventClass);
        }
        if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class) {
            post(new NoSubscriberEvent(this, event));
        }
    }
}

特性支持

  • 支持事件继承:父类或接口事件也可被匹配。
  • 若无订阅者,可发送 NoSubscriberEvent 做兜底处理。

3. 线程模式处理

代码语言:javascript
复制
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
    switch (subscription.subscriberMethod.threadMode) {
        case POSTING:
            invokeSubscriber(subscription, event);
            break;
        case MAIN:
            if (isMainThread) {
                invokeSubscriber(subscription, event);
            } else {
                mainThreadPoster.enqueue(subscription, event);
            }
            break;
        case MAIN_ORDERED:
            if (mainThreadPoster != null) {
                mainThreadPoster.enqueue(subscription, event);
            } else {
                invokeSubscriber(subscription, event);
            }
            break;
        case BACKGROUND:
            if (isMainThread) {
                backgroundPoster.enqueue(subscription, event);
            } else {
                invokeSubscriber(subscription, event);
            }
            break;
        case ASYNC:
            asyncPoster.enqueue(subscription, event);
            break;
        default:
            throw new IllegalStateException("Unknown thread mode: " + 
                subscription.subscriberMethod.threadMode);
    }
}

五种线程模式

模式

描述

POSTING

在发布线程直接执行(默认)

MAIN

总是在主线程执行

MAIN_ORDERED

主线程中有序排队执行

BACKGROUND

后台线程执行(非主线程时直接调用)

ASYNC

异步线程池执行,不阻塞任何线程


4. 订阅方法查找器(SubscriberMethodFinder)

代码语言:javascript
复制
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
    List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
    if (subscriberMethods != null) return subscriberMethods;

    if (ignoreGeneratedIndex) {
        subscriberMethods = findUsingReflection(subscriberClass);
    } else {
        subscriberMethods = findUsingInfo(subscriberClass); // 使用编译期索引
    }

    METHOD_CACHE.put(subscriberClass, subscriberMethods);
    return subscriberMethods;
}
反射查找逻辑:
代码语言:javascript
复制
private List<SubscriberMethod> findUsingReflection(Class<?> subscriberClass) {
    List<SubscriberMethod> subscriberMethods = new ArrayList<>();
    Class<?> clazz = subscriberClass;

    while (clazz != null) {
        Method[] methods = clazz.getDeclaredMethods();
        for (Method method : methods) {
            int modifiers = method.getModifiers();
            if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
                Class<?>[] parameterTypes = method.getParameterTypes();
                if (parameterTypes.length == 1) {
                    Subscribe annotation = method.getAnnotation(Subscribe.class);
                    if (annotation != null) {
                        Class<?> eventType = parameterTypes[0];
                        SubscriberMethod subscriberMethod = new SubscriberMethod(
                            method, eventType, annotation.threadMode(),
                            annotation.priority(), annotation.sticky()
                        );
                        subscriberMethods.add(subscriberMethod);
                    }
                }
            }
        }
        clazz = clazz.getSuperclass();
    }
    return subscriberMethods;
}

优化点

  • 跳过系统类包(java., android.),减少无效扫描。
  • 缓存结果避免重复反射。
  • 支持从父类中查找 @Subscribe 方法。

四、 核心数据结构

1. 主要存储结构

代码语言:javascript
复制
public class EventBus {
    // 事件类型 → 所有订阅者的映射
    private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
    
    // 订阅者对象 → 其订阅的所有事件类型的映射
    private final Map<Object, List<Class<?>>> typesBySubscriber;
    
    // 粘性事件缓存:事件类型 → 最新事件实例
    private final Map<Class<?>, Object> stickyEvents;
    
    // 当前线程的发布状态(ThreadLocal)
    private final ThreadLocal<PostingThreadState> currentPostingThreadState;
}

2. 关键对象定义

代码语言:javascript
复制
public class SubscriberMethod {
    final Method method;
    final ThreadMode threadMode;
    final Class<?> eventType;
    final int priority;
    final boolean sticky;
}

public class Subscription {
    final Object subscriber;
    final SubscriberMethod subscriberMethod;
}

五、 性能优化设计

1. 缓存机制

代码语言:javascript
复制
// 避免重复反射
private static final Map<Class<?>, List<SubscriberMethod>> METHOD_CACHE = 
    new ConcurrentHashMap<>();

// 事件继承链缓存
private static final Map<Class<?>, List<Class<?>>> eventTypesCache = new HashMap<>();

2. 编译时索引优化(EventBus 3+)

使用 APT 在编译阶段生成索引类,避免运行时反射开销。

代码语言:javascript
复制
public class MyEventBusIndex implements SubscriberInfoIndex {
    private static final Map<Class<?>, SubscriberInfo> SUBSCRIBER_INDEX = new HashMap<>();

    static {
        putIndex(new SimpleSubscriberInfo(MainActivity.class, true, 
            new SubscriberMethodInfo[] {
                new SubscriberMethodInfo("onMessageEvent", MessageEvent.class, ThreadMode.MAIN),
                new SubscriberMethodInfo("onOtherEvent", OtherEvent.class, ThreadMode.BACKGROUND)
            }));
    }
}

💡 优势

  • 显著提升注册速度。
  • 减少冷启动耗时。
  • 更适合大型项目。

六、 线程处理实现(Poster)

1. HandlerPoster 示例(主线程调度)

代码语言:javascript
复制
final static class HandlerPoster extends Handler implements Poster {
    private final PendingPostQueue queue;

    public void enqueue(Subscription subscription, Object event) {
        PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
        synchronized (this) {
            queue.enqueue(pendingPost);
            if (!handlerActive) {
                handlerActive = true;
                sendMessage(obtainMessage());
            }
        }
    }

    @Override
    public void handleMessage(Message msg) {
        try {
            long started = SystemClock.uptimeMillis();
            while (true) {
                PendingPost pendingPost = queue.poll();
                if (pendingPost == null) break;
                eventBus.invokeSubscriber(pendingPost);
                
                // 控制单次处理时间,防止 ANR
                if (SystemClock.uptimeMillis() - started > maxMillisInsideHandleMessage) {
                    sendMessage(obtainMessage());
                    return;
                }
            }
        } finally {
            handlerActive = false;
        }
    }
}

设计亮点

  • 使用 Handler 将任务投递到主线程。
  • 采用 PendingPostQueue 缓冲待处理事件。
  • 限制单次处理时长,保障 UI 流畅。

|

七、 注意事项

1. 及时反注册

代码语言:javascript
复制
@Override
protected void onDestroy() {
    super.onDestroy();
    EventBus.getDefault().unregister(this);
}

❗未反注册会导致内存泄漏!

2. 谨慎使用粘性事件

  • 粘性事件长期驻留内存。
  • 接收后建议手动清除:removeStickyEvent()

3. 避免过度使用

  • 过度依赖 EventBus 会使逻辑分散,难以追踪。
  • 复杂业务推荐使用 ViewModel、LiveData 或 RxJava。

4. 注意线程安全

  • 多线程环境下确保共享数据同步访问。
  • 粘性事件可能被多个线程修改。

5. 混淆配置

代码语言:javascript
复制
-keepattributes Signature
-keepclassmembers class * {
    @org.greenrobot.eventbus.Subscribe <methods>;
}
-keep enum org.greenrobot.eventbus.ThreadMode { *; }

八、 总结

1.设计亮点

特性

说明

✅ 优雅的 API 设计

注解驱动,register/unregister/post 三步完成通信

✅ 高效的线程切换

多种 ThreadMode 满足不同场景需求

✅ 性能优化充分

缓存 + 编译期索引大幅降低反射成本

✅ 支持事件继承

发送子类事件可通知父类订阅者

✅ 灵活配置能力

建造者模式构建自定义 EventBus 实例

✅ 内存安全管理

提供反注册机制,配合生命周期管理防泄漏

2.优势与价值

EventBus 通过精巧的设计,在保持简洁易用的同时,实现了强大的组件通信能力。其融合了多种经典设计模式,并结合 Android 平台特性做了深度优化,是 观察者模式在移动端的优秀实践范本

尽管随着 Jetpack 组件(如 LiveData、Flow)的发展,部分场景下可替代 EventBus,但在跨模块通信、松耦合广播等场景中,EventBus 依然具有不可替代的价值。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-11-27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、概述
  • 二、 EventBus 整体架构
    • 1. 核心设计模式
    • 2. 架构概览
  • 二、 核心源码解析
    • 1. 注册过程分析
      • subscribe() 方法详解:
    • 2. 事件发布流程
      • 分发单个事件:
    • 3. 线程模式处理
    • 4. 订阅方法查找器(SubscriberMethodFinder)
      • 反射查找逻辑:
  • 四、 核心数据结构
    • 1. 主要存储结构
    • 2. 关键对象定义
  • 五、 性能优化设计
    • 1. 缓存机制
    • 2. 编译时索引优化(EventBus 3+)
  • 六、 线程处理实现(Poster)
    • 1. HandlerPoster 示例(主线程调度)
  • 七、 注意事项
    • 1. 及时反注册
    • 2. 谨慎使用粘性事件
    • 3. 避免过度使用
    • 4. 注意线程安全
    • 5. 混淆配置
  • 八、 总结
    • 1.设计亮点
    • 2.优势与价值
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档