EventBus1.0.1源码解析

很久没有更新过源码解析类文章,以下内容作为源码分析类的笔记。分析方法适用于其它源码分析。

分析工具说明

许久以来,阅读源代码最得力的工具就非Source Insight莫属了。然,后来微软出了一款轻量级但功能强大的IDE就没Source Insight什么事了。微软的这款IDE就是大名鼎鼎的VSCODE,全名叫:Visual Studio Code。它的强大之处这里就不过多描述了。我们转回到正题。

获取源码并导入IDE

我们在Github.com上搜索到EventBus的项目地址,然后进行克隆即可。

    git clone https://github.com/greenrobot/EventBus.git

克隆完成之后,导入IDE。打开VS CODE,在菜单栏中选择File -> Open,选择克隆后文件所在的目录,然后点击确认。这就完成了代码导入工作。

由于我们只是分析EventBus的骨架脉络,所以我们要选择EventBus的早期版本。这里我们将要选择V1.0.1版。这同样适用于Android源代码分析,适宜选择1.5版或1.6版。

切换版本的步骤为:先进入EventBus刚才的克隆目录,然后执行以下步骤:

    //列出EventBus项目的所有tag。sahadevs-MacBook-Pro:EventBus sahadev$ git tagV1.0.1V2.2.0V2.3.0V2.4.0V3.0.0//上面可以看出EventBus共发布了5个版本。我们切换到V1.0.1版。sahadevs-MacBook-Pro:EventBus sahadev$ git checkout V1.0.1

经过以上步骤,我们的代码就切换到了V1.0.1版本。VS CODE中的内容也会同步更新。接下来开始我们的代码分析。

事件监听注册

我们熟知EventBus的用法,其中最有代表性的入口为:

     EventBus.getDefault().register(this);

为了方便分析,上面这行代码放在Activity中执行。所以上面的this就是Activity的实例。EventBus采用了单例模式,在这个版本中,采用了最简便的实现方式:

public class EventBus {private static final EventBus defaultInstance = new EventBus();public static EventBus getDefault() {return defaultInstance;}
}

好,以上并不是我们的重点。我们要从register方法说起。

    public void register(Object subscriber) {register(subscriber, defaultMethodName, ThreadMode.PostThread);}public void register(Object subscriber, String methodName, ThreadMode threadMode) {List<Method> subscriberMethods = findSubscriberMethods(subscriber.getClass(), methodName);for (Method method : subscriberMethods) {Class<?> eventType = method.getParameterTypes()[0];subscribe(subscriber, method, eventType, threadMode);}}

register(this)方法执行时调用了register(Object subscriber, String methodName, ThreadMode threadMode),subscriber是Activity,methodName采用了defaultMethodName的值,这个值在EventBus中定义为:’onEvent’,threadMode为ThreadMode.PostThread。其中ThreadMode为一个枚举,它有两个值:

    public enum ThreadMode {/** Subscriber will be called in the same thread, which is posting the event. */PostThread,/** Subscriber will be called in Android's main thread (sometimes referred to as UI thread). */MainThread,/* BackgroundThread */}

PostThread用于标识发送线程,MainThread用于标识主线程。它们在后期执行的区别为:如果在注册时为PostThread,则事件发送后,会直接调起监听方法。而如果注册时为MainThread,则会经过主线程的消息队列调起。

