Greenrobot-EventBus源码学习(四)

本文深入探讨了greenrobot的EventBus框架,介绍了其使用方法、创建实例的方式及关键类的实现原理,包括SubscriberMethod、Subscription、Subscribe注解等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

EventBus 深入学习四之实例&类说明

本篇开始,则转向greenrobot/EventBus, 之前基本上将Guava中设计的思路捋了一遍,逻辑比较简单和清晰,接下来则看下广泛运用于android的这个框架又有什么不一样的地方,有什么独特的精妙所在

一些废话

开始之前,当然是要先把代码donw下来,然后本机能跑起来才行; so,基本的环境要搞起, Android Studio 将作为主要的ide

在导入工程之后,发现一直报一个 jdk版本过低的异常,解决方法是设置ide的jdk环境,如下,指定jdk为8就可以了

输入图片说明

使用方法一览

在开始之前,先看下这个框架怎么用,会用了之后才能更好的考虑怎么去分析拆解

用法相比较Guava EventBus 差别不大, 除了支持注解方式之外,还支持非注解形式,如

public class EventBusIndexTest {
    private String value;

    @Test
    /** Ensures the index is actually used and no reflection fall-back kicks in. */
    public void testManualIndexWithoutAnnotation() {
        SubscriberInfoIndex index = new SubscriberInfoIndex() {

            @Override
            public SubscriberInfo getSubscriberInfo(Class<?> subscriberClass) {
                Assert.assertEquals(EventBusIndexTest.class, subscriberClass);
                SubscriberMethodInfo[] methodInfos = {
                        new SubscriberMethodInfo("someMethodWithoutAnnotation", String.class)
                };
                return new SimpleSubscriberInfo(EventBusIndexTest.class, false, methodInfos);
            }
        };

        EventBus eventBus = EventBus.builder().addIndex(index).build();
        eventBus.register(this);
        eventBus.post("Yepp");
        eventBus.unregister(this);
        Assert.assertEquals("Yepp", value);
    }

    public void someMethodWithoutAnnotation(String value) {
        this.value = value;
    }
}

上面与我们之前的使用,区别主要在于 EventBus对象的获取,down下来的工程中,有一个基础的测试类,给我们演示了不少的使用方式

@RunWith(AndroidJUnit4.class)
public class EventBusBasicTest {

    public static class WithIndex extends EventBusBasicTest {
        @Test
        public void dummy() {}

    }

    @Rule
    public final UiThreadTestRule uiThreadTestRule = new UiThreadTestRule();

    protected EventBus eventBus;
    private String lastStringEvent;
    private int countStringEvent;
    private int countIntEvent;
    private int lastIntEvent;
    private int countMyEventExtended;
    private int countMyEvent;
    private int countMyEvent2;

    @Before
    public void setUp() throws Exception {
        eventBus = new EventBus();
    }

    @Test
    @UiThreadTest
    public void testRegisterAndPost() {
        // Use an activity to test real life performance
        TestActivity testActivity = new TestActivity();
        String event = "Hello";

        long start = System.currentTimeMillis();
        eventBus.register(testActivity);
        long time = System.currentTimeMillis() - start;
        Log.d(EventBus.TAG, "Registered in " + time + "ms");

        eventBus.post(event);

        assertEquals(event, testActivity.lastStringEvent);
    }

// 无订阅者
    @Test
    public void testPostWithoutSubscriber() {
        eventBus.post("Hello");
    }

    @Test
    public void testUnregisterWithoutRegister() {
        // Results in a warning without throwing
        eventBus.unregister(this);
    }

    // This will throw "out of memory" if subscribers are leaked
    @Test
    public void testUnregisterNotLeaking() {
        int heapMBytes = (int) (Runtime.getRuntime().maxMemory() / (1024L * 1024L));
        for (int i = 0; i < heapMBytes * 2; i++) {
            EventBusBasicTest subscriber = new EventBusBasicTest() {
                byte[] expensiveObject = new byte[1024 * 1024];
            };
            eventBus.register(subscriber);
            eventBus.unregister(subscriber);
            Log.d("Test", "Iteration " + i + " / max heap: " + heapMBytes);
        }
    }

