我們把注冊(cè)、注銷堕绩、post和postSticky事件收發(fā)都已經(jīng)剖析完畢,接下來(lái)我們講一下
EventBus
隊(duì)列的實(shí)現(xiàn)思想.
我的其他文章地址,歡迎品讀:EventBus系列『一』——注冊(cè)與注銷
EventBus系列『二』——Post與postSticky事件的發(fā)布與接收
EventBus系列『番外』——認(rèn)真剖析 『PendingPostQueue』隊(duì)列的實(shí)現(xiàn)思想
概念
什么是隊(duì)列
隊(duì)列是一種數(shù)據(jù)結(jié)構(gòu),它支持 FIFO榕茧,尾部添加、頭部刪除(先進(jìn)隊(duì)列的元素先出隊(duì)列
Java中的常用隊(duì)列
在Java中提供了一個(gè) BlockingQueue<E>
接口,通過(guò)實(shí)現(xiàn)這個(gè)接口來(lái)實(shí)現(xiàn)隊(duì)列存儲(chǔ)客给,我們常用的隊(duì)列有
ArrayBlockingQueue
它在內(nèi)部維護(hù)一個(gè)數(shù)組用押,通過(guò)對(duì)數(shù)組元素的增刪改查,實(shí)現(xiàn)了隊(duì)列對(duì)元素FIFO(先進(jìn)先出)進(jìn)行的排序靶剑。LinkedBlockingQueue
它在內(nèi)存維護(hù)了一個(gè) Node節(jié)點(diǎn) 蜻拨,實(shí)現(xiàn)隊(duì)列對(duì)元素FIFO(先進(jìn)先出)進(jìn)行排序。
知識(shí)流程鋪墊
我們先回想一下將事件放入隊(duì)列的過(guò)程桩引,以
threadMode = MAIN
為例:
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
switch (subscription.subscriberMethod.threadMode) {
...
case MAIN:
if (isMainThread) {
//直接反射執(zhí)行
invokeSubscriber(subscription, event);
} else {
//入列操作
mainThreadPoster.enqueue(subscription, event);
}
break;
...
}
進(jìn)入
HandlerPoster.java
缎讼,執(zhí)行enqueue 函數(shù)
,將Event
放入隊(duì)列中
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 隊(duì)列的實(shí)現(xiàn)
PendingPostQueue
隊(duì)列的實(shí)現(xiàn)不同于上述兩個(gè)隊(duì)列坑匠,它的內(nèi)部既 沒有維護(hù)數(shù)組
血崭,也沒有維護(hù)Node節(jié)點(diǎn)
,同時(shí)也沒有維護(hù)List鏈表
,那么它是如何實(shí)現(xiàn)的呢?
答案是:內(nèi)存指針
我們先了解它內(nèi)部維護(hù)的變量都有哪些 :
final class PendingPostQueue {
//頭部
private PendingPost head;
//尾部
private PendingPost tail;
}
通過(guò)源碼我們可以看到
PendingPostQueue
隊(duì)列的頭部和尾部都被包裝成了PendingPost
類型,我們來(lái)看看PendingPost
類的實(shí)現(xiàn):
final class PendingPost {
private final static List<PendingPost> pendingPostPool = new ArrayList<PendingPost>();
Object event;
Subscription subscription;
//內(nèi)部變量夹纫,由于存儲(chǔ)下一個(gè)PendingPost對(duì)象
PendingPost next;
private PendingPost(Object event, Subscription subscription) {
this.event = event;
this.subscription = subscription;
}
//@重點(diǎn):在將Event事件放入隊(duì)列之前咽瓷,我們會(huì)執(zhí)行此方法。
//將完整的Event事件包裝成PendingPost對(duì)象
static PendingPost obtainPendingPost(Subscription subscription, Object event) {
synchronized (pendingPostPool) {
int size = pendingPostPool.size();
//判定pendingPostPool鏈表大小
if (size > 0) {
//獲取并移除鏈表中的最后一個(gè)元素
PendingPost pendingPost = pendingPostPool.remove(size - 1);
//重新為最后一個(gè)元素賦值
pendingPost.event = event;
pendingPost.subscription = subscription;
pendingPost.next = null;
//返回一個(gè)包裝好的 PendingPost 對(duì)象
return pendingPost;
}
}
//new 一個(gè)PendingPost對(duì)象 進(jìn)行包裝
return new PendingPost(event, subscription);
}
//將 pendingPost元素保存至鏈表舰讹,鏈表最大不超過(guò) 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);
}
}
}
}
我們?cè)?知識(shí)流程鋪墊 可以看到放入隊(duì)列之前先把完整Event事件封裝成
PendingPost
, 即僅執(zhí)行obtainPendingPost(Subscription subscription, Object event)
函數(shù)忱详。
回到 PendingPostQueue.java ,我們看看他是如何實(shí)現(xiàn)入棧和出棧的
進(jìn)棧流程
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");
}
//尾部不為空匈睁,證明隊(duì)列中已存在其他元素
if (tail != null) {
tail.next = pendingPost; //為tail對(duì)象的 next屬性賦值
tail = pendingPost; //重新為tail對(duì)象賦值
} else if (head == null) { //頭部為空,證明隊(duì)列是空的桶错,進(jìn)入元素是第一個(gè)
head = tail = pendingPost; //同時(shí)賦值給頭尾
} else {
throw new IllegalStateException("Head present, but no tail");
}
notifyAll();
}
}
- 入棧時(shí)先進(jìn)行判Null,以免Null進(jìn)入堆棧.
- 3.若我們?cè)俅螆?zhí)行插入元素 B院刁,此時(shí)的head 和 tail都是有值的糯钙,那么我們將執(zhí)行
tail.next = B
相當(dāng)于將元素 B賦予了A的A.next
屬性值,那么此時(shí)head.next
的值也就等于B退腥;緊接著我們重新為tail賦新值 B,那么此時(shí)A.next
和head.next
就等于 tail .- 4.我們就如此一直插入數(shù)據(jù)任岸,
tail
永遠(yuǎn)保存最后一個(gè)值,而通過(guò)不斷遍歷head.next
我們就可以難道堆棧里所有的值.實(shí)現(xiàn)了FIFO(先進(jìn)先出)排序原則
出棧流程
final class PendingPostQueue {
private PendingPost head;
private PendingPost tail;
//出棧
synchronized PendingPost poll() {
PendingPost pendingPost = head; //獲取頭部數(shù)據(jù)
if (head != null) { //若head不為空狡刘,證明隊(duì)列中還存在數(shù)據(jù)
head = head.next; //獲取下一個(gè)數(shù)據(jù)元素,賦予 head
if (head == null) { //若下一個(gè)數(shù)據(jù)元素為空,證明隊(duì)列已無(wú)元素
tail = null; //將尾部設(shè)置為Null
}
}
return pendingPost; //返回pendingPost對(duì)象
}
//出棧,指定最長(zhǎng)等待時(shí)間
synchronized PendingPost poll(int maxMillisToWait) throws InterruptedException {
if (head == null) {
wait(maxMillisToWait); //等待時(shí)長(zhǎng)
}
return poll();
}
}
第一次執(zhí)行
poll函數(shù)
享潜,獲取頭部的pendingPost元素,然后進(jìn)行判斷head
值是否為空,若不為Null,獲取head.next
值作為下一次的執(zhí)行poll函數(shù)
的head
值嗅蔬,一次類推直到head.next
為Null
時(shí),說(shuō)明沒有了下一元素剑按,也就說(shuō)到 棧底 了,將tail
置Null
澜术,結(jié)束艺蝴。
簡(jiǎn)單實(shí)例
為了方便了解,我做了一個(gè)依照
EventBus
的結(jié)構(gòu)我做了一個(gè)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 執(zhí)行程序
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,"我獲取的記過(guò)" + pendingPost.subscription + "," + pendingPost.event);
}
}
}
運(yùn)行結(jié)果
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
如此就清晰明了了.