好,回到刚才的地方,在register(Object subscriber, String methodName, ThreadMode threadMode)方法内先执行了findSubscriberMethods,我们移步看看这个方法内执行了什么工作:

    private List<Method> findSubscriberMethods(Class<?> subscriberClass, String methodName) {...List<Method> subscriberMethods;...subscriberMethods = new ArrayList<Method>();Class<?> clazz = subscriberClass;HashSet<Class<?>> eventTypesFound = new HashSet<Class<?>>();while (clazz != null) {String name = clazz.getName();...Method[] methods = clazz.getDeclaredMethods();for (Method method : methods) {if (method.getName().equals(methodName)) {Class<?>[] parameterTypes = method.getParameterTypes();if (parameterTypes.length == 1) {if (eventTypesFound.add(parameterTypes[0])) {// Only add if not already found in a sub classsubscriberMethods.add(method);}}}}clazz = clazz.getSuperclass();}...}

这里面的代码比较长,将不必要的略去。这段代码主要做了以下工作:

根据给定的类,遍历该类及其父类所有的方法,如果遍历时发现与给定的方法名相等,并且该方法的参数只有一个,那么就将该方法加入到subscriberMethods中,最后返回。这里被找到的方法是同名的,但是参数类型不同,这里的参数类型决定了将来要监听的事件类型。

好,我们回到findSubscriberMethods之前被调用的地方,继续往下走。接下来的动作是将findSubscriberMethods方法返回的Method列表进行遍历。而这次遍历时,将Method的第一个参数类型作为参数传给了subscribe方法:

    private void subscribe(Object subscriber, Method subscriberMethod, Class<?> eventType, ThreadMode threadMode) {CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);if (subscriptions == null) {subscriptions = new CopyOnWriteArrayList<Subscription>();subscriptionsByEventType.put(eventType, subscriptions);} ...subscriberMethod.setAccessible(true);Subscription subscription = new Subscription(subscriber, subscriberMethod, threadMode);subscriptions.add(subscription);...}

该方法首先做的就是根据eventType从subscriptionsByEventType对象中取出对应的subscriptions,然这里第一次肯定是null。所以先进行实例化,然后存放于subscriptionsByEventType中。而subscriptionsByEventType对象存放的就是根据eventType所对应的subscriptions。而subscriptions中存放的就是所有的观察者,待会有时间发生时,根据事件的类型,从subscriptionsByEventType中取出所有的观察者subscriptions逐一通知。

在创建了观察者列表subscriptions之后开始创建具体的观察者。Subscription是一个基本的类,它的示例只是持有了subscriber(Activity),subscriberMethod(onEvent),threadMode(ThreadMode.PostThread)三者的引用而已。

到这里,观察者Activity监听某一事件的注册工作就算完成了。

事件发送

接下来,我们将从事件发送入口post方法开始分析:

    public void post(Object event) {//这里的两个属性很重要,下面这个用于缓存事件,待下次事件执行时,一并执行。List<Object> eventQueue = currentThreadEventQueue.get();eventQueue.add(event);//这个用于标识是否正在发送事件,保证事件的顺序发送。防止多线程情况下的交叉执行。BooleanWrapper isPosting = currentThreadIsPosting.get();if (isPosting.value) {return;} else {isPosting.value = true;try {while (!eventQueue.isEmpty()) {postSingleEvent(eventQueue.remove(0));}} finally {isPosting.value = false;}}}

这里最开始有两个EventBus的成员属性,这里很重要:currentThreadEventQueue与currentThreadIsPosting。这两个属性本身是和线程有关系的。关于这两个属性内部的作用机制这里就不再赘述了。上面post方法的第一行currentThreadEventQueue.get()返回的是一个空的ArrayList()对象。而currentThreadIsPosting.get()方法返回的是一个BooleanWrapper对象,它的默认属性value默认是false。如果对这块的返回值有疑问,可以在EventBus.java文件内找到这两个对象的定义,它们在new时,通过initialValue方法返回了get获取到的初始值。

通过判断,最终进入postSingleEvent方法内,而该方法则是事件分发的核心:

    private void postSingleEvent(Object event) throws Error {List<Class<?>> eventTypes = findEventTypes(event.getClass());...int countTypes = eventTypes.size();for (int h = 0; h < countTypes; h++) {Class<?> clazz = eventTypes.get(h);CopyOnWriteArrayList<Subscription> subscriptions;synchronized (this) {subscriptions = subscriptionsByEventType.get(clazz);}if (subscriptions != null) {for (Subscription subscription : subscriptions) {if (subscription.threadMode == ThreadMode.PostThread) {postToSubscribtion(subscription, event);} else if (subscription.threadMode == ThreadMode.MainThread) {mainThreadPoster.enqueue(event, subscription);} else {throw new IllegalStateException("Unknown thread mode: " + subscription.threadMode);}}...}}...}

这个方法内首先调用了findEventTypes方法,我们先移步看看它做了什么工作:

    private List<Class<?>> findEventTypes(Class<?> eventClass) {...Class<?> clazz = eventClass;while (clazz != null) {eventTypes.add(clazz);addInterfaces(eventTypes, clazz.getInterfaces());clazz = clazz.getSuperclass();}return eventTypes;}

这部分的主要工作为:将指定类以及父类所实现的接口及其本身的类型加入到eventTypes中,最后将eventTypes返回。如果我们在定义事件类型时,没有继承任何类、实现任何接口,那么就会省去这个步骤,节省时间。

回到postSingleEvent方法继续往下看:根据刚刚返回的事件类型集合开始遍历,每次遍历时,根据给定的事件类型从subscriptionsByEventType中寻找对应的监听者。我们在上面分析注册部分时候已经见过,subscriptionsByEventType中存放的就是事件类型与监听者的键值对。找到监听者列表subscriptions之后,开始对subscriptions进行遍历,进行消息下发。

消息的发送主要两种模式:

  • PostThread
  • MainThread

我们在开头的时候就提到过,PostThread会通过反射直接调起对应的方法,而MainThread则会通过主线程的消息队列进行调起。我们来看看究竟是不是这样:

    //PostThread的执行方式static void postToSubscribtion(Subscription subscription, Object event) throws Error {...subscription.method.invoke(subscription.subscriber, event);...}//MainThread的执行方式void enqueue(Object event, Subscription subscription) {PendingPost pendingPost = PendingPost.obtainPendingPost(event, subscription);Message message = obtainMessage();message.obj = pendingPost;if (!sendMessage(message)) {throw new RuntimeException("Could not send handler message");}}

可以看到:对于PostThread类型,直接通过反射将事件传给了观察者。而对于MainThread类型,则是通过Handler将信息传给了主线程。最后消息到达主线程后,通过与PostThread方式一样经过postToSubscribtion方法执行。


以上就是关于EventBus事件监听注册、事件广播的解析过程。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/478699.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

pycharm插件之SonarLint

pycharm插件之SonarLint pycharm插件之SonarLint 一、插件安装位置 1、在线安装插件 通过File—>Settings—>Plugins进行安装插件&#xff0c;然后只需要重新启动IEDA即可。 2、离线安装插件 通过 Settings > Plugins > Install Plugin from 离线安装&#x…

论文浅尝 - ICLR2020 | You Can Teach an Old Dog New Tricks!关于训练知识图谱嵌入

论文笔记整理&#xff1a;谭亦鸣&#xff0c;东南大学博士生。来源&#xff1a;ICLR2020链接&#xff1a;https://openreview.net/pdf?idBkxSmlBFvrKG embedding&#xff08;KGE&#xff09;模型的目标是学习知识图谱中实体和关系的向量表示。近年来众多的KGE方法被提出&#…

撑起百万亿参数模型想象力!英伟达发布新一代SuperPOD超算,AI算力新巅峰!

周一&#xff0c;黄教主又很淡定的在自家厨房里开完了GTC发布会。众所周知&#xff0c;NLP领域的模型一个比一个大&#xff0c;自从百亿参数的Google T5出来后&#xff0c;大部分AI研究者只能望着手里的蹩脚算力兴叹。如今动辄就是千亿、万亿参数模型&#xff0c;目前比较流行的…

每日优鲜小程序基础组件介绍

每日优鲜小程序基础组件介绍1.基础组件介绍2.基础组件的结构与作用3.基础组件的接入方法初次引入初始化更新与维护基础组件接入1.基础组件介绍 小程序基础组件基于每日优鲜主商城小程序业务实践演变而来。 基础组件的名称为&#xff1a;mini_app_base_module。 基础组件的项…

知识图谱入门 (八) 语义搜索

原文链接&#xff1a;https://zhuanlan.zhihu.com/p/39237778

Redis 高负载下的中断优化

背景 2017年年初以来&#xff0c;随着Redis产品的用户量越来越大&#xff0c;接入服务越来越多&#xff0c;再加上美团点评Memcache和Redis两套缓存融合&#xff0c;Redis服务端的总体请求量从年初最开始日访问量百亿次级别上涨到高峰时段的万亿次级别&#xff0c;给运维和架构…

LeetCode 394. 字符串解码(栈)

1. 题目 给定一个经过编码的字符串&#xff0c;返回它解码后的字符串。 编码规则为: k[encoded_string]&#xff0c;表示其中方括号内部的 encoded_string 正好重复 k 次。注意 k 保证为正整数。 你可以认为输入字符串总是有效的&#xff1b;输入字符串中没有额外的空格&…

90TB显存!英伟达发布新一代SuperPod超算,AI算力新巅峰!

周一&#xff0c;黄教主又很淡定的在自家厨房里开完了GTC发布会众所周知&#xff0c;NLP领域的模型一个比一个大&#xff0c;自从百亿参数的Google T5出来后&#xff0c;大部分AI研究者只能望着手里的蹩脚算力兴叹。如今动辄就是千亿、万亿参数模型&#xff0c;目前比较流行的V…

VS Code HtmlFindClass 插件介绍

这款插件诞生于工作中&#xff0c;在写大量的前端代码之后&#xff0c;发现有的工作比较重复&#xff0c;浪费时间&#xff0c;于是想能不能通过工具来解决。起初是拿Java写的&#xff0c;但是它不利于推广&#xff0c;因为很多前端同学不掌握Java。以至于是一直我自己在使用。…

浅谈智能搜索和对话式OS

原文链接 &#xff1a;https://www.jianshu.com/p/3a9f49834c4a

论文浅尝 - ACL2020 | 一种用于关系三元组提取的级联二进制标记框架

论文笔记整理&#xff1a;窦春柳&#xff0c;天津大学硕士。链接&#xff1a;https://arxiv.org/pdf/1909.03227.pdf动机首先作者提出了问题&#xff0c;传统的关系抽取是不能很好的解决三元组重叠&#xff0c;如下图。从图中可以发现&#xff0c;传统的关系抽取针对Normal 类型…

LeetCode 733. 图像渲染(DFS/BFS)

文章目录1. 题目2. 解题2.1 DFS2.2 BFS1. 题目 有一幅以二维整数数组表示的图画&#xff0c;每一个整数表示该图画的像素值大小&#xff0c;数值在 0 到 65535 之间。 给你一个坐标 (sr, sc) 表示图像渲染开始的像素值&#xff08;行 &#xff0c;列&#xff09;和一个新的颜…

美团外卖Android平台化架构演进实践

美团外卖自2013年创建以来&#xff0c;业务一直高速发展。目前美团外卖日完成订单量已突破1800万&#xff0c;成为美团点评最重要的业务之一。美团外卖的用户端入口&#xff0c;从单一的外卖独立App&#xff0c;拓展为外卖、美团、点评等多个App入口。美团外卖所承载的业务&…

Vue源码探究笔记

对于源代码分析有一个基本原则&#xff1a;要找到它的最早期的版本&#xff0c;比如1.0版本。1.0版本奠定了一款框架的基础结构&#xff0c;之后的版本迭代都是基于这套结构进行更新的。所以掌握了基础结构&#xff0c;那也就掌握了这个框架。这个原则适用于世界上绝大多数事务…

PyCharm设置中文使用官方自带的汉化包

file->setting->plugins->chinese simplified 可参考&#xff1a;https://blog.csdn.net/qq_36513794/article/details/111713663

Jarvis:一个值得关注的多模态端到端人机对话框架,针对所有行业适配

说到应用级的人机对话框架&#xff0c;很多人可能首先想到的是RASA开源项目。不过&#xff0c;今天跟大家简要分享一个功能更为丰富、性能更为强劲的多模对话框架——Jarvis&#xff0c;非常值得对话系统从业者关注一下。Jarvis是英伟达于2019年发布的人机对话服务&#xff0c;…

技术动态 | 知识图谱构建的研究已走入下半场,但大规模落地应用仍需时间

本文转载自公众号&#xff1a;AI前线。作者 | 李冬梅 采访嘉宾 | 唐杰知识图谱是近年来人工智能技术蓬勃发展的核心驱动力之一&#xff0c;已广泛应用在金融、电商、医疗、政务等众多领域&#xff0c;经过短短几年的发展&#xff0c;热度依旧不减&#xff0c;未来&#xff0c;知…

LeetCode 542. 01 矩阵(BFS DP)

文章目录1. 题目2. 解题2.1 BFS2.2 DP动态规划1. 题目 给定一个由 0 和 1 组成的矩阵&#xff0c;找出每个元素到最近的 0 的距离。 两个相邻元素间的距离为 1 。 示例 1: 输入: 0 0 0 0 1 0 0 0 0 输出: 0 0 0 0 1 0 0 0 0示例 2: 输入: 0 0 0 0 1 0 1 1 1 输出: 0 0 0 0…

美团旅行销售绩效系统研发实践

背景 O2O是目前互联网竞争最激烈的领域之一&#xff0c;其重要的业务特征是有大规模的线下业务团队&#xff0c;他们分布在五湖四海&#xff0c;直接服务着数以百万的商家&#xff0c;责任很重&#xff0c;管理的难度巨大。能否通过技术手段&#xff0c;打造高效的线下团队&…

业界大盘点!文本相关性在搜广推三大场景中的应用!

业界大盘点&#xff01;文本相关性在搜广推三大场景中的应用&#xff01;:https://mp.weixin.qq.com/s/kYou_ikuNPr4PsWbJpDKYw