    @Test
    public void testRegisterTwice() {
        eventBus.register(this);
        try {
            eventBus.register(this);
            fail("Did not throw");
        } catch (RuntimeException expected) {
            // OK
        }
    }

    @Test
    public void testIsRegistered() {
        assertFalse(eventBus.isRegistered(this));
        eventBus.register(this);
        assertTrue(eventBus.isRegistered(this));
        eventBus.unregister(this);
        assertFalse(eventBus.isRegistered(this));
    }

    @Test
    public void testPostWithTwoSubscriber() {
        EventBusBasicTest test2 = new EventBusBasicTest();
        eventBus.register(this);
        eventBus.register(test2);
        String event = "Hello";
        eventBus.post(event);
        assertEquals(event, lastStringEvent);
        assertEquals(event, test2.lastStringEvent);
    }

    @Test
    public void testPostMultipleTimes() {
        eventBus.register(this);
        MyEvent event = new MyEvent();
        int count = 1000;
        long start = System.currentTimeMillis();
        // Debug.startMethodTracing("testPostMultipleTimes" + count);
        for (int i = 0; i < count; i++) {
            eventBus.post(event);
        }
        // Debug.stopMethodTracing();
        long time = System.currentTimeMillis() - start;
        Log.d(EventBus.TAG, "Posted " + count + " events in " + time + "ms");
        assertEquals(count, countMyEvent);
    }

    @Test
    public void testMultipleSubscribeMethodsForEvent() {
        eventBus.register(this);
        MyEvent event = new MyEvent();
        eventBus.post(event);
        assertEquals(1, countMyEvent);
        assertEquals(1, countMyEvent2);
    }

    @Test
    public void testPostAfterUnregister() {
        eventBus.register(this);
        eventBus.unregister(this);
        eventBus.post("Hello");
        assertNull(lastStringEvent);
    }

    @Test
    public void testRegisterAndPostTwoTypes() {
        eventBus.register(this);
        eventBus.post(42);
        eventBus.post("Hello");
        assertEquals(1, countIntEvent);
        assertEquals(1, countStringEvent);
        assertEquals(42, lastIntEvent);
        assertEquals("Hello", lastStringEvent);
    }

    @Test
    public void testRegisterUnregisterAndPostTwoTypes() {
        eventBus.register(this);
        eventBus.unregister(this);
        eventBus.post(42);
        eventBus.post("Hello");
        assertEquals(0, countIntEvent);
        assertEquals(0, lastIntEvent);
        assertEquals(0, countStringEvent);
    }

    @Test
    public void testPostOnDifferentEventBus() {
        eventBus.register(this);
        new EventBus().post("Hello");
        assertEquals(0, countStringEvent);
    }

    @Test
    public void testPostInEventHandler() {
        RepostInteger reposter = new RepostInteger();
        eventBus.register(reposter);
        eventBus.register(this);
        eventBus.post(1);
        assertEquals(10, countIntEvent);
        assertEquals(10, lastIntEvent);
        assertEquals(10, reposter.countEvent);
        assertEquals(10, reposter.lastEvent);
    }

    @Test
    public void testHasSubscriberForEvent() {
        assertFalse(eventBus.hasSubscriberForEvent(String.class));

        eventBus.register(this);
        assertTrue(eventBus.hasSubscriberForEvent(String.class));

        eventBus.unregister(this);
        assertFalse(eventBus.hasSubscriberForEvent(String.class));
    }

    @Test
    public void testHasSubscriberForEventSuperclass() {
        assertFalse(eventBus.hasSubscriberForEvent(String.class));

        Object subscriber = new ObjectSubscriber();
        eventBus.register(subscriber);
        assertTrue(eventBus.hasSubscriberForEvent(String.class));

        eventBus.unregister(subscriber);
        assertFalse(eventBus.hasSubscriberForEvent(String.class));
    }

    @Test
    public void testHasSubscriberForEventImplementedInterface() {
        assertFalse(eventBus.hasSubscriberForEvent(String.class));

        Object subscriber = new CharSequenceSubscriber();
        eventBus.register(subscriber);
        assertTrue(eventBus.hasSubscriberForEvent(CharSequence.class));
        assertTrue(eventBus.hasSubscriberForEvent(String.class));

        eventBus.unregister(subscriber);
        assertFalse(eventBus.hasSubscriberForEvent(CharSequence.class));
        assertFalse(eventBus.hasSubscriberForEvent(String.class));
    }

