我们把注册、注销、post和postSticky事件收发都已经剖析完毕,接下来我们讲一下
EventBus
队列的实现思想.
我的其他文章地址,欢迎品读:EventBus系列『一』——注册与注销
EventBus系列『二』——Post与postSticky事件的发布与接收
EventBus系列『番外』——认真剖析 『PendingPostQueue』队列的实现思想
概念
什么是队列
队列是一种数据结构,它支持 FIFO,尾部添加、头部删除(先进队列的元素先出队列
Java中的常用队列
在Java中提供了一个 BlockingQueue<E>
接口,通过实现这个接口来实现队列存储,我们常用的队列有
ArrayBlockingQueue
它在内部维护一个数组,通过对数组元素的增删改查,实现了队列对元素FIFO(先进先出)进行的排序。LinkedBlockingQueue
它在内存维护了一个 Node节点 ,实现队列对元素FIFO(先进先出)进行排序。
知识流程铺垫
我们先回想一下将事件放入队列的过程,以
threadMode = MAIN
为例:
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
switch (subscription.subscriberMethod.threadMode) {
...
case MAIN:
if (isMainThread) {
//直接反射执行
invokeSubscriber(subscription, event);
} else {
//入列操作
mainThreadPoster.enqueue(subscription, event);
}
break;
...
}
进入
HandlerPoster.java
,执行enqueue 函数
,将Event
放入队列中
public void enqueue(Subscription subscription, Object event) {
//将完整Event事件封装成 PendingPost 类型
PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
synchronized (this) {
//入列
queue.enqueue(pendingPost);
if (!handlerActive) {
handlerActive = true;
if (!sendMessage(obtainMessage())) {
throw new EventBusException("Could not send handler message");
}
}
}
}
PendingPostQueue 队列的实现
PendingPostQueue
队列的实现不同于上述两个队列,它的内部既 没有维护数组
,也没有维护Node节点
,同时也没有维护List链表
,那么它是如何实现的呢?
答案是:内存指针
我们先了解它内部维护的变量都有哪些 :
final class PendingPostQueue {
//头部
private PendingPost head;
//尾部
private PendingPost tail;
}
通过源码我们可以看到
PendingPostQueue
队列的头部和尾部都被包装成了PendingPost
类型,我们来看看PendingPost
类的实现:
final class PendingPost {
private final static List<PendingPost> pendingPostPool = new ArrayList<PendingPost>();
Object event;
Subscription subscription;
//内部变量,由于存储下一个PendingPost对象
PendingPost next;
private PendingPost(Object event, Subscription subscription) {
this.event = event;
this.subscription = subscription;
}
//@重点:在将Event事件放入队列之前,我们会执行此方法。
//将完整的Event事件包装成PendingPost对象
static PendingPost obtainPendingPost(Subscription subscription, Object event) {
synchronized (pendingPostPool) {
int size = pendingPostPool.size();
//判定pendingPostPool链表大小
if (size > 0) {
//获取并移除链表中的最后一个元素
PendingPost pendingPost = pendingPostPool.remove(size - 1);
//重新为最后一个元素赋值
pendingPost.event = event;
pendingPost.subscription = subscription;
pendingPost.next = null;
//返回一个包装好的 PendingPost 对象
return pendingPost;
}
}
//new 一个PendingPost对象 进行包装
return new PendingPost(event, subscription);
}
//将 pendingPost元素保存至链表,链表最大不超过 10000
static void releasePendingPost(PendingPost pendingPost) {
pendingPost.event = null;
pendingPost.subscription = null;
pendingPost.next = null;
synchronized (pendingPostPool) {
// Don't let the pool grow indefinitely
if (pendingPostPool.size() < 10000) {
pendingPostPool.add(pendingPost);
}
}
}
}
我们在 知识流程铺垫 可以看到放入队列之前先把完整Event事件封装成
PendingPost
, 即仅执行obtainPendingPost(Subscription subscription, Object event)
函数。
回到 PendingPostQueue.java ,我们看看他是如何实现入栈和出栈的
进栈流程
final class PendingPostQueue {
private PendingPost head;
private PendingPost tail;
synchronized void enqueue(PendingPost pendingPost) {
//判定pendingPost是否为空
if (pendingPost == null) {
throw new NullPointerException("null cannot be enqueued");
}
//尾部不为空,证明队列中已存在其他元素
if (tail != null) {
tail.next = pendingPost; //为tail对象的 next属性赋值
tail = pendingPost; //重新为tail对象赋值
} else if (head == null) { //头部为空,证明队列是空的,进入元素是第一个
head = tail = pendingPost; //同时赋值给头尾
} else {
throw new IllegalStateException("Head present, but no tail");
}
notifyAll();
}
}
出栈流程
final class PendingPostQueue {
private PendingPost head;
private PendingPost tail;
//出栈
synchronized PendingPost poll() {
PendingPost pendingPost = head; //获取头部数据
if (head != null) { //若head不为空,证明队列中还存在数据
head = head.next; //获取下一个数据元素,赋予 head
if (head == null) { //若下一个数据元素为空,证明队列已无元素
tail = null; //将尾部设置为Null
}
}
return pendingPost; //返回pendingPost对象
}
//出栈,指定最长等待时间
synchronized PendingPost poll(int maxMillisToWait) throws InterruptedException {
if (head == null) {
wait(maxMillisToWait); //等待时长
}
return poll();
}
}
第一次执行
poll函数
,获取头部的pendingPost元素,然后进行判断head
值是否为空,若不为Null,获取head.next
值作为下一次的执行poll函数
的head
值,一次类推直到head.next
为Null
时,说明没有了下一元素,也就说到 栈底 了,将tail
置Null
,结束。
简单实例
为了方便了解,我做了一个依照
EventBus
的结构我做了一个Demo小样,供大家参考:
Entry.java
public class Entry {
private String flag;
Entry next;
public Entry(String flag) {
this.flag = flag;
}
public static Entry createEntry(String flag) {
return new Entry(flag);
}
}
EntryControl.java
public class EntryControl {
private Entry head;
private Entry tail;
synchronized void enqueue(Entry entry) {
if (tail != null) {
tail.next = entry;
tail = entry;
} else if (head == null) {
head = tail = entry;
} else {
throw new IllegalStateException("Head present, but no tail");
}
System.out.println("Head HashCode:" + head.hashCode());
System.out.println("Tail HashCode:" + tail.hashCode());
notifyAll();
}
synchronized Entry poll() {
Entry entry = head;
if (head != null) {
head = head.next;
if (head == null) {
tail = null;
}
System.out.println("Current Head HashCode:" + entry.hashCode());
}
return entry;
}
}
Test.java 执行程序
public class Test {
public static void main(String[] args) {
EntryControl entryControl = new EntryControl();
for (int index = 0; index < 5; index++) {
Entry entry = Entry.createEntry("我是第" + index);
entryControl.enqueue(entry);
}
System.out.println("存值完成===========================");
while (true) {
Entry pendingPost = entryControl.poll();
if (pendingPost == null) {
return;
}
// System.out.println().e(TAG,"我获取的记过" + pendingPost.subscription + "," + pendingPost.event);
}
}
}
运行结果
Head HashCode:356573597
Tail HashCode:356573597
Head HashCode:356573597
Tail HashCode:1735600054
Head HashCode:356573597
Tail HashCode:21685669
Head HashCode:356573597
Tail HashCode:2133927002
Head HashCode:356573597
Tail HashCode:1836019240
存值完成===========================
Current Head HashCode:356573597
Current Head HashCode:1735600054
Current Head HashCode:21685669
Current Head HashCode:2133927002
Current Head HashCode:1836019240
如此就清晰明了了.