一文彻底搞懂EventBus 3

ThreadMode可以指定的模式有:

  1. ThreadMode.POSTING:默认的线程模式,在哪个线程发送事件就在对应线程处理事件,避免了线程切换,效率高。
  2. ThreadMode.MAIN:如在主线程(UI线程)发送事件,则直接在主线程处理事件;如果在子线程发送事件,则先将事件入队列,然后通过 Handler 切换到主线程,依次处理事件。
  3. ThreadMode.MAIN_ORDERED:无论在哪个线程发送事件,都将事件加入到队列中,然后通过Handler切换到主线程,依次处理事件。
  4. ThreadMode.BACKGROUND:与ThreadMode.MAIN相反,如果在子线程发送事件,则直接在子线程处理事件;如果在主线程上发送事件,则先将事件入队列,然后通过线程池处理事件。
  5. ThreadMode.ASYNC:与ThreadMode.MAIN_ORDERED相反,无论在哪个线程发送事件,都将事件加入到队列中,然后通过线程池执行事件

3.register注册

好了,要想使用Eventbus,则要先注册它,看看如何使用

EventBus.getDefault().register(this);

很简单吧,getDefault()其实就是一个单例模式,创建EventBus实例对象,并返回

public static EventBus getDefault() {

if (defaultInstance == null) {
synchronized (EventBus.class) {
if (defaultInstance == null) {
defaultInstance = new EventBus();
}
}
}
return defaultInstance;
}

没啥可说的,继续看register

先看图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RMmfkU4o-1650016916779)(https://user-gold-cdn.xitu.io/2019/10/18/16ddde2e9b75be3d?imageView2/0/w/1280/h/960/ignore-error/1)]

再看代码

public void register(Object subscriber) {
Class<?> subscriberClass = subscriber.getClass(); // 获取传入的要注册类的字节码文件
List subscriberMethods =
subscriberMethodFinder.findSubscriberMethods(subscriberClass); // ->>分析1

synchronized (this) {

// 遍历订阅方法封装类的集合
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod); // ->> 分析4
}
}
}

从上面的图可以看出,这个方法其实就是做了2件事

  1. 根据注册类的字节码文件,调用findSubscriberMethods方法,获取该注册类上的所有订阅方法的信息集合。
  2. 遍历这个信息集合,给2个map填充数据: subscriptionsByEventType可以根据event(事件类型,订阅方法上的参数类型)获取所有订阅方法信息集合。 typesBySubscriber可以根据这个注册类,获取这个注册类上所有的event事件类型。

/**

  • 分析1:findSubscriberMethods()
  • 作用:获取当前要进行注册类中的所有订阅方法,也就是找寻使用了Subscribe注解、有public修饰符、一个参数的方法
    */
    List findSubscriberMethods(Class<?> subscriberClass) {

// METHOD_CACHE: 是一个ConcurrentHashMap,key是要注册类的字节码文件,value是这个字节码文件里的所有订阅方法信息的集合,集合的元素是SubscriberMethod,它实际上就是订阅方法的信息类,包含Method对象、线程模式、事件类型、优先级、是否是粘性事等。
List subscriberMethods = METHOD_CACHE.get(subscriberClass); // 这步实际上就是看看这个注册类的方法是否已经缓存了,缓存过就直接根据类返回
if (subscriberMethods != null) {
return subscriberMethods;
}

// EventBus是支持EventBusBuilder的,如果我们自定义了EventBusBuilder,则ignoreGeneratedIndex为true,否则为false,我们没自定义,所有看false
if (ignoreGeneratedIndex) {
subscriberMethods = findUsingReflection(subscriberClass);
} else {

// ->>分析2
subscriberMethods = findUsingInfo(subscriberClass);
}

// 如果该类没有找到订阅方法,抛出异常
if (subscriberMethods.isEmpty()) {
throw new EventBusException(“Subscriber " + subscriberClass + " and its super classes have no public methods with the @Subscribe annotation”);
} else {

// 将该注册类的类型为key, 将这个类所有注册方法的封装类集合为value存入map集合
METHOD_CACHE.put(subscriberClass, subscriberMethods);
return subscriberMethods;

// ->> 返回register()方法中
}
}

这个方法主要作用就是根据传入的注册类返回该类上所有的订阅方法的信息,先找缓存METHOD_CACHE,有就走缓存,没有就调用findUsingInfo方法获取订阅方法信息集合,然后再根据注册类为key, 订阅方法的信息集合为value, 存入缓存(METHOD_CACHE)中。

