Eventbus3.0学习笔记
来源:互联网 发布:检测是否是字符串 js 编辑:程序博客网 时间:2024/05/16 06:57
EventBus3.0采用了注解来查找订阅事件的方法回调,用handler在切换事件传输线程,用反射来传递事件,这个版本中还加入了索引查找回调的方式,能大大提高运行效率。
1.0 基本使用
先在gradle中导入依赖。
compile 'org.greenrobot:eventbus:3.0.0'
然后可以在要订阅事件的界面中注册eventbus
EventBus.getDefault().register(this);
并且在界面上通过注解来订阅事件
@Subscribe(threadMode = ThreadMode.MAIN,sticky = false,priority = 2)public void getstickymsg(String msg) { Log.e("bbb",msg);}
可以看到它通过@subscribe注解来表示该方法为订阅事件的回调
threadMode表示回调的线程 包括:
ThreadMode: POSTING:发布事件的线程这是默认设置
ThreadMode: MAIN: UI线程
ThreadMode: BACKGROUND:后台线程
ThreadMode: ASYNC : 独立线程模式
sticky 则表示是否接收黏滞事件
priority表示该回调的优先度 数值越大则优先度越大 回调将优先被调用;
方法参数是订阅事件的类型String msg
在需要发布事件的地方发布事件即可
EventBus.getDefault().post("普通消息");
当然你也可以自定义事件类型
public class Event1 { private String msg; public Event1(String msg) { this.msg = msg; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; }}
最后要在页面销毁时注销EventBus
@Overrideprotected void onDestroy() { EventBus.getDefault().unregister(this); super.onDestroy();}
1.1 黏滞事件发布
在注解回调方法时sticky表示是否接收黏滞事件
这可以通过
EventBus.getDefault().postSticky("黏滞消息")
来发布黏滞事件
首先这种事件发布后已注册并订阅了该种类型的事件的回调方法无论是否接收黏滞事件都会被调用,而还没有注册的类中订阅了该种类型事件的回调方法则会在类注册后接受到该黏滞事件,标记为不接收的则不会接收到。
这种事件的发布方式可以用来更新尚未打开的界面上的ui,此外多次发布一个类型的黏滞事件 当订阅了该事件的类注册时,回调方法只会收到最后一次发布的该类型黏滞事件。
1.2 加入索引提高运行效率
首先要在项目的build.gradle中加入依赖
dependencies { ............................. classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files}
并在dependencies中加入
apt 'org.greenrobot:eventbus-annotation-processor:3.0.1'
之后进行下编译
完成后我们发现在\ProjectName\app\build\generated\source\apt\PakageName\目录下就可以发现自动生成的订阅方法索引类。
public class Index1 implements SubscriberInfoIndex { private static final Map<Class<?>, SubscriberInfo> SUBSCRIBER_INDEX; static { SUBSCRIBER_INDEX = new HashMap<Class<?>, SubscriberInfo>(); putIndex(new SimpleSubscriberInfo(com.mycroft.eventbusdemo.Main2Activity.class, true, new SubscriberMethodInfo[] { new SubscriberMethodInfo("onString1", String.class, ThreadMode.MAIN), new SubscriberMethodInfo("onString2", String.class, ThreadMode.MAIN, 1, true), new SubscriberMethodInfo("getSticky", com.mycroft.eventbusdemo.event.Event1.class, ThreadMode.MAIN, 0, true), new SubscriberMethodInfo("getSticky1", com.mycroft.eventbusdemo.event.Event2.class, ThreadMode.MAIN, 0, true), })); putIndex(new SimpleSubscriberInfo(com.mycroft.eventbusdemo.MainActivity.class, true, new SubscriberMethodInfo[] { new SubscriberMethodInfo("getstickymsg", String.class, ThreadMode.MAIN, 2, true), new SubscriberMethodInfo("getstickymsg1", String.class, ThreadMode.MAIN, 3, false), })); } private static void putIndex(SubscriberInfo info) { SUBSCRIBER_INDEX.put(info.getSubscriberClass(), info); } @Override public SubscriberInfo getSubscriberInfo(Class<?> subscriberClass) { SubscriberInfo info = SUBSCRIBER_INDEX.get(subscriberClass); if (info != null) { return info; } else { return null; } }
这里面已经将项目中的所有订阅方法封装好了,接下来只要将其加入到单例EventBus中
EventBus.builder().addIndex(new Index1()).installDefaultEventBus();
就能够提高EventBus查找订阅方法的效率了。
2.0源码解析
作为最主要的类EventBus,我们从这个类开始,其中的一些变量有比较重要的作用
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(); }};
首先来分析这4个变量
subscriptionsByEventType这张map表以 发布的事件的类型为键 将订阅该类型事件的所有回调方法
封装成了
final class Subscription { final Object subscriber; final SubscriberMethod subscriberMethod; /** * Becomes false as soon as {@link EventBus#unregister(Object)} is called, which is checked by queued event delivery * {@link EventBus#invokeSubscriber(PendingPost)} to prevent race conditions. */ volatile boolean active; Subscription(Object subscriber, SubscriberMethod subscriberMethod) { this.subscriber = subscriber; this.subscriberMethod = subscriberMethod; active = true; } @Override public boolean equals(Object other) { if (other instanceof Subscription) { Subscription otherSubscription = (Subscription) other; return subscriber == otherSubscription.subscriber && subscriberMethod.equals(otherSubscription.subscriberMethod); } else { return false; } } @Override public int hashCode() { return subscriber.hashCode() + subscriberMethod.methodString.hashCode(); }
subscriber为订阅者对象
subscriberMethod则具体封装了方法
public class SubscriberMethod final Method method; final ThreadMode threadMode; final Class<?> eventType; final int priority; final boolean sticky; /** Used for efficient comparison */ String methodString;
可以看到其中包含订阅方法的信息
再来看 typesBySubscriber这张map,它以订阅者为键,以所有该订阅者订阅的事件类型组成的list为值 可以想到在注销EventBus的时候从这张map中可以快速找到当前订阅者所订阅的事件类型从而快速的从 subscriptionsByEventType中删除当前的订阅者的方法信息
stickyEvents这张map则用黏滞事件的类型为键 以事件的实例对象为值,这张map用以存储待接收的黏滞事件这种的存储方式也让我们理解了为什么只能接收最后一次被发送的黏滞事件。
currentPostingThreadState这个ThreadLocal则存储了当前线程的推送信息状态
状态被封装成了
final static class PostingThreadState { final List<Object> eventQueue = new ArrayList<Object>(); boolean isPosting; boolean isMainThread; Subscription subscription; Object event; boolean canceled;}
然后我们从EventBus的register()方法开始
public void register(Object subscriber) { Class<?> subscriberClass = subscriber.getClass(); List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass); synchronized (this) { for (SubscriberMethod subscriberMethod : subscriberMethods) { subscribe(subscriber, subscriberMethod); } }}
可以看到一旦订阅者传入就获得他的类型然后用SubscriberMethodFinder类去查找这个订阅类中所有的订阅方法
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); } if (subscriberMethods.isEmpty()) { throw new EventBusException("Subscriber " + subscriberClass + " and its super classes have no public methods with the @Subscribe annotation"); } else { METHOD_CACHE.put(subscriberClass, subscriberMethods); return subscriberMethods; }}
查询的方式有两种如果不使用加速索引的话就用反射遍历方法来获取注解封装成方法信息
private void findUsingReflectionInSingleClass(FindState findState) { Method[] methods; try { // This is faster than getMethods, especially when subscribers are fat classes like Activities methods = findState.clazz.getDeclaredMethods(); } catch (Throwable th) { // Workaround for java.lang.NoClassDefFoundError, see https://github.com/greenrobot/EventBus/issues/149 methods = findState.clazz.getMethods(); findState.skipSuperClasses = true; } 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 subscribeAnnotation = method.getAnnotation(Subscribe.class); if (subscribeAnnotation != null) { Class<?> eventType = parameterTypes[0]; if (findState.checkAdd(method, eventType)) { ThreadMode threadMode = subscribeAnnotation.threadMode(); findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode, subscribeAnnotation.priority(), subscribeAnnotation.sticky())); } } } else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) { String methodName = method.getDeclaringClass().getName() + "." + method.getName(); throw new EventBusException("@Subscribe method " + methodName + "must have exactly 1 parameter but has " + parameterTypes.length); } } else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) { String methodName = method.getDeclaringClass().getName() + "." + method.getName(); throw new EventBusException(methodName + " is a illegal @Subscribe method: must be public, non-static, and non-abstract"); } }}
如果使用索引加速就直接用编译时生成的订阅方法索引来获取信息
private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) { FindState findState = prepareFindState(); findState.initForSubscriber(subscriberClass); while (findState.clazz != null) { findState.subscriberInfo = getSubscriberInfo(findState); if (findState.subscriberInfo != null) { SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods(); for (SubscriberMethod subscriberMethod : array) { if (findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)) { findState.subscriberMethods.add(subscriberMethod); } } } else { findUsingReflectionInSingleClass(findState); } findState.moveToSuperclass(); } return getMethodsAndRelease(findState);}
由于运行时不再需要去遍历方法获取信息而直接采用编译时就查找好的信息这就大大提高了其效率。
而EventBus在获取了所有该订阅者的订阅方法信息后就会遍历调用subscribe(Object subscriber, SubscriberMethod 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); } else { if (subscriptions.contains(newSubscription)) { throw new EventBusException("Subscriber " + subscriber.getClass() + " 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) { if (eventInheritance) { // Existing sticky events of all subclasses of eventType have to be considered. // Note: Iterating over all events may be inefficient with lots of sticky events, // thus data structure should be changed to allow a more efficient lookup // (e.g. an additional map storing sub classes of super classes: Class -> List<Class>). Set<Map.Entry<Class<?>, Object>> entries = stickyEvents.entrySet(); for (Map.Entry<Class<?>, Object> entry : entries) { Class<?> candidateEventType = entry.getKey(); if (eventType.isAssignableFrom(candidateEventType)) { Object stickyEvent = entry.getValue(); checkPostStickyEventToSubscription(newSubscription, stickyEvent); } } } else { Object stickyEvent = stickyEvents.get(eventType); checkPostStickyEventToSubscription(newSubscription, stickyEvent); } }}
这个方法就主要是将传进来的订阅方法信息添加到几张map中去以便在发布事件时可以清楚的找到接收目标。而如果该回调方法接受黏滞事件,那么黏滞事件也在这个时候发送
Object stickyEvent = stickyEvents.get(eventType); checkPostStickyEventToSubscription(newSubscription, stickyEvent);
接下来我们看事件发布部分,首先是黏滞事件
public void postSticky(Object event) { synchronized (stickyEvents) { stickyEvents.put(event.getClass(), event); } // Should be posted after it is putted, in case the subscriber wants to remove immediately post(event);}
发送黏滞事件的过程只是将事件添加进了stickyEvents中等待发送然后再用普通事件的方式post
public void post(Object event) { PostingThreadState postingState = currentPostingThreadState.get(); List<Object> eventQueue = postingState.eventQueue; eventQueue.add(event); if (!postingState.isPosting) { postingState.isMainThread = Looper.getMainLooper() == Looper.myLooper(); postingState.isPosting = true; if (postingState.canceled) { throw new EventBusException("Internal error. Abort state was not reset"); } try { while (!eventQueue.isEmpty()) { postSingleEvent(eventQueue.remove(0), postingState); } } finally { postingState.isPosting = false; postingState.isMainThread = false; } }}
post方法中首先获得本线程中的发送状态,从而获得本线程中发送事件的队列然后循环发送
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error { Class<?> eventClass = event.getClass(); boolean subscriptionFound = false; if (eventInheritance) { List<Class<?>> eventTypes = lookupAllEventTypes(eventClass); int countTypes = eventTypes.size(); for (int h = 0; h < countTypes; h++) { Class<?> clazz = eventTypes.get(h); 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 && eventClass != SubscriberExceptionEvent.class) { post(new NoSubscriberEvent(this, event)); } }}
最终通过下面这个方法进行发送
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 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); }}
可以看发送事件时会先判断接受目标是否在同一个线程中,如果在同一个线程中就直接通过反射调用
void invokeSubscriber(Subscription subscription, Object event) { try { subscription.subscriberMethod.method.invoke(subscription.subscriber, event); } catch (InvocationTargetException e) { handleSubscriberException(subscription, event, e.getCause()); } catch (IllegalAccessException e) { throw new IllegalStateException("Unexpected exception", e); }}
而如果不在同一个线程中就分别使用不同的线程切换类来切换线程
如mainThreadPoster中
void enqueue(Subscription subscription, Object event) { PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event); synchronized (this) { queue.enqueue(pendingPost); if (!handlerActive) { handlerActive = true; if (!sendMessage(obtainMessage())) { throw new EventBusException("Could not send handler message"); } } }}@Overridepublic void handleMessage(Message msg) { boolean rescheduled = false; try { long started = SystemClock.uptimeMillis(); while (true) { PendingPost pendingPost = queue.poll(); if (pendingPost == null) { synchronized (this) { // Check again, this time in synchronized pendingPost = queue.poll(); if (pendingPost == null) { handlerActive = false; return; } } } eventBus.invokeSubscriber(pendingPost); long timeInMethod = SystemClock.uptimeMillis() - started; if (timeInMethod >= maxMillisInsideHandleMessage) { if (!sendMessage(obtainMessage())) { throw new EventBusException("Could not send handler message"); } rescheduled = true; return; } } } finally { handlerActive = rescheduled; }}
这里面用了handler,将事件信息存进PendingPost中,再在主线程里拿出来并进行反射发送。
参考文章:这里写链接内容
- Eventbus3.0学习笔记
- EventBus3.0学习小结
- EventBus3.0学习资料
- 关于EventBus3.0(笔记)
- EventBus3.0源码学习(一)
- Android事件总线 EventBus3.0用法学习
- Eventbus3.0
- EventBus3.0
- EventBus3.0
- EventBus3.0
- 学习EventBus3的使用
- EventBus3.0 组件通信框架源码学习总结
- EventBus3.0使用总结
- EventBus3.0 总结
- EventBus3.0使用总结
- EventBus3.0使用详解
- EventBus3.0使用
- EventBus3.0的使用
- llinux根文件系统/etc/passwd文件详解
- 常见web漏洞攻防杂谈
- Hadoop集群搭建过程中遇到的那些事
- Android探索之旅(第四篇) 面试总结大全
- 第四周项目1建立单链表
- Eventbus3.0学习笔记
- JS对象继承的一些方法
- 浅拷贝问题出现原因剖析
- 二、oracle sql*plus常用命令
- 二维码的生成细节和原理
- day14 python logging复习
- Fresco图片框架内部实现原理探索
- win7系统下安装UbuntuKylin16.04双系统成功经验
- linux根文件系统/etc/group文件详解