美文网首页
handler源码分析及手写handler架构

handler源码分析及手写handler架构

作者: Coder_Sven | 来源:发表于2019-08-14 17:04 被阅读0次

    APP启动的入口在ActivityThread类中的main()方法

        public static void main(String[] args) {
            // 1. 为主线程创建1个Looper对象,同时生成1个消息队列对象(MessageQueue)
            // 方法逻辑类似Looper.prepare()
            Looper.prepareMainLooper();
            ...
            //创建了主线程
            ActivityThread thread = new ActivityThread();
            thread.attach(false, startSeq);
            ...
            //开始循环
            Looper.loop();
        }
    

    再看看Looper源码中相关方法

       //Looper构造方法,绑定了一个线程并创建了一个消息队列MessageQueue
        private Looper(boolean quitAllowed) {
            mQueue = new MessageQueue(quitAllowed);
            mThread = Thread.currentThread();
        }
      
        public static void prepareMainLooper() {
            prepare(false);
            ...
            sMainLooper = myLooper();
            ...
        }
        
        private static void prepare(boolean quitAllowed) {
            if (sThreadLocal.get() != null) {
                throw new RuntimeException("Only one Looper may be created per thread");
            }
            //sThreadLocal是一个存储容器,里面存着线程中唯一一个Looper对象
            sThreadLocal.set(new Looper(quitAllowed));
        }    
        
        //取出来sThreadLocal中保存的唯一一个Looper对象
        public static @Nullable Looper myLooper() {
            return sThreadLocal.get();
        }
        
        //循环取读取message
         public static void loop() {
            
            final Looper me = myLooper();      
            final MessageQueue queue = me.mQueue;
            ...
    
            for (;;) {//一个死循环
                //这是一个阻塞方法,当队列中没有消息时会一直阻塞
                Message msg = queue.next(); // might block
                if (msg == null) {
                    // No message indicates that the message queue is quitting.
                    return;
                }
                ...
    
                final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
                final long dispatchEnd;
                try {
                    //==handler.dispatchMessage回调handler中重写的handlemessage方法
                    msg.target.dispatchMessage(msg);
                    dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
                } finally {
                    if (traceTag != 0) {
                        Trace.traceEnd(traceTag);
                    }
                }
                ...
    
                msg.recycleUnchecked();
            }
        }  
        
    

    现在来看看MessageQueue类的源码

       //消息出队,将消息移出消息队列
       Message next() {
            ...
            // 该参数用于确定消息队列中是否还有消息
            // 从而决定消息队列应处于出队消息状态 or 等待状态
            int nextPollTimeoutMillis = 0;
            for (;;) {
                if (nextPollTimeoutMillis != 0) {
                    Binder.flushPendingCommands();
                }
    
                //真正的阻塞方法,native层方法实现  nextPollTimeoutMillis =-1时代表一直阻塞
                nativePollOnce(ptr, nextPollTimeoutMillis);
    
                //下面的代码的作用是计算阻塞时间,延时加入消息队列时间等(msg.when)
                synchronized (this) {
                    // Try to retrieve the next message.  Return if found.
                    final long now = SystemClock.uptimeMillis();
                    Message prevMsg = null;
                    Message msg = mMessages;
                    if (msg != null && msg.target == null) {
                        do {
                            prevMsg = msg;
                            msg = msg.next;
                        } while (msg != null && !msg.isAsynchronous());
                    }
                    if (msg != null) {
                        if (now < msg.when) {
                            // Next message is not ready.  Set a timeout to wake up when it is ready.
                            nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                        } else {
                            // Got a message.
                            mBlocked = false;
                            if (prevMsg != null) {
                                prevMsg.next = msg.next;
                            } else {
                                mMessages = msg.next;
                            }
                            msg.next = null;
                            if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                            msg.markInUse();
                            return msg;
                        }
                    } else {
                        // No more messages.
                        nextPollTimeoutMillis = -1;
                    }
    
                    if (pendingIdleHandlerCount < 0
                            && (mMessages == null || now < mMessages.when)) {
                        pendingIdleHandlerCount = mIdleHandlers.size();
                    }
                    if (pendingIdleHandlerCount <= 0) {
                        // No idle handlers to run.  Loop and wait some more.
                        mBlocked = true;
                        continue;
                    }
    
                }
    
                ...
            
            }
        }
        
         /**分析4:queue.enqueueMessage(msg, uptimeMillis)
              * 作用:入队,即 将消息 根据时间 放入到消息队列中(Message ->> MessageQueue)
              * 采用单链表实现:提高插入消息、删除消息的效率
               */
        boolean enqueueMessage(Message msg, long when) {
         
            //根据消息的延时时间,来将消息加入到队列中去
            synchronized (this) {
                ...
                msg.markInUse();
                msg.when = when;
                Message p = mMessages;
                boolean needWake;
                if (p == null || when == 0 || when < p.when) {
                    // New head, wake up the event queue if blocked.
                    msg.next = p;
                    mMessages = msg;
                    needWake = mBlocked;
                } else {
                    // Inserted within the middle of the queue.  Usually we don't have to wake
                    // up the event queue unless there is a barrier at the head of the queue
                    // and the message is the earliest asynchronous message in the queue.
                    needWake = mBlocked && p.target == null && msg.isAsynchronous();
                    Message prev;
                    for (;;) {
                        prev = p;
                        p = p.next;
                        if (p == null || when < p.when) {
                            break;
                        }
                        if (needWake && p.isAsynchronous()) {
                            needWake = false;
                        }
                    }
                    msg.next = p; // invariant: p == prev.next
                    prev.next = msg;
                }
    
                // We can assume mPtr != 0 because mQuitting is false.
                if (needWake) {
                    //唤醒next()方法中的阻塞
                    nativeWake(mPtr);
                }
            }
            return true;
        }    
        
    

    Message类是一个使用了链表结构来实现的对象池(Message.next)

    package android.os;
    
    import android.os.MessageProto;
    import android.util.TimeUtils;
    import android.util.proto.ProtoOutputStream;
    
    public final class Message implements Parcelable {
    
        public int what;
    
    
        public int arg1;
    
        public int arg2;
    
        public Object obj;
    
        public Messenger replyTo;
    
        public int sendingUid = -1;
    
        /*package*/ static final int FLAG_IN_USE = 1 << 0;
    
        /** If set message is asynchronous */
        /*package*/ static final int FLAG_ASYNCHRONOUS = 1 << 1;
    
        /** Flags to clear in the copyFrom method */
        /*package*/ static final int FLAGS_TO_CLEAR_ON_COPY_FROM = FLAG_IN_USE;
    
        /*package*/ int flags;
    
        /*package*/ long when;
    
        /*package*/ Bundle data;
    
        /*package*/ Handler target;
    
        /*package*/ Runnable callback;
    
        // sometimes we store linked lists of these things
        /*package*/ Message next;//链表连接
    
        ...
        
        /** @hide */
        public static final Object sPoolSync = new Object();
        private static Message sPool;
        private static int sPoolSize = 0;//对象池大小
    
        private static final int MAX_POOL_SIZE = 50;
    
        private static boolean gCheckRecycle = true;
    
        /**
         * Return a new Message instance from the global pool. Allows us to
         * avoid allocating new objects in many cases.
         */
        public static Message obtain() {
            synchronized (sPoolSync) {
                if (sPool != null) {
                    Message m = sPool;
                    sPool = m.next;
                    m.next = null;
                    m.flags = 0; // clear in-use flag
                    sPoolSize--;
                    return m;
                }
            }
            return new Message();
        }
        
        ...
        
        public long getWhen() {
            return when;
        }
    
        public void setTarget(Handler target) {
            this.target = target;
        }
    
        public Handler getTarget() {
            return target;
        }
    
        /*package*/ boolean isInUse() {
            return ((flags & FLAG_IN_USE) == FLAG_IN_USE);
        }
    
        /*package*/ void markInUse() {
            flags |= FLAG_IN_USE;
        }
    
        public Message() {
        }
    }
    
    

    开始使用hanldler发送消息(handler.sendMessage())

       //构造方法
        public Handler() {
            this(null, false);
        }
       
        public Handler(Callback callback, boolean async) {
            ...
            mLooper = Looper.myLooper();
            if (mLooper == null) {
                throw new RuntimeException(
                    "Can't create handler inside thread " + Thread.currentThread()
                            + " that has not called Looper.prepare()");
            }
            mQueue = mLooper.mQueue;
            mCallback = callback;
            mAsynchronous = async;
        }
      
       
       public final boolean sendMessage(Message msg)
        {
            // ->>分析1   
            return sendMessageDelayed(msg, 0);
        }
        
        //分析1:sendMessageDelayed(msg, 0)
        public final boolean sendMessageDelayed(Message msg, long delayMillis)
        {
            if (delayMillis < 0) {
                delayMillis = 0;
            }
            // ->> 分析2
            return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
        }
           
        //分析2:sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis)      
        public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
            MessageQueue queue = mQueue;
            ...
            return enqueueMessage(queue, msg, uptimeMillis);
        }
        
        //分析3:enqueueMessage(queue, msg, uptimeMillis)
        private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis)    {
            // 1. 将msg.target赋值为this
            // 即 :把 当前的Handler实例对象作为msg的target属性
            msg.target = this;
            ...
            // 2. 调用消息队列的enqueueMessage()
            // 即:Handler发送的消息,最终是保存到消息队列->>分析4
            return queue.enqueueMessage(msg, uptimeMillis);
        }
    

    根据上面源码分析总结:

    944365-494e0b26a2724087.png

    常见问题分析

    为什么不能在子线程中更新UI,根本原因是什么?

    在访问UI时,ViewRootImpl会首先调用checkThread()方法检查当前线程是不是UI线程,如果不是UI线程就会抛出异常。那么为什么onCreate里面没有进行这个检查呢。这个问题原因出现在Activity的生命周期中,在onCreate方法中,UI处于创建过程,对用户来说界面还不可视,直到onStart方法后界面可视了,再到onResume方法后界面可以交互。从某种程度来讲,在onCreate方法中不能算是更新UI,只能说是配置UI,或者是设置UI的属性。这个时候不会调用到ViewRootImpl.checkThread(),因为ViewRootImpl没被创建。而在onResume方法后,ViewRootImpl才被创建。这个时候去交互界面才算是更新UI。
    setContentView只是建立了View树,并没有进行渲染工作(其实真正的渲染工作是在
    onResume之后)。也正是建立了View树,因此我们可以通过findViewById()来获取到View对象,但是由于并没有进行渲染视图的工作,也就是没有执行ViewRootImpl.performTransversal。同样View中也不会执行onMeasure(),如果在onResume()方法里直接获取View.getHeight()/View.getWidth()得到的结果总是0。

    为什么主线程用Looper死循环不会引发ANR异常?

    简单说就是在主线程的MessageQueue没有消息时,便阻塞在loop的queue.next()中的nativePollOnce()方法里,
    此时主线程会释放CPU资源进入休眠状态,直到下个消息到达或者有事务发生,
    通过往pipe管道写端写入数据来唤醒主线程工作。这里采用的epoll机制,是一种IO多路复用机制。

    为什么Handler构造方法里面的Looper不是直接new?

    如果在Handler构造方法里面new Looper,怕是无法保证保证Looper唯一,只有用Looper.prepare()才能保证唯一性,具体去看prepare方法。

    MessageQueue为什么要放在Looper私有构造方法初始化?

    因为一个线程只绑定一个Looper,所以在Looper构造方法里面初始化就可以保证mQueue也是唯一的Thread对应一个Looper 对应一个 mQueue。

    手写handler架构代码:
    https://github.com/games2sven/Handler

    相关文章

      网友评论

          本文标题:handler源码分析及手写handler架构

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