/**

  • 分析2:findUsingInfo()
  • 作用:如果findState缓存了,订阅方法信息,则使用findState里的缓存,否则调用findUsingReflectionInSingleClass方法,反射获取订阅方法信息。
    */
    private List findUsingInfo(Class<?> subscriberClass) {

// FindState辅助我们查找订阅方法的类,后面会讲述
FindState findState = prepareFindState();
findState.initForSubscriber(subscriberClass);

// findState.clazz就是我们的注册类subscriberClass
while (findState.clazz != null) {

findState.subscriberInfo = getSubscriberInfo(findState);

// 该类第一次注册时,findState.subscriberInfo为null, 我们走false
if (findState.subscriberInfo != null) {
SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods();
for (SubscriberMethod subscriberMethod : array) {
if (findState.checkAdd(subscriberMethod.method, subs 《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》开源 criberMethod.e Android开源项目《ali1024.coding.net/public/P7/Android/git》 ventType)) {
findState.subscriberMethods.add(subscriberMethod);
}
}
} else {

// ->> 分析3
findUsingReflectionInSingleClass(findState);
}

// 修改findState.clazz为subscriberClass的父类Class,即需要遍历父类
findState.moveToSuperclass();
}

// 将查找到的方法保存在了FindState实例的subscriberMethods集合中。然后使用subscriberMethods构建一个新的List并返回,最后释放掉findState
return getMethodsAndRelease(findState);

// ->> 返回到findSubscriberMethods() 方法中
}

/**

  • 分析3:findUsingReflectionInSingleClass()
  • 作用:通过反射获取订阅方法的信息
    */
    private void findUsingReflectionInSingleClass(FindState findState) {
    Method[] methods;
    try {
    // 通过反射获取订阅类中的所有方法
    methods = findState.clazz.getDeclaredMethods();
    } catch (Throwable th) {

    }

// 遍历方法
for (Method method : methods) {

// 获取方法修饰符
int modifiers = method.getModifiers();

// 方法是public类型,但非abstract、static等
if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {

// 获取方法的修饰类型
Class<?>[] parameterTypes = method.getParameterTypes();

// 只能是1个参数
if (parameterTypes.length == 1) {

// 获取方法上的名为Subscribe的注解
Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);

// 如果该方法带Subscribe注解
if (subscribeAnnotation != null) {

// 获取该订阅方法上的第一个参数类型,也就是订阅的事件类型
Class<?> eventType = parameterTypes[0];

// checkAdd()方法用来判断FindState中是否已经添加过将该事件类型为key的键值对,没添加过则返回true
if (findState.checkAdd(method, eventType)) {

// 获取线程模式
ThreadMode threadMode = subscribeAnnotation.threadMode();

// 将该订阅方法,事件类型,线程模式,优先级,是否支持粘性事件等信息,封装成SubscriberMethod对象,并添加到findState中的subscriberMethods集合里
findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode, subscribeAnnotation.priority(), subscribeAnnotation.sticky()));

// ->> 返回到findUsingInfo() 方法中
}
}
} else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {

}
} else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {

}
}
}

根据反射,获取订阅方法的信息数据,然后将它分封装成SubscriberMethod对象,并添加到findState的集合中。

/**

  • 分析4:subscribe()
  • 作用:主要就是构建2个map对象
    */
    private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {

// 获取该订阅方法的事件类型
Class<?> eventType = subscriberMethod.eventType;

// 将订阅方法的封装类,再进行封装,也就是注册类的信息也存入了
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);

// subscriptionsByEventType是hashmap, 以事件类型为key, Subscription集合为value
// 先查找subscriptionsByEventType是否存在以当前事件类型为key的值
CopyOnWriteArrayList 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);
}
}

// 添加上边创建的newSubscription对象到subscriptions中
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也是一个HashMap,保存了以当前要注册类的对象为key,注册类中订阅事件的方法的参数类型的集合为value的键值对
// 和上面一样,根据key先判断,是否已经存储过了,如果已经存储过了,直接取出订注册类中订阅事件的方法的参数类型的集合
List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
subscribedEvents.add(eventType);

// 是否支持粘性事件
if (subscriberMethod.sticky) {

// ->> 分析5

}

2个map构建完毕了,我们的注册也就完事了

总结一下

传入注册类信息,根据反射获取注册类上的所有方法,遍历这些方法,取出其中的订阅方法(条件是,一个参数,权限为public,使用了Subscribe标签)将方法的信息封装成SubscriberMethod对象,并存入集合,然后再遍历这个集合,取出其中的SubscriberMethod对象,再根据注册类的字节码文件,合并成Subscription对象,再根据event类型,进行重新分类,存入map subscriptionsByEventType中(key 为event, value 为List),再创建map typesBySubscriber, 注册类为key , list为value。 完事了。

4.unregister取消注册

使用很简单

EventBus.getDefault().unregister(this);

看图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TtFrsVL5-1650016916780)(https://user-gold-cdn.xitu.io/2019/10/18/16ddde2ea4e79f98?imageView2/0/w/1280/h/960/ignore-error/1)]

看下代码

public synchronized void unregister(Object subscriber) {

// ->> 分析6
List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);

// 如果集合不为null
if (subscribedTypes != null) {

// 遍历集合,获取订阅事件的类型
for (Class<?> eventType : subscribedTypes) {

// ->> 分析7
unsubscribeByEventType(subscriber, eventType);
}
typesBySubscriber.remove(subscriber);
} else {
logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass());
}
}

分析6:还记得我们分析注册时,创建的那2个map吗? 其中一个是typesBySubscriber,key是注册类,value是事件类型的集合(List), 这一步就是根据注册类获取该类所有订阅方法的事件类型。

