转载请说明地址: http://blog.csdn.net/it_peng/article/details/51581426
前言:
还记得我曾经在知乎上面提问:说怎么看源码,什么样的姿势比较好?
但是也很少人回答我。但是也没有关系。原来我不知道怎么回答,现在
估计能回答一点点了。
姿势_1:
就拿EventBus框架来说吧!我们首先应该会用是吧.
基本功能:
注册: EventBus.getDefault().register(this);
反注册: EventBus.getDefault().unregister(this);
发送: EventBus.getDefault().post(Object xxx);
接收:
@Subscribe
public void onEventMainThread(Object xxx) {
//逻辑处理。
}
这里特别提醒一点:Evenbus3 不需要必须这onEvent开头的方法。(至于为什么我们下面解析)
姿势二:
1.先看别人解析。
2.好奇心。
3.敢于探索。
4.敢于发出疑问
为什么 先看别人解析啦? 其实我们自己分析一般都不是特别到位了,这个时候我们应该找一找有没有大牛们分析过的文章看一下,先知道大概的思路是怎么样的,一边看一遍想。然后自己看多看几遍。然后一点要多动手。
————————————不多说了,开始分析了————————————————-
这里我们先提出几个问题带着问题思考:
1.注册的时候发生了什么?
2.为什么我只需要 @Subscribe 注解声明一下就可以了,不用规范方法名称(我说的规范指的是onEvent这样规则命名方式)
3.post的时候怎么就可以发送到我创建的相同参数的方法,并且执行啦?
4.为什么说直接发送到主线程不能运行太耗时的方法?
5.为什么听别人说eventbus3的运行效率貌似比原来的快了许多倍?
6. Eventbus 有哪些很好的设计?
1.注册的时候发生了什么?
/**
* 注册给定的订阅服务器接收事件。用户必须调用{@链接#注销(对象)}一旦
* 不再对接收事件感兴趣。
* Registers the given subscriber to receive events. Subscribers must call {@link #unregister(Object)} once they
* are no longer interested in receiving events.
* <p>
* <p>
* 用户有事件处理方法,必须注明“{ @链接订阅}。
* {“链接”订阅}注释还允许配置像{ @链接
* threadmode }和优先级。
* Subscribers have event handling methods that must be annotated by {@link Subscribe}.
* The {@link Subscribe} annotation also allows configuration like {@link
* ThreadMode} and priority.
*/
public void register(Object subscriber) {
//首先获得订阅者的class对象。
Class<?> subscriberClass = subscriber.getClass();
//通过subscriberMethodFinder来找到订阅者订阅了那些事件
// ,返回一个SubscriberMethod对象的list,SubscriberMethod
// 里包含了这个方法的Method对象,以及将来响应订阅是在哪个线程的ThreadMode
//以及订阅的事件类型eventType,以及优先级priority,以及是否接收粘性sticky事件的boolean值.
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
//订阅。(定义数据存放规则)
subscribe(subscriber, subscriberMethod);
}
}
}
上面的注释先大概看一下就好了,
这里我们先看到 findSubscriberMethods 其实从字面的意思我们就可以知道 找到订阅者下的方法
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
//先从Method_cache取看是否有缓存,key:保存订阅类的类名,value:保存类中订阅的方法数据。
1. List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
if (subscriberMethods != null) {
return subscriberMethods;
}
// 是否忽视注解器生成的MyEventBusIndex类。 默认都是false.
if (ignoreGeneratedIndex) {
//利用反射来读取订阅类中的订阅方法信息。
2. subscriberMethods = findUsingReflection(subscriberClass);
} else {
//从注解器生成的MyEventBusIndex类中获得订阅类的订阅方法信息。
3. subscriberMethods = findUsingInfo(subscriberClass);
}
if (subscriberMethods.isEmpty()) {
throw new EventBusException("Subscriber " + subscriberClass
+ " and its super classes have no public methods with the @Subscribe annotation");
} else {
//保存Mehod_CACHE缓存。
METHOD_CACHE.put(subscriberClass, subscriberMethods);
return subscriberMethods;
}
}
我们主要看到 ignoreGeneratedIndex 其实默认都是false的。
不相信的话可以看到
EventBusBuilder—> ignoreGeneratedIndex 这个赋值的方法。
既然是false,那我们就看看
subscriberMethods = findUsingInfo(subscriberClass);
private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
1. FindState findState = prepareFindState(); // 复用。
2. findState.initForSubscriber(subscriberClass); //初始化
// 判断这个类是否为空。
while (findState.clazz != null) {
//获取该类订阅的所有方法了。
3. findState.subscriberInfo = getSubscriberInfo(findState);
//如果在加速索引没有找到值得话,那就反射吧。
4. 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);
}
我最喜欢一段代码就是 1. 2. 这两行代码了。下面我会解释为什么的。
/3. 这个只有开启了索引加速才会有实际意义。如果想知道的话可以看到 4.为什么听别人说eventbus3的运行效率貌似比原来的快了许多倍? 这一标题下
那么此时的 4. ==false。
我们就需要看到
findUsingReflectionInSingleClass 这个方法
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;
}
//遍历Method
for (Method method : methods) {
//获取方法的修饰符
int modifiers = method.getModifiers();
// 后面一部分 解读忽视的 修饰符。
if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
//获取参数类型。
Class<?>[] parameterTypes = method.getParameterTypes();
//保证必须只有一个事件参数。 //因为 post 方法就只能传递一个object。
if (parameterTypes.length == 1) {
//得到注解(看方法有没有这个标识) //3.0 貌似必须用注解。
Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
if (subscribeAnnotation != null) { // 如果有的话。
Class<?> eventType = parameterTypes[0]; //获取参数的类型
//校验是否添加该方法。
if (findState.checkAdd(method, eventType)) {
ThreadMode threadMode = subscribeAnnotation.threadMode();
//实例化SubscribeMethod对象并添加。
findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode,
subscribeAnnotation.priority(), subscribeAnnotation.sticky()));
}
}
// strictMethodVerification //严格查询Method的名称吗? && 判断是否有这个注解。
} 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");
}
}
}
step1:
获取该类的所以方法
methods = findState.clazz.getDeclaredMethods();
methods = findState.clazz.getMethods();
为什么会有两个方法的?并且为什么注释说第一个比较快啦?
第一种只获取本类的所有方法
第二种会获取父类的已经子类的所有方法。
效果图片:
step 2:
对每一个方法进行遍历筛选符合的方法。
1. 判断一:
先对方法的修饰符进行筛选:
规则是:必须是public 并且不能是 Modifier.ABSTRACT | Modifier.STATIC | BRIDGE | SYNTHETIC;
2. 判断二
对方法的参数个数进行筛选:
因为post的参数就只是一个Object类型。所以两个以上参数肯定是不行的
3. 判断三
方法有没有 @Subscribe 的注解
4. 判断四
checkAdd是为了避免在父类中找到的方法是被子类重写的,此时应该保证回调时执行子类的方法。
进行层层的判断,一遍遍的筛选成功的话:
添加进入: 该类的一个方法集合中去。
如果判断不合格的话,就不解释了。直接看源码错误信息。
还有一些过程就是一异常处理,以及资源移除和重复利用。
接下来看到 register方法中的
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
//订阅。(定义数据存放规则)
subscribe(subscriber, subscriberMethod);
}
}
那我们就进入 subscribe 这个方法咯
// Must be called in synchronized block
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
//获取订阅的属于哪个类。
Class<?> eventType = subscriberMethod.eventType;
// 创建Subscription对象
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
//从subscriptionsByEventType里检查是否已经添加过该Subscription,如果添加过就抛出异常。
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);
}
}
//根据优先级priority来添加 新的 Subscription对象
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;
}
}
//将订阅者对象以及订阅的事件保存typesBySubscriber里
//为什么要用集合啦 是因为 一个类中可以包含很多 订阅事件。
List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
subscribedEvents.add(eventType);
//分发粘行事件处理。
//可以查看源码
}
我来解释一些这些代码:
1.先获取这个方法参数的类型。
2.在根据这个参数判断是否有订阅者。
3如果原来没有的话,就以方法的参数为 key 并添加一个空的对象集合。
4.根据优先级priority来添加 新的 Subscription对象
注意: 这里是根据方法参数 来添加订阅者的。
看到这里:
List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
- 获取到这个类下的所有订阅方法集合。
- 如果该类没有订阅的方法,那么生成一个空的对象集合。
以这个类为key,添加这个方法到集合中。
注意: 以当前类,获取或添加订阅的方法集合
如果读完上面的这些解析其实就明白了
1.注册的时候发生了什么?
答:获取订阅类中的所有被订阅的方法名称,方法参数类型。并且把对应的关系存放到集合。
有。1。根据方法参数 来添加订阅者的。
2 .以当前类,获取或添加订阅的方法集合
筛选 一些合法的订阅方法参数。
2.为什么我只需要 @Subscribe 注解声明一下就可以了,不用规范方法名称(我说的规范指的是onEvent这样规则命名方式)
答:因为在源码中通过注解获取到方法的参数类型以及方法。并通过方法的参数类型进行存储 订阅者的。(当然啦,如果命名有意义,那么阅读代码的速度也就更快了)
既然已经知道注册的过程了,那必然需要看到post方法啦(post 在手说到就到)
/**
* Posts the given event to the event bus.
*/
public void post(Object event) {
//得到当前线程的Posting状态 ThreadLocal 可以存储线程的值。
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()) {
/**
* eventQueue.remove()--》 移除第一个。
* postingState 判断是在子线程 或者 主线程 状态。
*/
postSingleEvent(eventQueue.remove(0), postingState);
}
} finally {
postingState.isPosting = false;
postingState.isMainThread = false;
}
}
}
那么就来解释一下是为什么吧?
可能大家可能很少接触 ThreadLocal 中的 ThreadLocal 这个类,其实可以这样理解
ThreadLocal是一个线程内部的数据存储类。通过它可以在指定线程中存储数据,并获取到指定线程中的数据。
如果还没有看到我原来写的 ThreadLocal 工作原理
其他的也就蛮简单直接看到下面这个方法吧
postSingleEvent();
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
Class<?> eventClass = event.getClass();
boolean subscriptionFound = false;
//是否触发订阅了该事件的父类,以及接口的类的响应方法。
if (eventInheritance) { // ------------- 等下再来看。
//查找eventClass类所有的父类以及接口。
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 {
//post 单个
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) {
//发送一个NoSubscriberEvent事件,如果我们需要处理这种状态。接收这个事
post(new NoSubscriberEvent(this, event));
}
}
}
说一下思路:
首先获取到 发送的类型。然后判断是否触发订阅事件的父类,以及响应方法。如果 eventInheritance ==false的话
就把这个类型的发送到所有订阅这个方法的类型。
如果没有订阅者的话,就直接发送一个
new NoSubscriberEvent()类型的事件。
接下来看看:
postSingleEventForEventType(event, postingState, eventClass); 方法
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
CopyOnWriteArrayList<Subscription> subscriptions;
synchronized (this) {
//根据这个类来查找 所有订阅在的集合。
subscriptions = subscriptionsByEventType.get(eventClass);
}
if (subscriptions != null && !subscriptions.isEmpty()) {
for (Subscription subscription : subscriptions) {
postingState.event = event;
postingState.subscription = subscription;
//是否被中断。 ----为什么要这样判断.
boolean aborted = false;
try {
//分发给订阅者
postToSubscription(subscription, event, postingState.isMainThread);
aborted = postingState.canceled;
} finally {
postingState.event = null;
postingState.subscription = null;
postingState.canceled = false;
}
if (aborted) {
break;
}
}
return true;
}
return false;
}
其实这个方法就是获取到订阅该种类型的所有订阅者。和处理一些异常的情况。然后循环变量发送给所有订阅过的订阅者
通过
postToSubscription(subscription, event, postingState.isMainThread);
那么继续看到这个方法
//判断发送的状态。
//不能在方法中写耗时的操作,避免消息延迟。因为是按照顺序执行。
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);
}
}
大家可能看到这么多的
case posing:
case Main:
case backgound:
case async:
这些都是注解修饰调用方法的类型
@Subscribe(threadMode = ThreadMode.BACKGROUND)
public void onEventMainThread(WorkEvent event) {
}
@Subscribe(threadMode = ThreadMode.ASYNC)
public void onEventMainThread(String event) {
}
以及它们的解释:
PostThread:如果使用事件处理函数指定了线程模型为PostThread,那么该事件在哪个线程发布出来的,事件处理函数就会在这个线程中运行,也就是说发布事件和接收事件在同一个线程。在线程模型为PostThread的事件处理函数中尽量避免执行耗时操作,因为它会阻塞事件的传递,甚至有可能会引起ANR。
MainThread:如果使用事件处理函数指定了线程模型为MainThread,那么不论事件是在哪个线程中发布出来的,该事件处理函数都会在UI线程中执行。该方法可以用来更新UI,但是不能处理耗时操作。
BackgroundThread:如果使用事件处理函数指定了线程模型为BackgroundThread,那么如果事件是在UI线程中发布出来的,那么该事件处理函数就会在新的线程中运行,如果事件本来就是子线程中发布出来的,那么该事件处理函数直接在发布事件的线程中执行。在此事件处理函数中禁止进行UI更新操作。
Async:如果使用事件处理函数指定了线程模型为Async,那么无论事件在哪个线程发布,该事件处理函数都会在新建的子线程中执行。同样,此事件处理函数中禁止进行UI更新操作。
其实分析我们就会好奇我为什么怎么调用它,怎么才能触发这个方法,并得到执行?
答案:必然是反射的使用
我们看到调用的方法。
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);
}
}
还记得我们说过为什么不能再回调的方法中超长时间的处理吗?
我们来看一个效果:
//效果图
//———————–
有没有看到其实这个时候我就是模拟队列请求。等一个个参数都按照顺序执行。
也正是因为顺序执行遇到第一个方法运行时间过长,就会导致后续的事件延迟发送。这个时候也可以用异步的方式发送,不过需要用handle 配合更新UI。
这也解决了这个问题 4.为什么说直接发送到主线程不能运行太耗时的方法?
还有那个问题 我们开启下一篇去写吧?我想把细节什么都写清楚啦。