    @Subscribe
    public void onEvent(String event) {
        lastStringEvent = event;
        countStringEvent++;
    }

    @Subscribe
    public void onEvent(Integer event) {
        lastIntEvent = event;
        countIntEvent++;
    }

    @Subscribe
    public void onEvent(MyEvent event) {
        countMyEvent++;
    }

    @Subscribe
    public void onEvent2(MyEvent event) {
        countMyEvent2++;
    }

    @Subscribe
    public void onEvent(MyEventExtended event) {
        countMyEventExtended++;
    }

    public static class TestActivity extends Activity {
        public String lastStringEvent;

        @Subscribe
        public void onEvent(String event) {
            lastStringEvent = event;
        }
    }

    public static class CharSequenceSubscriber {
        @Subscribe
        public void onEvent(CharSequence event) {
        }
    }

    public static class ObjectSubscriber {
        @Subscribe
        public void onEvent(Object event) {
        }
    }

    public class MyEvent {
    }

    public class MyEventExtended extends MyEvent {
    }

    public class RepostInteger {
        public int lastEvent;
        public int countEvent;

        @Subscribe
        public void onEvent(Integer event) {
            lastEvent = event;
            countEvent++;
            assertEquals(countEvent, event.intValue());

            if (event < 10) {
                int countIntEventBefore = countEvent;
                eventBus.post(event + 1);
                // All our post calls will just enqueue the event, so check count is unchanged
                assertEquals(countIntEventBefore, countIntEventBefore);
            }
        }
    }

}

EventBus实例创建

提供了三中创建方式,一个是直接使用默认实例; 一个最简单普通的构造;再者使用 EventBusBuilder来构建, 下面会分别对上面的几种情况进行分析说明

1. EventBus.getDefault() 默认实例

这里使用了最常见的延迟加载的单例模式,来获取实例,注意下 snchronized 的使用位置,并没有放在方法签名上( 注意这个类不是严格意义上的单例,因为构造函数是public)

static volatile EventBus defaultInstance;

 /** Convenience singleton for apps using a process-wide EventBus instance. */
    public static EventBus getDefault() {
        if (defaultInstance == null) {
            synchronized (EventBus.class) {
                if (defaultInstance == null) {
                    defaultInstance = new EventBus();
                }
            }
        }
        return defaultInstance;
    }

此外另一种常见的单例模式下面也顺手贴出,主要利用静态内部类

    private static class InnerInstance {
        static volatile EventBus defaultInstance = new EventBus();
    }

    public static EventBus getInstance() {
        return InnerInstance.defaultInstance;
    }

2. new EventBus() 构造器

这个比较简单了,基本上获取实例的方法都这么玩,对于EventBus而言,把这个放开的一个关键点是再你选的系统中,不会限制你的EventBus实例个数,也就是说,你的系统可以有多个并行的消息-事务总线

3. Builder模式

这个模式也是比较常用的,对于构建复杂对象时,选择的Builder模式,对这个的分析之前,先看下 EventBus的属性

static volatile EventBus defaultInstance;

private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();
private static final Map<Class<?>, List<Class<?>>> eventTypesCache = new HashMap<>();

private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
private final Map<Object, List<Class<?>>> typesBySubscriber;
private final Map<Class<?>, Object> stickyEvents;

private final ThreadLocal<PostingThreadState> currentPostingThreadState = new ThreadLocal<PostingThreadState>() {
   @Override
   protected PostingThreadState initialValue() {
       return new PostingThreadState();
   }
};

private final HandlerPoster mainThreadPoster;
private final BackgroundPoster backgroundPoster;
private final AsyncPoster asyncPoster;
private final SubscriberMethodFinder subscriberMethodFinder;
private final ExecutorService executorService;

private final boolean throwSubscriberException;
private final boolean logSubscriberExceptions;
private final boolean logNoSubscriberMessages;
private final boolean sendSubscriberExceptionEvent;
private final boolean sendNoSubscriberEvent;
private final boolean eventInheritance;

常用类分析

1. SubscriberMethod 订阅者回调方法封装类

这个类主要保存的就是订阅者的回调方法相关信息

