EventBus系列『番外』——认真剖析 『PendingPostQueue』队列的实现思想

我们把注册、注销、post和postSticky事件收发都已经剖析完毕,接下来我们讲一下EventBus队列的实现思想.
我的其他文章地址,欢迎品读:

EventBus系列『一』——注册与注销

EventBus系列『二』——Post与postSticky事件的发布与接收

EventBus系列『番外』——认真剖析 『PendingPostQueue』队列的实现思想

概念

什么是队列

队列是一种数据结构,它支持 FIFO,尾部添加、头部删除(先进队列的元素先出队列

Java中的常用队列

在Java中提供了一个 BlockingQueue接口,通过实现这个接口来实现队列存储,我们常用的队列有

  • 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 pendingPostPool = new ArrayList();

    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();
    }
}
    1. 入栈时先进行判Null,以免Null进入堆栈.
    1. 若是堆栈首次插入元素 A,此时的 head 和 tail 都应该为Null,将需要插入元素A同时赋给 head 和 tail, 那么此时 head 和 tail的内存指针都是指向 A 的
  • 3.若我们再次执行插入元素 B,此时的head 和 tail都是有值的,那么我们将执行 tail.next = B相当于将元素 B赋予了A的A.next属性值,那么此时 head.next的值也就等于B;紧接着我们重新为tail赋新值 B,那么此时 A.nexthead.next 就等于 tail .
  • 4.我们就如此一直插入数据,tail永远保存最后一个值,而通过不断遍历head.next我们就可以难道堆栈里所有的值.实现了FIFO(先进先出)排序原则
出栈流程
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.nextNull时,说明没有了下一元素,也就说到 栈底 了,将tailNull,结束。

简单实例

为了方便了解,我做了一个依照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

如此就清晰明了了.

This ALL! Thanks EveryBody!

你可能感兴趣的:(EventBus系列『番外』——认真剖析 『PendingPostQueue』队列的实现思想)