EventBus源码解析-总结篇

EventBus 利弊与源码解析 EventBus 利弊 和 源码解析 阅读详情

上一篇文章:EventBus源码解析贴了很多源码解释了EventBus的运作原理,根据上篇文章看到的代码总结一下

目录

register
unregister
post
资源池的实现
拼接字符串
Poster事件入队和出队的机制


register

SubscriberMethodFinder.findSubscriberMethods

检查缓存

根据订阅者检查Finder内部的缓存,当存在该订阅者的缓存,则直接使用缓存的数据返回,提升效率。

ignoreGeneratedIndex

false表示忽略构造器获取到的SubscriberInfoIndex列表,直接使用反射。默认为false。

findUsingReflection
里面很多代码和findUsingInfo重合,所以就不展开了

findUsingInfo

  1. 准备FindState对象,用于辅助查找。
  2. 获取SubscriberInfo
  3. 当SubscriberInfo不为空,使用SubscriberInfo的数据。为空,调用findUsingReflectionInSingleClass方法
  4. 移动到订阅者的父类,继续寻找订阅方法
  5. 返回订阅方法并缓存FindState对象

准备FIndState对象
这个过程比较简单,从FindState池里面获取FindState对象,当获取不到的时候,就创建一个。准备我拿成之后就为该对象初始化必要的初始值。

getSubscriberInfo
获取SubscriberInfo,SubscriberInfo的作用是,让开发者手动声明订阅者所有的订阅方法。如果开发者自己手动声明,EventBus就不会使用反射的方式寻找订阅方法,直接使用SubscriberInfo提供的信息。这种方式相对来说性能比较高,因为使用反射还需要扫描所有方法,然后一个一个判断。
如果执行完成之后不为空,就执行添加的代码,将获取到的方法添加到findState的subscriberMethods里面。
如果为空,调用findUsingReflectionInSingleClass方法。

findUsingReflectionInSingleClass
该方法内部就会使用反射,扫描所有的方法,判断方法的:修饰符、方法参数、是否带有Subscribe注解。这三种方法去判断一个方法是否为订阅方法,如果是,就组装成SubscriberMethod对象,并添加到findState的subscriberMethods里面。

moveToSuperclass
上面两种方式执行完毕后,都会findState执行这个方法。这方法的作用就是findUsingInfo写的第4个步骤,移动到订阅者的父类。移动到订阅者的父类之后,就继续寻找订阅方法。当订阅者没有父类或者是父类是Java或Android的核心库,就停止。判断是否为Java或Android的核心库的方式是,直接贴代码:

// Skip system classes, this degrades performance.
// Also we might avoid some ClassNotFoundException (see FAQ for background).
if (clazzName.startsWith("java.") || clazzName.startsWith("javax.") ||
        clazzName.startsWith("android.") || clazzName.startsWith("androidx.")) {
    clazz = null;
}

如果clazz为空,就结束循环,开始返回订阅方法列表并将FindState对象放入到FIndState池里面

getMethodsAndRelease
在该方法里面,会为FindState里面的订阅方法列表创建一个副本。然后情况FIndState的数据并放入到FindState池里面,然后将这个副本返回。

寻找结束
寻找结束后会重新回到findSubscriberMethods方法,并获取到订阅方法列表。
这个时候就会做判断,为空时,抛异常。
不为空,将订阅者作为Key,将方法列表作为Value缓存起来。


EventBus.subscribe

在寻找方法完成后,就会对这些方法遍历,遍历过程中会调用subscribe方法

检查方法是否存在
会检查是否存在完全相同的订阅方法。
完全相同的标准:订阅者、订阅方法所在的类、订阅方法名称、订阅方法的Event类型
上面这4个数据完全相同,就断定他们是同一个方法,会抛异常。

订阅方法插入到合适的位置
这个比较简单,就是根据订阅方法的优先级做一次排序。

将订阅者和事件列表组成映射关系
这里只是将他们组成映射关系,然后就没有做其他操作,他们的映射关系会在unregister的时候发挥出作用。

