缘由:
平时工作,因为懒于动笔的原因,也没注重技术和经验的积累,导致之前曾经研究过的问题现在又忘记了,所以要慢慢注重积累,那么就从写作开始,谈谈对工作中碰到的问题进行整理和归纳。
我们都知道,在Android中,想处理事件传递,可以用Handler+MessageQueue+Message+Looper循环,固然是有解决方法,但是这个使用起来不方便,代码写起来也不简洁,同时还必须要理解好Handler+MessageQueue+Message+Looper之间的关系,比如这样的图:
是不是看到觉得头大,理解起来也麻烦,但是这个原理是很有必要去了解的,这个也是基本功之一吧。今天我们来讲讲对于EventBus这个开源库的分析,看看高手代码是如何写的,如何解耦合,对于自身的技术的进步相比也是很有大的帮助吧。
初认识:
因为在工作大量用到Fragment和Activity之间的耦合,需要获取对方某某的实例,自然而然就去寻找事件之间是否有解耦合的库,EventBus就涌现出来了,这个是greenrobot大神写的,多么经典已经无需多说了,github地址:
https://github.com/greenrobot/EventBus,虽然我们会使用,但是也需要了解里面的原理,这样使用的起来也放心,毕竟对于陌生的东西不明白内部情况,就去用,什么时候被坑也会不知道的。
脑补下:
介绍这个库之前,需要认识以下几个名词。
事件(Event):又可称为消息,本文中统一用事件表示。其实就是一个对象,可以是网络请求返回的字符串,也可以是某个开关状态等等。事件类型(EventType)指事件所属的 Class。事件分为一般事件和 Sticky 事件,相对于一般事件,Sticky 事件不同之处在于,当事件发布后,再有订阅者开始订阅该类型事件,依然能收到该类型事件最近一个 Sticky 事件。
订阅者(Subscriber):订阅某种事件类型的对象。当有发布者发布这类事件后,EventBus 会执行订阅者的 onEvent 函数,这个函数叫事件响应函数。订阅者通过 register 接口订阅某个事件类型,unregister 接口退订。订阅者存在优先级,优先级高的订阅者可以取消事件继续向优先级低的订阅者分发,默认所有订阅者优先级都为 0。
发布者(Publisher):发布某事件的对象,通过 post 接口发布事件。
结构图:
从中,我们可以看到这个库整体结构是基于生产者/消费者模式,也可以称呼为发布者/订阅者,显得更亲切。所谓的发布者/订阅者,比如我们日常生活中,小区的通告栏,生产者类似小区居委会,订阅者就是小区里的住户,当你需要小区居委会帮忙的时候,比如你外出了,可以叫居委会里某个大妈帮你拿下快递,那么你需要在居委会那里登记,也可以叫做注册,只有登记了,居委会里的大妈才知道你是我们同一个小区的,也可以认识你了。同时,每当有事情发生,需要通知的时候,需要在通告栏里发布一个通告,现在信息发达了,通告栏可以通过微信公众号来发布,但是也需要每个人去扫一扫,关注下小区的公众号,一发通知,有关注的业主自然就知道通知事情是什么。那么这个EventBus库的实现原理,跟我们日常生活中的微信公众号很类型,首先在使用的时候,我们需要向公众平台注册,注册完之后, 当生产者需要发布信息的时候,平台会帮我们把这些消息推送给订阅者,订阅者根据消息内容,进行不同处理操作。
使用方法流程:
类图:
根据类来分析,从中,我们可以看到EventBus依赖有五个类,分别如下。
SubscriberMethod:这是一个订阅方法类的封装,包含了方法反射的名称,线程模型和事件类型,比如当你向主干注册的时候,这时候就会实例化SubscriberMethod类实例,来存放以onEvent开头的方法,运行的线程模型和自定义的传递事件类型。
SubscriberMethodFinder:在这个类中,我们可以看到为何在定义方法消息接受回调时,会以“onEvent”开头的方法,因为这里有个
private static final String ON_EVENT_METHOD_NAME = "onEvent";
ON_EVENT_METHOD_NAME的常量。里面关键方法是findSubscriberMethods(),具体实现可以去看代码,通过反射的方法,找出订阅者上的以onEvent开头的方法,最终返回的时候SubscriberMethod类的集合,也就是所有事件响应函数。
HandlerPoster:这是继承Handler的请求类,封装了请求队列,整个过程是在队列不断发送请求,直到所有的请求都出队列,也就是全部发送完毕。事件主线程处理,对应ThreadMode.MainThread。enqueue 函数将事件放到队列中,并利用 handler 发送 message,handleMessage 函数从队列中取事件,invoke 事件响应函数处理。
AsyncPoster:这个其实一个Runnable实现类,封装执行的过程,在异步时调用。是一个事件异步线程处理,对应ThreadMode.Async。enqueue 函数将事件放到队列中,并调用线程池执行当前任务,在 run 函数从队列中取事件,invoke 事件响应函数处理。
BackgroundPoster:这个其实一个Runnable实现类,事件 Background 处理,对应ThreadMode.BackgroundThread。enqueue 函数将事件放到队列中,并调用线程池执行当前任务,在 run 函数从队列中取事件,invoke 事件响应函数处理。与 AsyncPoster.java 不同的是BackgroundPoster 中的任务只在同一个线程中依次执行,而不是并发执行。
ThreadMode模型
线程模型共有四类:
- PostThread,默认的ThreadMode,表示在执行Post操作的线程直接调用订阅者的事件响应方法不论该线程是否为主线程(UI线程)。当该线程为主线程时,响应方法不能有耗时操作,否则有卡主线程风险。适用场景:对于是否在主线程执行无要求,但若 Post 线程为主线程,不能耗时的操作。
- MainThread,在主线程中执行响应方法。如果发布线程就是主线程,则直接调用订阅者的事件响应方法,否则通过主线程的 Handler 发送消息在主线程中处理——调用订阅者的事件响应函数。显然,MainThread类的方法也不能有耗时操作,以避免卡主线程。适用场景:必须在主线程执行的操作;
- BackgroundThread,在后台线程中执行响应方法。如果发布线程不是主线程,则直接调用订阅者的事件响应函数,否则启动唯一的后台线程去处理。由于后台线程是唯一的,当事件超过一个的时候,它们会被放在队列中依次执行,因此该类响应方法虽然没有PostThread类和MainThread类方法对性能敏感,但最好不要有重度耗时的操作或太频繁的轻度耗时操作,以造成其他操作等待。适用场景:操作轻微耗时且不会过于频繁,即一般的耗时操作都可以放在这里;
- Async,不论发布线程是否为主线程,都使用一个空闲线程来处理。和BackgroundThread不同的是,Async类的所有线程是相互独立的,因此不会出现卡线程的问题。适用场景:长耗时操作,例如网络访问。
EventBus类介绍
EventBus类是对外暴露的API,包括register(),post(),unregister()方法,配合自定义的EventTypeji s事件响应,即可完成使用过程。
EventBus类有个默认getDefault单例,当然也可以通过EventBusBuilder 或构造函数新建一个EventBus,每个新建的EventBus发布和订阅事件都是相互隔离的,即在一个EventBus对象中发布事件,如果有另外一个EventBus对象,则另外一个EventBus对象的订阅者不会收到该订阅。
关键点:
1,register和unregister,分别表示订阅事件和取消订阅,register 最底层函数有三个参数,分别为订阅者对象、是否是 Sticky 事件、优先级。
register 函数中会先根据订阅者类名去subscriberMethodFinder中查找当前订阅者所有事件响应函数,然后循环每一个事件响应函数,依次执行下面的 subscribe 函数:
第一,通过subscriptionsByEventType得到该事件类型的所有订阅者信息队列,根据优先级把当前订阅者信息插入到订阅者队列subscriptionsByEventType中。
第二,在typesBySubscriber中得到当前订阅者的所有事件队列,将此事件保存到队列typesBySubscriber中,用于后续取消订阅。
第三,检查这个事件是否是 Sticky 事件,如果是则从stickyEvents事件保存队列中取出该事件类型最后一个事件发送给当前订阅者。
发布流程:
post方法,用于发布事件,cancle方法用户取消订阅者订阅的所有事件类型。
post 方法会首先得到当前线程的 post 信息PostingThreadState,其中包含事件队列,将当前事件添加到其事件队列中,然后循环调用 postSingleEvent 函数发布队列中的每个事件。
postSingleEvent 方法会先去eventTypesCache得到该事件对应类型的的父类及接口类型,没有缓存则查找并插入缓存。循环得到的每个类型和接口,调用 postSingleEventForEventType 方法发布每个事件到每个订阅者。
postSingleEventForEventType 方法在subscriptionsByEventType查找该事件订阅者订阅者队列,调用 postToSubscription 函数向每个订阅者发布事件。
结尾:
重点名称解释:
- typesBySubscriber订阅者订阅的事件的保存队列,以 subscriber 为 key,元素为 eventType 的 ArrayList 为 Value。
- currentPostingThreadState当前线程的 post 信息,包括事件队列、是否正在分发中、是否在主线程、订阅者信息、事件实例、是否取消。
- mainThreadPoster、backgroundPoster、asyncPoster事件主线程处理者、事件 Background 处理者、事件异步处理者。
- subscriberMethodFinder订阅者响应函数信息存储和查找类。
- executorService异步和 BackGround 处理方式的线程池。
阅读扩展
源于对掌握的Android开发基础点进行整理,罗列下已经总结的文章,从中可以看到技术积累的过程。