美文网首页
EventBus系列『番外』——认真剖析 『PendingPos

EventBus系列『番外』——认真剖析 『PendingPos

作者: AntCoding | 来源:发表于2019-05-03 14:36 被阅读0次

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

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

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

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

    概念

    什么是队列

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

    Java中的常用队列

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

    知识流程铺垫

    我们先回想一下将事件放入队列的过程,以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();
        }
    }
    
      1. 入栈时先进行判Null,以免Null进入堆栈.
      1. 若是堆栈首次插入元素 A,此时的 headtail 都应该为Null,将需要插入元素A同时赋给 headtail, 那么此时 headtail的内存指针都是指向 A
    • 3.若我们再次执行插入元素 B,此时的headtail都是有值的,那么我们将执行 tail.next = B相当于将元素 B赋予了AA.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系列『番外』——认真剖析 『PendingPos

          本文链接:https://www.haomeiwen.com/subject/cfownqtx.html