订阅方法是粘性的
所谓粘性的,就是Subscribe注解的sticky属性为true。当订阅方法是粘性的,且在调用register之前发送过订阅事件,且事件类型和订阅方法的事件类型一致,就会调用订阅方法。
梳理一下调用的条件:订阅方法必须是粘性的、调用register之前调用过postSticky、发送粘性事件的Event和订阅方法的Evnet的类型一样。


unregister

获取订阅者所有的Event
获取方式其实很简单,在register的时候,有一个步骤为将订阅者和事件列表组成映射关系,所以这个时候通过这个Map就直接获取到事件列表。

unsubscribeByEventType
获取到事件列表之后,就调用这个方法。在这个方法里面,就根据Event获取到订阅方法列表。遍历订阅方法列表,判断订阅者和取消订阅的对象是否为同一个。如果是,将移除该订阅方法。


post

获取当前线程Event队列,并将Event添加到Event队列
之所以说当前线程,是因为使用ThreadLocal保证一个线程只有一个对象,获取到PostingThreadState之后就获取内部的Event队列。

判断当前线程是否在POTING
通过PostingThreadState.isPosting判断当前是否在POSTING,如果POSTING,就结束方法运行。否则,下一步。这个时候会将isPosting设置为true。

记录当前线程的信息并开启while循环从Event队列取出Event
while循环的结束条件是Event队列为空,在while内部会不断从Event队列取出Event并执行。所以外面才能判断如果是isPosting就结束运行,因为只需要将Event加入到Event队列即可。剩下的就交给while处理就行了。处理的方法是postSingleEvent。

postSingleEvent
在这里面经过一些判断后最终会调用postSingleEventForEventType方法。

postSingleEventForEventType
在这个方法里面,会根据Event获取到订阅方法列表,并遍历列表调用postToSubscription方法执行订阅方法。在执行完一个订阅方法之后,会判断是否取消POSTING,如果取消,会抛弃剩下的订阅方法结束该方法。之所以要判断是否取消POSTING,是因为EventBus提供了cancelEventDelivery这个方法让开发者在POSTING过程中取消执行某个Event的订阅方法。

postToSubscription
在这里面会根据订阅方法的线程模式做不同的操作。

  • POSTING:当前线程模式。这种模式直接调用invokeSubscriber方法执行订阅方法。这也是默认的线程模式。
  • MAIN:主线程模式。这种模式下,会判断调用线程是否为主线程。如果是,直接执行。如果不是,切换到主线程执行。
  • MAIN_ORDERED:这种模式下,无论是在什么线程,都会直接交给主线程执行。
  • BACKGROUND:这种模式下,会判断当前是否为主线程。如果是,切换到后台线程执行。如果不是,直接执行。
  • ASYNC:这种模式下,不管当前是什么线程,直接将任务扔给子线程的线程池。

注:上面没有写模式名称的,是因为不知道怎么翻译比较好。

图里面还有一个default,这个不是线程模式,所以不能和上面的混在一起。
这里的default是指swtich语法的default条件,也就是找不到合适的条件。所以这个时候会抛出一个未知线程模式的异常。


资源池的实现

EventBus内部一些地方为了避免频繁创建对象,使用池这种设计方式来缓存对象和复用对象。拿FindState池来说,提供了prepareFindState方法和getMethodsAndRelease方法来复用和缓存FindState对象。
private static final int POOL_SIZE = 4;
private static final FindState[] FIND_STATE_POOL = new FindState[POOL_SIZE];

private FindState prepareFindState() {
    synchronized (FIND_STATE_POOL) {
    	//遍历POOL_SIZE数组
        for (int i = 0; i < POOL_SIZE; i++) {
        	//根据index取出FindState对象
            FindState state = FIND_STATE_POOL[i];
            //如果不为空,表示有缓存,取出之后将所在的index置为空
            //之所以要置空,是因为该对象返回之后会被使用。
            //如果不置空,那该对象就有可能同时被多次使用,那就会出现问题。
            if (state != null) {
                FIND_STATE_POOL[i] = null;
                return state;
            }
        }
    }
    //当循环结束后,没有找到缓存对象,就重新创建一个。
    return new FindState();
}