final Method method;
final ThreadMode threadMode;
// 监听的事件类型, 也就是注册方法的唯一参数类型
final Class<?> eventType;
// 定义监听消息的优先级
final int priority;
//在Android开 发中,Sticky事件只指事件消费者在事件发布之后才注册的也能接收到该事件的特殊类型
final boolean sticky;
/** Used for efficient comparison */
String methodString;

ThreadMode, 区分了以下几种类型,且各自的意思如下

   /**
    * 和发布消息的公用一个线程
     * Subscriber will be called in the same thread, which is posting the event. This is the default. Event delivery
     * implies the least overhead because it avoids thread switching completely. Thus this is the recommended mode for
     * simple tasks that are known to complete is a very short time without requiring the main thread. Event handlers
     * using this mode must return quickly to avoid blocking the posting thread, which may be the main thread.
     */
    POSTING,

    /**
     * 再android的主线程下
     * Subscriber will be called in Android's main thread (sometimes referred to as UI thread). If the posting thread is
     * the main thread, event handler methods will be called directly. Event handlers using this mode must return
     * quickly to avoid blocking the main thread.
     */
    MAIN,

    /**
     * Subscriber will be called in a background thread. If posting thread is not the main thread, event handler methods
     * will be called directly in the posting thread. If the posting thread is the main thread, EventBus uses a single
     * background thread, that will deliver all its events sequentially. Event handlers using this mode should try to
     * return quickly to avoid blocking the background thread.
     */
    BACKGROUND,

    /**
     * Event handler methods are called in a separate thread. This is always independent from the posting thread and the
     * main thread. Posting events never wait for event handler methods using this mode. Event handler methods should
     * use this mode if their execution might take some time, e.g. for network access. Avoid triggering a large number
     * of long running asynchronous handler methods at the same time to limit the number of concurrent threads. EventBus
     * uses a thread pool to efficiently reuse threads from completed asynchronous event handler notifications.
     */
    ASYNC

2. Subscription 注册回调信息封装类

EventBus中维护的订阅关系, 在对象注册时注入到 EventBus, 推送消息时,则会从EventBus 中获取

EventBus 中维护的订阅者关系数据结构就是 Map<Class<?>, CopyOnWriteArrayList<Subscription>>, 其中key为事件类型, value就是订阅者信息集合

final Object subscriber;
final SubscriberMethod subscriberMethod;

3. Subscribe 注解

注解标注在类的唯一参数的公共方法上, 表示这个方法就是我们注册的订阅者回调方法

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Subscribe {
    ThreadMode threadMode() default ThreadMode.POSTING;

    /**
     * If true, delivers the most recent sticky event (posted with
     * {@link EventBus#postSticky(Object)}) to this subscriber (if event available).
     */
    boolean sticky() default false;

    /** Subscriber priority to influence the order of event delivery.
     * Within the same delivery thread ({@link ThreadMode}), higher priority subscribers will receive events before
     * others with a lower priority. The default priority is 0. Note: the priority does *NOT* affect the order of
     * delivery among subscribers with different {@link ThreadMode}s! */
    int priority() default 0;
}

4. SubscriberMethodFinder 辅助工具类,获取订阅者中的回调方法

顾名思义,这个就是用来获取订阅者类中的所有注册方法的,支持两种方式,一个是上面的注解方式;还有一个则是利用SubscriberInfo

核心方法:List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass)

5. SubscriberInfo 定义获取订阅者注册方法的接口

通常这个会和SubscriberInfoIndex 配合使用,后面这个接口专注返回 SubscriberInfo对象,其默认的实现也比较简单,基本上就是指定完整的注册方法信息(SubscriberMethodInfo)即可

public class SubscriberMethodInfo {
    final String methodName;
    final ThreadMode threadMode;
    final Class<?> eventType;
    final int priority;
    final boolean sticky;
}

public class SimpleSubscriberInfo implements SubscriberInfo {
    public SimpleSubscriberInfo(Class subscriberClass, boolean shouldCheckSuperclass,SubscriberMethodInfo[] methodInfos) {
        this.subscriberClass = subscriberClass;
        this.superSubscriberInfoClass = null;
        this.shouldCheckSuperclass = shouldCheckSuperclass;
        this.methodInfos = methodInfos;
    }
}

