内容简介:EventBus是为为能接收消息,订阅者需要通过
一、简介
1.1 特性
EventBus是为 Android 而设的中心化 publish/subscribe (发布/订阅) 事件系统。消息通过 post(Object) 把消息提交到总线,总线把消息分发给订阅者。当然,该订阅者需拥有匹配消息类型的处理方法。
为能接收消息,订阅者需要通过 register(Object) 把自己注册到总线上。一旦成功注册,订阅者将一直接收对应消息,直到订阅者通过 unregister(Object) 注销监听。
处理消息的方法需满足以下条件:
- 用 Subscribe 关键字进行注解;
- 方法可见性为 public ;
- 方法返回值为 void ;
- 仅含有一个参数,为接收的事件的类型;
1.2 优点
- 简化不同组件间的通讯
- 对事件发送者和接收者两者进行解耦
- 与Activities、 Fragments、和 background threads 运行得很好
- 避免复杂、易错的依赖和生命周期问题
- 令实现代码更简洁
- 运行速度快
- 库体积小 (约50KB)
- 已经过累计 100,000,000+ 安装量的应用验证
- 有消息分发线程、订阅者优先级等的高级特性
二、用法
2.1 订阅者
订阅者需要在合适的生命周期,把自己注册到消息总线上,以便接收关心的事件。同时,由于事件的基本接收单位是方法,所以需要给接收事件的方法添加注解,以便 EventBus 把事件发送到该方法上。
接收者方法需要遵循以下规则:
- 使用 EventBus 的注册修饰方法;
- 方法不能为 private ,才能让 EventBus 获取该方法;
- 方法必须只有一个参数,且参数类型就是所关心事件的类型;
class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) if (EventBus.getDefault().isRegistered(this)) { EventBus.getDefault().register(this) } } // 注册类必须包含至少一个接收事件的方法 @Subscribe(threadMode = ThreadMode.MAIN, sticky = false, priority = 0) fun onEventReceived(event: UserEvent) { val name = event.name val age = event.age Log.i(TAG, "Event received, Name: $name, Age: $age.") } override fun onDestroy() { super.onDestroy() EventBus.getDefault().unregister(this) } }
除了把实例注册到 EventBus ,如果接收者类对事件不再关心,也需要在合适时间点移除注册。每个接收者类只需向 EventBus 注册一次。为避免多次注册,可以向上述代码一样,在注册前检查是否已注册。
2.2 发布者
对事件发布者来说,工作就比较简单了。只需要构建目标事件,把数据或负载内容构建到事件中发出即可。如果事件只是为了发出通知,事件实现类甚至可以不带任何数据成员。
fun postEvent() { val user = UserEvent("Mike", 24) EventBus.getDefault().post(user) }
2.3 事件消息体
这是示例的消息体,消息体重包含用户的名字和年龄
class UserEvent(val name: String, val age: Int)
如果消息只是为了发出简单通知,事件消息体可以不含任何数据成员。例如在 Kotlin 中:
class Notification
三、初始化
3.1 单例
整个 EventBus 通过以下方法创建单例。所有在此单例发送的事件,只对注册在单例里的订阅者有效。为了所有事件能在同一个 EventBus 内流动,一定要从此方法获取 EventBus 实例。
public class EventBus { // 变量使用volatile修饰 static volatile EventBus defaultInstance; // 同一进程内有效,传统的双重检验锁 public static EventBus getDefault() { if (defaultInstance == null) { synchronized (EventBus.class) { if (defaultInstance == null) { defaultInstance = new EventBus(); } } } return defaultInstance; } }
调用 getDefault() 方法时,以下两个静态常量也获得初始化:
EventBusBuilder类将在后续文章进行详细介绍
private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();
事件类型缓存,实例为HashMap<>
private static final Map<Class<?>, List<Class<?>>> eventTypesCache = new HashMap<>();
3.2 基础构造
单例的初始化调用此构造方法,然后方法内又调用自身的另一个构造方法:
public EventBus() { this(DEFAULT_BUILDER); }
3.3 深入构造
构造过程对以下数据成员进行赋值
// 按照事件类型分类订阅,事件类型预期对应的订阅类 private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType; // 通过订阅者类型分类 private final Map<Object, List<Class<?>>> typesBySubscriber; // 保存粘性事件 private final Map<Class<?>, Object> stickyEvents; private final MainThreadSupport mainThreadSupport; // 主线程发布器 private final Poster mainThreadPoster; // 后台线程发布器 private final BackgroundPoster backgroundPoster; // 异步发布器 private final AsyncPoster asyncPoster; // 订阅者方法查找器 private final SubscriberMethodFinder subscriberMethodFinder; // ExecutorService 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; // 索引总计 private final int indexCount; // 日志类 private final Logger logger;
事件负责构造工作的是这个构造方法
EventBus(EventBusBuilder builder) { // 从EventBusBuilder获取Logger logger = builder.getLogger(); // 初始化容器 subscriptionsByEventType = new HashMap<>(); typesBySubscriber = new HashMap<>(); stickyEvents = new ConcurrentHashMap<>(); // 从EventBusBuilder获取MainThreadSupport mainThreadSupport = builder.getMainThreadSupport(); mainThreadPoster = mainThreadSupport != null ? mainThreadSupport.createPoster(this) : null; // 构建BackgroundPoster backgroundPoster = new BackgroundPoster(this); // 构建AsyncPoster asyncPoster = new AsyncPoster(this); // 获取数量 indexCount = builder.subscriberInfoIndexes != null ? builder.subscriberInfoIndexes.size() : 0; // 初始化订阅者方法查找器 subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes, builder.strictMethodVerification, builder.ignoreGeneratedIndex); // 初始化Boolean logSubscriberExceptions = builder.logSubscriberExceptions; logNoSubscriberMessages = builder.logNoSubscriberMessages; sendSubscriberExceptionEvent = builder.sendSubscriberExceptionEvent; sendNoSubscriberEvent = builder.sendNoSubscriberEvent; throwSubscriberException = builder.throwSubscriberException; eventInheritance = builder.eventInheritance; // ExecutorService executorService = builder.executorService; }
3.4 PostingThreadState
在单例初始化过程还初始化了以下 ThreadLocal 实例
private final ThreadLocal<PostingThreadState> currentPostingThreadState = new ThreadLocal<PostingThreadState>() { @Override protected PostingThreadState initialValue() { return new PostingThreadState(); } };
PostingThreadState是变量的封装,用在 ThreadLocal 中,以便快速设置、获取多个变量。
final static class PostingThreadState { // 事件队列 final List<Object> eventQueue = new ArrayList<>(); // 消息是否在投递中 boolean isPosting; // 是否在主线程 boolean isMainThread; // 订阅记录 Subscription subscription; // 事件 Object event; // 是否已被取消投递 boolean canceled; }
四、注册事件
4.1 register
所有订阅者通过此方法向 EventBus 注册自己,以便在注册后收取所关心的事件。注销订阅则通过方法 unregister(Object) ,这样观察者就能在不再关心事件的时候取消订阅。
public void register(Object subscriber) { // 获取订阅者的类型 Class<?> subscriberClass = subscriber.getClass(); // 从订阅者方法中找出该类接收事件的方法 List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass); synchronized (this) { // 逐个注册订阅者中的订阅方法 for (SubscriberMethod subscriberMethod : subscriberMethods) { subscribe(subscriber, subscriberMethod); } } }
4.2 subscribe
此方法必须在同步块中调用,主要是把订阅方法按照订阅事件分类
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<>(); // 把事件类型或对应Subscription放入 subscriptionsByEventType.put(eventType, subscriptions); } else { if (subscriptions.contains(newSubscription)) { // 多次register(this)抛出此异常 throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event " + eventType); } } // 获取同一事件上subscriptions数量 int size = subscriptions.size(); for (int i = 0; i <= size; i++) { // 根据方法注解设置的priority值,倒序插入新记录newSubscription // priority值越大越优先接收消息,默认值为0 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); // 订阅者方法的sticky为true,把历史事件发送给此订阅者 if (subscriberMethod.sticky) { // eventInheritance为true,表示订阅subscriberMethod子类消息的订阅者也接收粘性事件 if (eventInheritance) { // 消息类型所有子类的已存在粘性事件,都需要受到关注 // 有非常多粘性事件时进行事件遍历是低效的,数据结构需在遍历上更高效 // 例如:使用额外的map存储父类的子类:Class -> List<Class> Set<Map.Entry<Class<?>, Object>> entries = stickyEvents.entrySet(); for (Map.Entry<Class<?>, Object> entry : entries) { // 候选事件类型 Class<?> candidateEventType = entry.getKey(); // 检查eventType是否为candidateEventType的父类或同类 if (eventType.isAssignableFrom(candidateEventType)) { Object stickyEvent = entry.getValue(); checkPostStickyEventToSubscription(newSubscription, stickyEvent); } } } else { // 仅发送指定类型订阅事件 Object stickyEvent = stickyEvents.get(eventType); checkPostStickyEventToSubscription(newSubscription, stickyEvent); } } }
4.3 checkPostStickyEventToSubscription
如果订阅者尝试终止该事件会失败,因为事件没有通过投递状态进行跟踪
private void checkPostStickyEventToSubscription(Subscription newSubscription, Object stickyEvent) { if (stickyEvent != null) { postToSubscription(newSubscription, stickyEvent, isMainThread()); } }
4.4 postToSubscription
ThreadMode中几种类别的主要含义在后续文章详解
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 { // 处在其他线程,向主线程Handler发送消息 mainThreadPoster.enqueue(subscription, event); } break; case MAIN_ORDERED: if (mainThreadPoster != null) { mainThreadPoster.enqueue(subscription, event); } else { // temporary: technically not correct as poster not decoupled from subscriber invokeSubscriber(subscription, event); } break; case BACKGROUND: if (isMainThread) { backgroundPoster.enqueue(subscription, event); } else { invokeSubscriber(subscription, event); } break; case ASYNC: asyncPoster.enqueue(subscription, event); break; // 传入未知threadMode,引起异常 default: throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode); } }
五、注销订阅
5.1 unregister
从所有事件类中注销指定订阅者
public synchronized void unregister(Object subscriber) { // 订阅者订阅的事件类型 List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber); if (subscribedTypes != null) { // 逐个注销订阅者订阅的事件 for (Class<?> eventType : subscribedTypes) { unsubscribeByEventType(subscriber, eventType); } // 从typesBySubscriber移除subscriber typesBySubscriber.remove(subscriber); } else { // 此订阅者之前未曾注册过,却进行注销操作 logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass()); } }
5.2 unsubscribeByEventType
只更新 subscriptionsByEventType ,而不是 typesBySubscriber , typesBySubscriber 由调用者更新。
private void unsubscribeByEventType(Object subscriber, Class<?> eventType) { // 根据事件类型获取订阅记录 List<Subscription> subscriptions = subscriptionsByEventType.get(eventType); if (subscriptions != null) { // 订阅记录数量 int size = subscriptions.size(); for (int i = 0; i < size; i++) { // 查找并移除所有相关订阅记录 Subscription subscription = subscriptions.get(i); if (subscription.subscriber == subscriber) { // subscription设置为不活动 subscription.active = false; // 从列表中移除 subscriptions.remove(i); i--; size--; } } } }
以上所述就是小编给大家介绍的《EventBus源码剖析(1) — 注册与注销订阅》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- PHP 实例化对象注销
- Vue+Express实现登录,注销
- 腾讯QQ:注销功能预计下周正式发布
- 腾讯QQ号注销真的来了,方法还超简单
- 排查 Dubbo 接口重复注销问题,我发现了一个巧妙的设计
- 「小程序JAVA实战」小程序我的个人信息-注销功能(40)
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
亚马逊跨境电商运营宝典
老魏 / 电子工业出版社 / 2018-6 / 69
《亚马逊跨境电商运营宝典》详细讲解亚马逊的平台知识和运营技巧,内容围绕亚马逊卖家的日常工作,系统讲解亚马逊账号注册、后台操作、选品、产品发布、Listing 优化、站内广告、FBA 发货、VAT 税务等内容,并且通过大量的案例分析,用生动翔实的案例为读者传递运营中必备的操作技巧和运营方法。 《亚马逊跨境电商运营宝典》内容针对性强,讲解的知识、技巧和方法都充分考虑到易学、易懂、易操作、易落地执......一起来看看 《亚马逊跨境电商运营宝典》 这本书的介绍吧!