//在FindState使用完成之后,会调用该方法将FindState对象缓存起来
private List<SubscriberMethod> getMethodsAndRelease(FindState findState) {
	//将FindState里面的SubscriberMethod列表克隆出来
    List<SubscriberMethod> subscriberMethods = new ArrayList<>(findState.subscriberMethods);
    //清空FindState里面的数据
    findState.recycle();
    synchronized (FIND_STATE_POOL) {
        for (int i = 0; i < POOL_SIZE; i++) {
        	//找到没有FindState对象的index,将FindState存进去
            if (FIND_STATE_POOL[i] == null) {
                FIND_STATE_POOL[i] = findState;
                break;
            }
        }
    }
    return subscriberMethods;
}

所以在开发的时候,如果某个对象需要频繁创建,就可以考虑能不能使用这种方式来实现一个资源池,避免频繁创建对象。


拼接字符串

拼接字符串可以算是开发中一个特别常见的操作。所以可能在写代码的时候,并没有考虑那么多,需要拼接的时候就直接拼接。而EventBus在这方面,也下了功夫。

在FindState里面有这样一段代码
static class FindState {
    final StringBuilder methodKeyBuilder = new StringBuilder(128);

    private boolean checkAddWithMethodSignature(Method method, Class<?> eventType) {
        methodKeyBuilder.setLength(0);
        methodKeyBuilder.append(method.getName());
        methodKeyBuilder.append('>').append(eventType.getName());
        ....
	}
}

由于需要频繁的拼接字符串,所以EventBus使用StrngBuilder来拼接,这倒也没什么问题。不过EventBus为了不用频繁创建StringBuilder对象,每次在调用setLength清空StringBuilder里面的数据,然后再重新append,从而避免频繁创建StringBuilder对象。


Poster事件入队和出队的机制

这个可以看HandlerPoster和BackgroundPoster的实现,我就贴HanlderPoster,反正都差不多。
public class HandlerPoster extends Handler implements Poster {
	private boolean handlerActive;
	//在源码中new是在构造方法里面new的,不过其实写在哪里区别都不大。
	private final PendingPostQueue queue = new PendingPostQueue();

    public void enqueue(Subscription subscription, Object event) {
        PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
        synchronized (this) {
        	//入队
            queue.enqueue(pendingPost);
            //handlerActive这个变量是用来判断当前Hander是否在执行handleMessage方法的
         	//当没有执行的时候,调用sendMessage
            if (!handlerActive) {
                handlerActive = true;
                if (!sendMessage(obtainMessage())) {
                    throw new EventBusException("Could not send handler message");
                }
            }
        }
    }	
    
	@Override
    public void handleMessage(Message msg) {
        boolean rescheduled = false;
        try {
            long started = SystemClock.uptimeMillis();
            //当执行的时候,在while循环内部不断地从queue取出消息
            //这样就不用频繁地调用sendMessage,从而减少主线程Looper的调度
            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;
        }
    }
}

目前只想到这些,后续想到了什么会继续补充。

一文搞定EventBus(含示例及源码解析) 日常开发中经常遇到一个业务发生之后需要触发好几个业务点,比如订单支付完成之后需要发送短信、送会员积分、发送优惠券等。在分布式系统中我们可以通过消息队列实现,各个系统之间订阅支付成功事件,然后实现各自的业务,达到一个系统之间的解耦和异步的目的。如果在同一个进程中也存在类似的通知需求,通过消息队列显得太笨重而且也没有跨进程或者系统架构中都没引入消息队列。这时候要实现进程内的消息通讯就可以通过Spring自带的Event事件或者google的EventBus。 阅读详情

相关推荐

EventBus 源码解析

使用方法 使用方法很简单,按照官方文档介绍,分为三个步骤。 步骤1:定义事件 public static class MessageEvent { } 步骤2:准备订阅者 定义订阅方法,来处理收到的事件。 @Subscribe(threadMode = ThreadMode.MAIN) public void onMessageEvent(MessageEvent event) {/* Do something */}; 并在Activity、Fragment中按照其生命周期进行注册与注销。 @O