/**

  • 分析7
    */
    private void unsubscribeByEventType(Object subscriber, Class<?> eventType) {

// 根据事件类型,获取该事件类型所对应的订阅方法信息的集合
List subscriptions = subscriptionsByEventType.get(eventType);

// 如果集合不为null
if (subscriptions != null) {

// 遍历,该事件类型所对应的订阅方法
int size = subscriptions.size();
for (int i = 0; i < size; i++) {

// 获取Subscription对象,该对象包含了订阅方法的所有信息和注册类信息
Subscription subscription = subscriptions.get(i);

// 因为subscriptionsByEventType可不光包含了1个注册类的信息,所以要加下面的判读,如果该订阅方法所在的注册类是我们要解除的注册类的话
if (subscription.subscriber == subscriber) {
subscription.active = false;

// 从集合中,将该订阅方法的信息删除掉
subscriptions.remove(i);
i–;
size–;
}
}
}
}

总结一下

解除绑定,其实比较简单,主要就是运用注册时所产生的2个map, 先根据typesBySubscriber,也就是根据要解除绑定的注册类,找到这个类所拥有的所有订阅事件,然后遍历这些订阅事件,再根据这些订阅事件,在subscriptionsByEventType中找到,这个事件所对应的订阅方法的集合,再遍历集合,判断该订阅方法的注册类信息,是否是要解除绑定的注册类,如果是,移除该订阅方法信息,完成解除绑定。

4.post发布事件

使用也很简单

EventBus.getDefault().post(new Object());

看图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hLPCjqck-1650016916781)(https://user-gold-cdn.xitu.io/2019/10/18/16ddde2ea44d4858?imageView2/0/w/1280/h/960/ignore-error/1)]

看代码

public void post(Object event) {

// ->> 分析8
PostingThreadState postingState = currentPostingThreadState.get();

// 获取postingState里面存的一个队列
List eventQueue = postingState.eventQueue;

// 将要发送的事件,存入队列中
eventQueue.add(event);

// 判断该事件是否正在发送,如果在发送,则跳过下面的逻辑
if (!postingState.isPosting) {

// 判断是否在主线程
postingState.isMainThread = isMainThread();
postingState.isPosting = true;
if (postingState.canceled) {
throw new EventBusException(“Internal error. Abort state was not reset”);
}
try {

// 遍历队列
while (!eventQueue.isEmpty()) {

// ->> 分析9
postSingleEvent(eventQueue.remove(0), postingState);
}
} finally {

// 重置状态
postingState.isPosting = false;
postingState.isMainThread = false;
}
}
}

分析8:postingState实际就是一个线程状态的封装类,包含事件队列,线程状态,是否正在发送的标识位,Subscription等信息,currentPostingThreadState为ThreadLocal,这也就说明postingState为线程独有的,不会让其他线程共享当前线程的数据

post() 方法主要就是要先将发送的事件保存在postingState中的队列里面,它是线程独有的,然后通过循环队列,将事件交给postSingleEvent()方法处理。

/**

  • 分析9
    */
    private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
    Class<?> eventClass = event.getClass();
    boolean subscriptionFound = false;

// 是否要查看所有的继承关系
if (eventInheritance) {

// 通过lookupAllEventTypes()拿到该事件所有的父类事件类型
List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
int countTypes = eventTypes.size();

// 遍历事件类型
for (int h = 0; h < countTypes; h++) {
Class<?> clazz = eventTypes.get(h);

// ->> 分析10

总结

开发是面向对象。我们找工作应该更多是面向面试。哪怕进大厂真的只是去宁螺丝,但你要进去得先学会面试的时候造飞机不是么?

作者13年java转Android开发,在小厂待过,也去过华为,OPPO等,去年四月份进了阿里一直到现在。等大厂待过也面试过很多人。深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。

这里附上上述的技术体系图相关的几十套腾讯、头条、阿里、美团等公司的面试题,把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,这里以图片的形式给大家展示一部分。

相信它会给大家带来很多收获:

960页全网最全Android开发笔记

资料太多,全部展示会影响篇幅,暂时就先列举这些部分截图

当程序员容易,当一个优秀的程序员是需要不断学习的,从初级程序员到高级程序员,从初级架构师到资深架构师,或者走向管理,从技术经理到技术总监,每个阶段都需要掌握不同的能力。早早确定自己的职业方向,才能在工作和能力提升中甩开同龄人。

也面试过很多人。深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。

这里附上上述的技术体系图相关的几十套腾讯、头条、阿里、美团等公司的面试题,把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,这里以图片的形式给大家展示一部分。

相信它会给大家带来很多收获:

[外链图片转存中…(img-naofqnt4-1650016916781)]

[外链图片转存中…(img-a5bkcwf7-1650016916782)]

资料太多,全部展示会影响篇幅,暂时就先列举这些部分截图

当程序员容易,当一个优秀的程序员是需要不断学习的,从初级程序员到高级程序员,从初级架构师到资深架构师,或者走向管理,从技术经理到技术总监,每个阶段都需要掌握不同的能力。早早确定自己的职业方向,才能在工作和能力提升中甩开同龄人。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值