2019独角兽企业重金招聘Python工程师标准>>>

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() {@Overridepublic 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 {@Testpublic void dummy() {}}@Rulepublic 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;@Beforepublic void setUp() throws Exception {eventBus = new EventBus();}@Test@UiThreadTestpublic void testRegisterAndPost() {// Use an activity to test real life performanceTestActivity 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);}// 无订阅者@Testpublic void testPostWithoutSubscriber() {eventBus.post("Hello");}@Testpublic void testUnregisterWithoutRegister() {// Results in a warning without throwingeventBus.unregister(this);}// This will throw "out of memory" if subscribers are leaked@Testpublic 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);}}@Testpublic void testRegisterTwice() {eventBus.register(this);try {eventBus.register(this);fail("Did not throw");} catch (RuntimeException expected) {// OK}}@Testpublic void testIsRegistered() {assertFalse(eventBus.isRegistered(this));eventBus.register(this);assertTrue(eventBus.isRegistered(this));eventBus.unregister(this);assertFalse(eventBus.isRegistered(this));}@Testpublic 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);}@Testpublic 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);}@Testpublic void testMultipleSubscribeMethodsForEvent() {eventBus.register(this);MyEvent event = new MyEvent();eventBus.post(event);assertEquals(1, countMyEvent);assertEquals(1, countMyEvent2);}@Testpublic void testPostAfterUnregister() {eventBus.register(this);eventBus.unregister(this);eventBus.post("Hello");assertNull(lastStringEvent);}@Testpublic void testRegisterAndPostTwoTypes() {eventBus.register(this);eventBus.post(42);eventBus.post("Hello");assertEquals(1, countIntEvent);assertEquals(1, countStringEvent);assertEquals(42, lastIntEvent);assertEquals("Hello", lastStringEvent);}@Testpublic void testRegisterUnregisterAndPostTwoTypes() {eventBus.register(this);eventBus.unregister(this);eventBus.post(42);eventBus.post("Hello");assertEquals(0, countIntEvent);assertEquals(0, lastIntEvent);assertEquals(0, countStringEvent);}@Testpublic void testPostOnDifferentEventBus() {eventBus.register(this);new EventBus().post("Hello");assertEquals(0, countStringEvent);}@Testpublic 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);}@Testpublic void testHasSubscriberForEvent() {assertFalse(eventBus.hasSubscriberForEvent(String.class));eventBus.register(this);assertTrue(eventBus.hasSubscriberForEvent(String.class));eventBus.unregister(this);assertFalse(eventBus.hasSubscriberForEvent(String.class));}@Testpublic 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));}@Testpublic 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));}@Subscribepublic void onEvent(String event) {lastStringEvent = event;countStringEvent++;}@Subscribepublic void onEvent(Integer event) {lastIntEvent = event;countIntEvent++;}@Subscribepublic void onEvent(MyEvent event) {countMyEvent++;}@Subscribepublic void onEvent2(MyEvent event) {countMyEvent2++;}@Subscribepublic void onEvent(MyEventExtended event) {countMyEventExtended++;}public static class TestActivity extends Activity {public String lastStringEvent;@Subscribepublic void onEvent(String event) {lastStringEvent = event;}}public static class CharSequenceSubscriber {@Subscribepublic void onEvent(CharSequence event) {}}public static class ObjectSubscriber {@Subscribepublic void onEvent(Object event) {}}public class MyEvent {}public class MyEventExtended extends MyEvent {}public class RepostInteger {public int lastEvent;public int countEvent;@Subscribepublic 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 unchangedassertEquals(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>() {@Overrideprotected 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);}@Overridepublic 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源码学习(四)相关推荐

  1. dubbo源码学习(四):暴露服务的过程

    dubbo采用的nio异步的通信,通信协议默认为 netty,当然也可以选择 mina,grizzy.在服务端(provider)在启动时主要是开启netty监听,在zookeeper上注册服务节点, ...

  2. mutations vuex 调用_Vuex源码学习(六)action和mutation如何被调用的(前置准备篇)...

    前言 Vuex源码系列不知不觉已经到了第六篇.前置的五篇分别如下: 长篇连载:Vuex源码学习(一)功能梳理 长篇连载:Vuex源码学习(二)脉络梳理 作为一个Web前端,你知道Vuex的instal ...

  3. 第十四课 k8s源码学习和二次开发原理篇-调度器原理

    第十四课 k8s源码学习和二次开发原理篇-调度器原理 tags: k8s 源码学习 categories: 源码学习 二次开发 文章目录 第十四课 k8s源码学习和二次开发原理篇-调度器原理 第一节 ...

  4. postgresql源码学习(57)—— pg中的四种动态库加载方法

    一. 基础知识 1. 什么是库 库其实就是一些通用代码,可以在程序中重复使用,比如一些数学函数,可以不需要自己编写,直接调用相关函数即可实现,避免重复造轮子. 在linux中,支持两种类型的库: 1. ...

  5. Spring源码学习(四) | @Configuration的cglib动态代理

    文章目录 前言 例子 @Configuration :full or lite 设置 full or lite Cglib生成代理类AppConfig Where is it generated Ho ...

  6. 第四课 k8s源码学习和二次开发-DeltaFIFO和Indexer原理学习

    第四课 k8s源码学习和二次开发-DeltaFIFO和Indexer原理学习 tags: k8s 源码学习 categories: 源码学习 二次开发 文章目录 第四课 k8s源码学习和二次开发-De ...

  7. mongo源码学习(四)服务入口点ServiceEntryPoint

    在上一篇博客mongo源码学习(三)请求接收传输层中,稍微分析了一下TransportLayer的作用,这篇来看下ServiceEntryPoint是怎么做的. 首先ServiceEntryPoint ...

  8. EventBus源码分析

    简介 前面我学习了如何使用EventBus,还有了解了EventBus的特性,那么接下来我们一起来学习EventBus的源码,查看EventBus的源码,看看EventBus给我们带来什么惊喜以及编程 ...

  9. JDK源码学习笔记——Integer

    一.类定义 public final class Integer extends Number implements Comparable<Integer> 二.属性 private fi ...

  10. EventBus源码解析

    前面一篇文章讲解了EventBus的使用,但是作为开发人员,不能只停留在仅仅会用的层面上,我们还需要弄清楚它的内部实现原理.所以本篇博文将分析EventBus的源码,看看究竟它是如何实现"发 ...

最新文章

  1. ESP8266-SDK的硬件定时器
  2. 软件调试学习笔记(七)—— 单步步入单步步过
  3. 完全背包问题从简单到复杂
  4. 视频回顾丨带你逛腾讯全球数字生态大会「腾讯技术工程」展区
  5. 数据不足,如何进行迁移学习?
  6. 无法打开caffe.pb.h, no such file or directory错误
  7. redis复制原理和应用
  8. CentOS下搭建Git服务器Gitosis
  9. HDU 1068 Girls and Boys(最大独立集合 = 顶点数 - 最大匹配数)
  10. 微信群 保存到通讯录
  11. C++ for const 是什么意思
  12. 4-7终极无敌DP乱秀
  13. 新学期个人作息时间安排
  14. 八皇后问题----Java实现
  15. Android源码开发之蓝牙通知振动
  16. Greenplum在HTAP场景下的优化和应用
  17. Unity 灯光系统详解
  18. 【面经、笔经攒人品】中国银行、浦发银行
  19. winform模拟登陆网页_用c#模拟手机app向网站登录,如何写?
  20. 【按键精灵篇】如何做一个自动打开APP进入注册页面自动输入自己手机号

热门文章

  1. opencv cv2.copyMakeBorder()函数详解
  2. php 安装scws,SCWS分词扩展在windows下的安装方法
  3. 大一计算机期末考试高数试卷,大一高数期末考试试题
  4. python快乐数,快乐数 - SegmentFault 思否
  5. springboot日志写入mysql_springboot运用logback将日志写入数据库
  6. huffman编码的程序流程图_基于哈夫曼编码的压缩解压程序(C 语言)
  7. sqlserver两种分页方法比较
  8. activity启动模式之standard
  9. linux pptp服务器安装
  10. 6、Java包的命名与划分