6. xxxPost 发送事件的辅助类

拿一个异步的例子看一下, 里面包含两个对象, 一个 queue 消息推送的队列,一个 eventBus 实例,消息推送,则是调用 enqueue() 方法, 先将监听者 + 消息塞入队列, 然后调用 eventBus的线程池实进行实现异步的消息推送

private final PendingPostQueue queue;
    private final EventBus eventBus;

    AsyncPoster(EventBus eventBus) {
        this.eventBus = eventBus;
        queue = new PendingPostQueue();
    }

    public void enqueue(Subscription subscription, Object event) {
        PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
        queue.enqueue(pendingPost);
        eventBus.getExecutorService().execute(this);
    }

    @Override
    public void run() {
        PendingPost pendingPost = queue.poll();
        if(pendingPost == null) {
            throw new IllegalStateException("No pending post available");
        }
        eventBus.invokeSubscriber(pendingPost);
    }

转载于:https://my.oschina.net/u/566591/blog/884681

### GreenRobot EventBus 使用指南 GreenRobot EventBus 是一种轻量级的消息传递框架,用于简化 Android 应用程序中的组件通信。它通过订阅者模式实现事件驱动架构,从而减少代码耦合并提高可维护性。 #### 1. 添加依赖 为了在项目中使用 GreenRobot EventBus,首先需要将其添加到 `build.gradle` 文件中: ```gradle dependencies { implementation 'org.greenrobot:eventbus:3.2.0' } ``` #### 2. 基本概念 EventBus 的核心功能基于发布-订阅模型。以下是几个重要术语及其作用: - **Event**: 被发布的对象,可以是任何 Java 类型。 - **Subscriber (订阅者)**: 接收 Event 并执行相应操作的方法。 - **Publisher (发布者)**: 发布 Event 的类或方法。 #### 3. 注册与注销 为了让某个类能够接收事件,必须先注册该实例到 EventBus 中。通常在 Activity 或 Fragment 的生命周期中完成此操作: ```java @Override protected void onStart() { super.onStart(); EventBus.getDefault().register(this); } @Override protected void onStop() { super.onStop(); EventBus.getDefault().unregister(this); } ``` 如果未正确注销可能导致内存泄漏[^3]。 #### 4. 定义事件 创建一个简单的 POJO 来表示要发送的数据: ```java public class MessageEvent { private String message; public MessageEvent(String message) { this.message = message; } public String getMessage() { return message; } } ``` #### 5. 订阅事件 定义带有特定签名的回调函数来处理接收到的事件。这些方法需标注为 `@Subscribe` 并指定线程模式: ```java @Subscribe(threadMode = ThreadMode.MAIN) public void onMessageEvent(MessageEvent event) { Log.d("EventBus", "Received message: " + event.getMessage()); } ``` 此处设置线程模式为主线程运行逻辑以便更新 UI 组件[^1]。 #### 6. 发送事件 可以通过调用以下静态方法之一触发事件传播过程: ```java // 同步方式 EventBus.getDefault().post(new MessageEvent("Hello World")); // 异步方式(仅限于后台线程) EventBus.getDefault().postSticky(new MessageEvent("Persistent Hello")); ``` 注意 sticky events 不会被自动清理除非显式移除它们或者重新启动应用。 #### 7. 处理混淆配置 当启用 ProGuard/R8 缩减工具时,应确保保留必要的元数据以免破坏反射机制工作正常运作。为此,在 proguard-rules.pro 文件里加入下面几行指令即可解决潜在问题[^3]: ``` -keepattributes *Annotation* -keepclassmembers class ** { @org.greenrobot.eventbus.Subscribe <methods>; } -keep enum org.greenrobot.eventbus.ThreadMode { *; } # 如果使用异步处理器,则还需要保持此类成员不变 -keepclassmembers class * extends org.greenrobot.eventbus.util.ThrowableFailureEvent { <init>(java.lang.Throwable); } ``` ### 总结 以上介绍了如何集成以及基本运用 GreenRobot 提供的 EventBus 工具包来进行跨模块间高效沟通协作。遵循上述指导原则可以帮助开发者快速上手并有效利用其强大特性提升开发效率的同时降低复杂度风险。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值