jerechen的博客 678

EventBus 传值问题

EventBus3.0,Fragment 跳转到外部activity后,从外部activity传值到fragment,无法接收的问题首先分析一下问题的所在;解决这个问题的办法 有两个 首先分析一下问题的所在; 通过debug fragment生命周期,我们发现如果我们把 EventBus的注册和注销写在onResume 和onPause中时,那么从外部activity传值过来时,就不会执行Eve...

流年似水何不珍惜 732

EventBus 原理简化分析版

EventBus 原理简化分析版

EventBus 普通事件的post

EventBus 普通事件的post 当订阅事件完成后,就需要发送时间通知了,调用比较简单 EventBus.getDefault().post(eventType); 看一下post事件: /** Posts the given event to the event bus. */ public void post(Object event) { PostingThre...

相公 1389

EventBus使用详解教程,带你学习eventbus。详解Demo

注意事项遇到问题请看:http://blog.csdn.net/qq_21376985/article/details/52438143

《EventBus使用详解(二)——EventBus使用进阶》对应源码

博客《EventBus使用详解(二)——EventBus使用进阶》对应源码,博客地址:http://blog.csdn.net/harvic880925/article/details/40787203

MySQL入门准备:环境搭建、基础操作与常见排错全指南

数据库管理系统(DBMS)是现代应用的数据基石,而SQL则是与这类系统交互的标准语言。初学者常因环境安装失败、连接报错或字符集乱码而止步于第一行代码。本文从工程实践视角出发,梳理MySQL学习前的完整准备链路:先理解数据库与表结构的关系,再完成版本选择、安装验证和基础CRUD操作,同时解析字段类型、主键、索引等核心概念,并提供一套按端口占用、权限验证、字符集配置顺序执行的排错方法。无论你是准备课程设计,还是转入后端开发,这套入门检查清单都能帮你快速建立可复用的本地数据库环境,降低学习摩擦,为后续事务、查询优

weixin_34413103的博客 171

从JX-2R-01热敏打印机芯到稳定打印:硬件设计与时序控制的实践指南

本文详细解析了JX-2R-01热敏打印机芯的硬件设计与驱动开发全流程。核心内容包括:实现严格的双电源上电时序控制、采用PWM恒流驱动步进电机确保走纸平稳,以及精确控制SPI数据加载与加热脉冲时序以获得清晰打印效果。文章提供了从电路设计、芯片选型到代码调试的实战指南,旨在帮助开发者构建稳定可靠的热敏打印模块。

weixin_29169899的博客 223

【EventBus】EventBus 源码解析 ( 事件发送 | postToSubscription 方法 | EventBus 线程模式处理细节 )

一、事件发送 postSingleEventForEventType 方法、 二、事件发送 postToSubscription 方法、 三、事件发送 invokeSubscriber 方法、

让 学习 成为一种 习惯 ( 韩曙亮 の 技术博客 ) 1065

无刷直流电机矢量控制(四):基于滑模观测器的无传感器控制

无刷直流电机矢量控制,采用转速、电流双环控制,电流环dq解耦,d轴采用id=0控制,q轴采用转速外环。采用滑模观测器(SMO),根据电压电流信息进行电机位置角度动态估算,模型可直接运行,转速和角度跟踪精度较高。 本文整理了该方法的基本原理,介绍了在MATLAB/Simulink中的建模和仿真过程,最后附上完整的模型文件。 一、基本原理 滑模观测器(下文简称SMO)是基于滑模变结构控制方法的一种状态观测器。其针对无刷直流电机转子位置估算的实现过程如下。 假设采用矢量控...

Forster-C的博客 6867

COMSOL+AI实战:5个超实用技巧让你的仿真效率翻倍(附Python代码)

本文详细介绍了如何将人工智能(AI)与COMSOL仿真软件结合,通过Python脚本实现五大实用技巧,包括自动化生成数据集、构建代理模型、集成AI优化算法、利用神经网络加速场量求解以及智能结果分析,从而显著提升多物理场仿真的效率和自动化水平。

weixin_29230865的博客 443

EventBus源码解析,看这篇就够了

先说下大家熟悉的EventBus EventBus使用起来特别方便简单,如果一个类需要被通知,然后做一些事情,那么只需要在这个类里面调用EventBus.getDefault().register(this);方法进行注册,其次是使用@Subscribe注解方法,那么只需要在需要发送事件的地方,调用post或者postSticky方法(参数就是被注解函数的参数类型)就可以在这个被注解的方法这里收...

和小胖 1776

麒麟系统下PaddleOCR的离线部署实战:从环境搭建到模型优化

本文详细介绍了在国产麒麟操作系统上离线部署百度PaddleOCR的完整实战流程。从系统环境准备、Conda虚拟环境搭建,到PaddlePaddle框架和PaddleOCR的离线安装,再到预训练模型的本地部署与优化技巧,提供了全面的文字识别解决方案。重点解决了离线环境下依赖包管理、模型量化加速等核心问题,并附带了批量处理与内存监控的进阶代码,适用于对数据安全有严格要求的政务、金融等场景。

docker7nomad的博客 963

Android EventBus源码解析 带你深入理解EventBus

转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/40920453,本文出自:【张鸿洋的博客】上一篇带大家初步了解了EventBus的使用方式,详见:Android EventBus实战 没听过你就out了,本篇博客将解析EventBus的源码,相信能够让大家深入理解该框架的实现,也能解决很多在使用中的疑问:为什么可以这么做?为什么

Hongyang 11万+

AE-扭曲-变形-弧

一些AE效果的着色器实现记录

What_can_you_do的博客 1175

【CASS精品教程】032:CASS11台阶画法案例大全

一、普通无边台阶 二、普通有边台阶 三、普通圆弧无边台阶 四、U型台阶 五、曲线U型台阶 六、L型台阶

智慧空间实验室 7466

EventBus原理与源码解析

一、概述       EventBus是针对Android优化的发布-订阅事件总线,简化了Android组件间的通信。EventBus以其简单易懂、优雅、开销小等优点而备受欢迎。      关于EventBus的基本使用不再详述,网上的资料很多,可以自行学习,或者直接去GitHub上查看基本使用方法。      在讲解源码之前,先说一下EventBus需要关注的点 - EventBus支持的四种线...

json_it的博客 7157

基于mengzi-t5训练生成式模型

【代码】基于mengzi-t5训练生成式模型。

开卷有益 171

【EventBus】EventBus源码浅析

简单分析了Eventbus的构造方法,订阅事件、取消订阅,发送Post的源码

摸鱼小小虫的博客 2333

LeRobot — 基于 PyTorch 的真实世界机器人学习框架(一)- 核心框架模块解析

LeRobot 是一个开源的机器人学习框架,由 Hugging Face 开发,旨在降低机器人学的准入门槛。该框架提供完整的机器学习流程支持,包括数据采集、训练、评估和部署。核心模块涵盖数据集标准化、策略算法、硬件抽象和强化学习组件,采用模块化设计实现灵活扩展。通过集成 Hugging Face Hub,用户可以轻松共享数据集和预训练模型。LeRobot 特别注重真实世界的机器人应用,支持多种前沿算法如模仿学习、视觉语言动作模型等,并提供统一的硬件接口和仿真环境适配。

熊猫小妖的AI世界 171

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

EventBus源码解析

前言:在现在的android项目开发小伙伴们越来越多的在使用EventBus了作为事件的同步了,EventBus是针对Android优化的发布-订阅事件总线,简化了Android组件间的通信,以其简单易懂、优雅、开销小等优点而备受欢迎。所以在之前已经整理了EventBus与Otto使用和区别之后,我最近再次梳理了EventBus的源码原理。 友情提示:该篇文章主要是介绍梳理EventBus的源码...

蒙的博客 422

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

上一篇: EventBus源码解析
下一篇: android unit test mock框架使用记录
杜壁奇
博客等级 码龄10年 10粉丝 31原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值