源码分析Handler中巧妙的设计模式

这里我们就不去讲handler的基本使用了,我们会讲一些网上没提到过的,我们会从内存共享的角度去分析handler的源码。通过这篇文章相信大家能get很多干货。那么现在,我问大家一个问题。

为什么Handler机制能实现各个线程间的内存共享呢?

带这个这个问题,直接进入主题。handler相信所有Android的开发者都会用。我们都知道在Android线程通信中涉及到几个很重要的类:Handler、Message、MessageQueue、Looper、Thread其实还包括ThreadLocal和ActivityThread。下面我给出一个Handler的通信流程。


Handler流程图

结合上面的流程图,我们知道,handler是通过sendXXX和postXXX等一系列方法发送消息的,发送的消息会按时间顺序组装到MessageQueue里面,然后Looper会执行loop方法从MessageQueue里面不断的取消息,取出来通过message的msg.target.dispatchMessage(msg)方法,返回给message指定的handler(可能是一个线程中多个不同的handler,多个线程怎么保证消息的顺序,我们后续会讲到)。

了解了handler的基本运作流程,我们再去具体分析下各个方法的调用者,我们都是知道handler是通过调用handler的sendXXX和postXXX的一些列方法发送信息的,但不管是它们其中的哪个方法,最后都会调用到MessageQueue的enqueueMessage方法去发送消息,将消息放到MessageQueue里,那么既然有发送消息,那么接收消息是怎么接收的呢?就是MessageQueue的next方法,他会去接收消息,从MessageQueue取出消息,那么next方法是谁去调用的呢?其实就是Looper.loop方法里去循环调用的。那么Looper.loop又是谁去调用的呢?其实每个非主线程都必须自己去调用Looper.loop的方法(而且在Looper.loop之前必须先调用looper的prepare方法去获取一个looper)。那么非主线程是自己去调用的,那么主线程里呢?是怎么去调用的呢?我们在使用主线程的handler发送消息的时候,我们并没有进行Looper新建这些一系列的操作啊?那到底怎么回事呢?其实在主线程在自己的mian方法里会去维护一个looper,我们Android的主线程就是ActivityThread这个类,所以我们从这个切入点,具体到源码的分析

    public static void main(String[] args) {
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
        SamplingProfilerIntegration.start();

        // CloseGuard defaults to true and can be quite spammy.  We
        // disable it here, but selectively enable it later (via
        // StrictMode) on debug builds, but using DropBox, not logs.
        CloseGuard.setEnabled(false);

        Environment.initForCurrentUser();

        // Set the reporter for event logging in libcore
        EventLogger.setReporter(new EventLoggingReporter());

        AndroidKeyStoreProvider.install();

        // Make sure TrustedCertificateStore looks in the right place for CA certificates
        final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
        TrustedCertificateStore.setDefaultUserDirectory(configDir);

        Process.setArgV0("");

        Looper.prepareMainLooper();

        ActivityThread thread = new ActivityThread();
        thread.attach(false);

        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }

        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }

        // End of event ActivityThreadMain.
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        Looper.loop();

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

我们进入到ActivityThread源码的main方法,我们注意到两个方法:Looper.prepareMainLooper()和 Looper.loop(),所以我们现在大致知道了,主线程其实是自己内部调用了Looper.prepare和Looper.loop。那么prepareMainLooper里面到底干了什么事情呢?我们进入到Looper的prepare方法里去看看,我们注意到一个关键方法:

    private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));//把新创建的Looper和ThreadLocal关联起来,保证looper的唯一性
    }

我们注意到在主线程在new一个Looper的时候把new的这个Looper通过set方法放到了ThreadLocal里,主线程在初始化Looper对象的时候,为什么要把Looper放进ThreadLocal里面么?我们都知道ThreadLocal是一个线程隔离工具类,其实这样做的目的,就是保证了多线程间的线程隔离。要具体了解ThreadLocal是怎么工作的。我们肯定得进入源码了解下ThreadLocal类,我们直接定位到ThreadLocal类的set方法里

/**
     * Sets the value of this variable for the current thread. If set to
     * {@code null}, the value will be set to null and the underlying entry will
     * still be present.
     *
     * @param value the new value of the variable for the caller thread.
     */
    public void set(T value) {
        Thread currentThread = Thread.currentThread();//拿到当前的线程
        Values values = values(currentThread);//获取这个线程的Values对象
        if (values == null) {
            values = initializeValues(currentThread);
        }
        values.put(this, value);//把threadLocal当作key,Looper当作value存储起来
    }

第一步它是拿到当前的线程currentThread,为什么要先拿到当前线程呢?其实每个线程的内部都会有一个成员变量ThreadLocal.Values localValues;


image.png

我们再继续set里源码的分析,然后通过currentThread获取到当前线程的Values对象,然后把当前的this也就是ThreadLocal和传进来的value也就是我们新建的looper对象put进values里,那么Values对象是什么呢?我们发现Values是ThreadLocal的内部类


image.png

,除了一些int类型的成员变量,我们只发现了一个table数组(其实早些源码版本里是一个map数组,只是这个table数组实现了和map相同的功能),是数组我们会想到数组的增加和删除方法,所以我们再进入Values里面的add方法(其实put方法也是差不多的)看看,

 /**
         * Adds an entry during rehashing. Compared to put(), this method
         * doesn't have to clean up, check for existing entries, account for
         * tombstones, etc.
         */
        void add(ThreadLocal key, Object value) {
            for (int index = key.hash & mask;; index = next(index)) {//根据key获取index下标
                Object k = table[index];
                if (k == null) {
                    table[index] = key.reference;//index未知存储key
                    table[index + 1] = value;//index+1位置存储value
                    return;
                }
            }
        }

我们发现是入参key和value的键值对,我们知道table是数组,那么为什么要用键值对map的这种方式存储呢?(上面说过了,早期版本确实是map来实现的)我们分析下这个add方法,首先他会根据key通过key.hash 获取index下标(.hash通过哈希值获取的,就是保证key的唯一性),然后下标是index的存储key,下标是index+1的存储value,所以我们综上分析,table的数组的确切数据存储方式是以threadLocal当作key,Looper当作value,并且以key1,value1,key2,value2,key3,value3..........】这种顺序存储起来的。现在,我们再回到Looper类的 prepare(boolean quitAllowed) 方法里

private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));//把新创建的Looper和ThreadLocal关联起来,保证looper的唯一性
    }

我们从上面ThreadLocal的源码分析知道ThreadLocal是通过set的方式将sThreadLocal和looper绑定起来的。我们再看到prepare里有一个 if (sThreadLocal.get() != null) {这样的判断,它的意思是通过ThreadLocal去取对应的looper,如果已经存在了,就会抛出"Only one Looper may be created per thread"异常,所以它保证了每个ThreadLocal的values的table数组里存储的looper是唯一的,也就是说ThreadLocal和Looper是一一对应的,不会重复的,又因为每个Thread都会有一个ThreadLocal.Values localValues的成员变量,所以

结论一:不管是主线程还是非主线程,每一个Thread对应了一个唯一的looper。(当然一个线程可以创建多个Handler)。

分析完主线程里的prepareMainLooper方法,我们再进入主线程里的另外一个方法Looper.loop()里去看看,所以我们进入Looper类的looper方法去分析:

public static void loop() {
        final Looper me = myLooper();//保证Looper的唯一性
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        final MessageQueue v = me.mQueue;//保证MessageQueue的唯一性

        // Make sure the identity of this thread is that of the local process,
        // and keep track of what that identity token actually is.
        Binder.clearCallingIdentity();
        final long ident = Binder.clearCallingIdentity();

        for (;;) {
            //死循环里从MessageQuene里取消息,没有消息就会一直等待
            //如果没有要执行的消息,就会一直在等待,所以主线程永远不会退出,有消息就执行消息,没消息就一直等待
            //这就是主线程永远不需要创建handle,但可以一直执行,而且looper一直在工作的原因
            //主线程不是在工作,就是在等待
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

            // This must be in a local variable, in case a UI event sets the logger
            Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }

            msg.target.dispatchMessage(msg);//通过调用target(实际是handle实例)的dispatchMessage传回给handle

            if (logging != null) {
                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
            }

            // Make sure that during the course of dispatching the
            // identity of the thread wasn't corrupted.
            final long newIdent = Binder.clearCallingIdentity();
            if (ident != newIdent) {
                Log.wtf(TAG, "Thread identity changed from 0x"
                        + Long.toHexString(ident) + " to 0x"
                        + Long.toHexString(newIdent) + " while dispatching to "
                        + msg.target.getClass().getName() + " "
                        + msg.callback + " what=" + msg.what);
            }

            msg.recycleUnchecked();//msg会去回收msg
        }
    }

我们看到final Looper me = myLooper();这一句,一开始先是去拿到一个looper对象,我们进入myLooper()看看是怎么拿到的?


 /**
     * Return the Looper object associated with the current thread.  Returns
     * null if the calling thread is not associated with a Looper.
     */
    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();//从ThreadLocal里拿到Looper对象
    }

它就是从sThreadLocal里去拿到的,就是保证了looper对象的唯一性,只要你这个线程new了一次looper,以后不管你在这个线程的哪里再次用到looper,都会去sThreadLocal去拿。


 if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }

如果你的looper是空,会报异常,提示你"No Looper; Looper.prepare() wasn't called on this thread.",小伙伴么,这个异常我们在刚刚开始学习多线程通信的时候是不是常常遇到啊?我们想实现两个子线程的通信,是不是要在其中一个子线程里调用looper.loop方法啊?我们在调用looper.loop之前是不是要先调用Looper.prepare()?所以你看这里,如果你在looper.loop之前不调用Looper.prepare()是会报异常的,我们继续往下看,然后再通过looper对象,拿到MessageQueue对象,我们先去看看这个MessageQueue对象怎么拿到的?进入到looper的构造方法看看

 private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);//保证了MessageQueue的唯一性
        mThread = Thread.currentThread();
    }

MessageQueue就是在Looper的构造方法里创建的,因为looper我们知道在一个线程里是唯一的,所以这样创建MessageQueue的好处就是保证了MessageQueue的唯一性。我们接着看,我们发现取消息是在一个死循环里去取的?那么为什么是一个死循环呢?直接死循循环就是为了保持当前应用执行不退出。


image.png

我们看到ActivityThread执行到最后就会抛出异常,实际上因为looper.loop的死循环,主线程是不会执行到这一步的。我们接下去看,我们看到 死循环里有那么一句代码Message msg = queue.next(); // might block,就是说这句代码kennel会阻塞,具体怎么会阻塞呢?我们后面再去分析messagequeue的收发消息机制(enqueueMessage和next),这个问题就能解答了,现在先把loop的流程看完。我们直接看到msg.target.dispatchMessage(msg);这一句代码,这句代码的意思,就是会把接收到的消息,通过msg的target的dispatchMessage方法,将消息返回给上层的handler,其实这个target对象就是msg的一个成员变量,它是handler类型的,就是为了区分不同消息的来源。最后在看到loop方法里的最后一句代码 msg.recycleUnchecked();//msg会去回收msg,我们现在先想一个问题,在 handler流程里,有那么多msg对象,它是怎么管理的呢?是每次都去new一个msg对象么?我们进入Messenger类的recycleUnchecked方法

/**
     * Recycles a Message that may be in-use.
     * Used internally by the MessageQueue and Looper when disposing of queued Messages.
     */
    void recycleUnchecked() {//
        // Mark the message as in use while it remains in the recycled object pool.
        // Clear out all other details.
        //享元模式(并不是销毁msg,而是把msg里的各种成员变量置为null,下次有新的消息的时候,直接复用
        //,而不是再去new msg,减少了内存碎片的出现,减少了内存抖动,提高了程序的性能
        flags = FLAG_IN_USE;
        what = 0;
        arg1 = 0;
        arg2 = 0;
        obj = null;
        replyTo = null;
        sendingUid = -1;
        when = 0;
        target = null;
        callback = null;
        data = null;

        synchronized (sPoolSync) {
            if (sPoolSize < MAX_POOL_SIZE) {
                next = sPool;
                sPool = this;
                sPoolSize++;
            }
        }
    }

它这里其实就是用到了一个java的设计模式:享元模式 。也就是每次并不是销毁msg,而是把msg里的各种成员变量置为null,下次有新的消息的时候,直接复用,而不是再去new新的msg,我们知道new的过程就是在jvm里开辟一块内存区域的过程,如果每次new一下,是不是会出现很多内存碎片啊?但有人会说,其实虚拟机会去gc回收啊?小伙伴们想,等你要到gc回收的时候其实已经是内存不够用的时候了,我们直接不让内存不够用不是更好么,更能提升app的性能么,所以,享元模式其实就是为了减少了内存碎片的出现,减少了内存抖动,提高了程序的性能。好了loop源码讲完了,为什么了解loop是怎么休眠的,怎么被唤醒的,Message msg = queue.next();这句代码为什么会被阻塞,我们现在直接进入messagequeue的next和enqueueMessage的源码分析

 Message next() {//消费者,取消息是从队伍头部开始取的
        // Return here if the message loop has already quit and been disposed.
        // This can happen if the application tries to restart a looper after quit
        // which is not supported.
        final long ptr = mPtr;
        if (ptr == 0) {
            return null;
        }

        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        int nextPollTimeoutMillis = 0;
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }
            //native函数没进入等待的方法nextPollTimeoutMillis=-1会一直等待,
            //nextPollTimeoutMillis>0会等待nextPollTimeoutMillis时间
            //nextPollTimeoutMillis = 0;不会等待
            nativePollOnce(ptr, nextPollTimeoutMillis);

            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) {
                    // Stalled by a barrier.  Find the next asynchronous message in the queue.
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                if (msg != null) {
                    //如果当前的时间now还没达到消息执行的时间when,就开始等待
                    if (now < msg.when) {//msg.when是当前时间加上消息的延迟时间,在handle发送消息的时候会带过来(SystemClock.uptimeMillis() + delayMillis)
                        // Next message is not ready.  Set a timeout to wake up when it is ready.
                        //等待的时间就是msg.when - now,也就是再经过这些时间再执行这个消息
                        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 {//msg==null没有消息的时候nextPollTimeoutMillis为-1
                    // No more messages.
                    nextPollTimeoutMillis = -1;
                }

                // Process the quit message now that all pending messages have been handled.
                if (mQuitting) {
                    dispose();
                    return null;
                }

                // If first time idle, then get the number of idlers to run.
                // Idle handles only run if the queue is empty or if the first message
                // in the queue (possibly a barrier) is due to be handled in the future.
                if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {//消息为空走这个if
                    //将 mIdleHandlers.size()赋值给pendingIdleHandlerCount,因为大多数情况下 mIdleHandlers.size()是为0的,所以
                    //pendingIdleHandlerCount大多数情况下等于0,也就是大多数情况下会走下面pendingIdleHandlerCount <= 0这个if,
                    //所以主线程大多数是不走这个死循环的会直接continue,直接continue也就是extPollTimeoutMillis为-1
                    //extPollTimeoutMillis为-1传入native等待函数的时候native等待函数会一直等待。
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                if (pendingIdleHandlerCount <= 0) {//消息不为空走这个if,
                    // No idle handlers to run.  Loop and wait some more.
                    mBlocked = true;//阻塞标志置为true
                    continue;//continue就会退出当前的循环继续走循环的下一个,所以循环末尾的 nextPollTimeoutMillis = 0;不会走,所以调用前面的native休眠的方法,进入等待
                }

                if (mPendingIdleHandlers == null) {
                    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
                }
                mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
            }

            // Run the idle handlers.
            // We only ever reach this code block during the first iteration.
            for (int i = 0; i < pendingIdleHandlerCount; i++) {
                final IdleHandler idler = mPendingIdleHandlers[i];
                mPendingIdleHandlers[i] = null; // release the reference to the handler

                boolean keep = false;
                try {
                    keep = idler.queueIdle();
                } catch (Throwable t) {
                    Log.wtf(TAG, "IdleHandler threw exception", t);
                }

                if (!keep) {
                    synchronized (this) {
                        mIdleHandlers.remove(idler);
                    }
                }
            }

            // Reset the idle handler count to 0 so we do not run them again.
            pendingIdleHandlerCount = 0;

            // While calling an idle handler, a new message could have been delivered
            // so go back and look again for a pending message without waiting.
            nextPollTimeoutMillis = 0;
        }
    }
    //生产者:消息是按时间排序的,越早进来的消息(加上消息延迟时间),越排在队伍的前面
boolean enqueueMessage(Message msg, long when) {
   //不接受没有目标处理对象的消息入队(即不收没有收件人的信件),也不接受正在使用中的消息入队
    if (msg.target == null) {
        throw new IllegalArgumentException("Message must have a target.");
    }
    if (msg.isInUse()) {
        throw new IllegalStateException(msg + " This message is already in use.");
    }

    synchronized (this) {
        //若是该队列已经退出,则直接回收消息,只有在quit方法中才设置该属性为true
        if (mQuitting) {
            IllegalStateException e = new IllegalStateException(
                    msg.target + " sending message to a Handler on a dead thread");
            Log.w(TAG, e.getMessage(), e);
            msg.recycle();
            return false;
        }
        //标记正在使用,并设置时间
        msg.markInUse();
        msg.when = when;
        //p指向队列(单链表)的头部
        Message p = mMessages;//这里的队列也是由Message构成的单链表
        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 {
            needWake = mBlocked && p.target == null && msg.isAsynchronous();
            //按照时间顺序(从小到大)将Message插在对应的位置
            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;
        }
        //这里在插入方法中可以看出,只要插入的Message不是异步的,那么
        //needWake的值就会是mBlocked的值,而mBlocked的值会在出队方
        //法next中,当线程阻塞的时候设为True。而这里当有非异步的Message入队时,
        //就会调用nativeWake方法将线程唤醒来处理消息
        if (needWake) {
            nativeWake(mPtr);
        }
    }
    return true;
}

我们看到,不管是next方法和enqueueMessage方法都用了synchronized修饰,其实就是为了保证线程安全。我们想在线程间通信时,消息的next和enqueueMessage是不是可能被多个线程调用啊?例如现在thread1调用主线程的handler发送消息,同时thread2也调用这个handler发送消息,那么怎么保证MessageQueue的顺序呢?怎么保证保消息收发的顺序呢?所以必须加上synchronized修饰。回过头来,那么整个MessageQueue是什么设计模式呢?其实enqueueMessage往MessageQueue里添加消息是一个生产者,next往MessageQueue里拿消息是一个消费者,MessageQueue就是一个仓库,而这个仓库就是由message构成的队列。


image.png

为了保证这个仓库里的所有的消息不变乱,当我往这个仓库里添加消息的时候,这个生产者可能是多个线程的,所以加入了synchronized修饰添加消息的方法enqueueMessage,同时从这个仓库取出消息的时候,为了保证一个消息只被一个线程取出,所以加入了synchronized修饰取出方法next。大方向讲完了,我们现在直接进入next方法的分析阻塞和唤醒机制。我们看到 nativePollOnce(ptr, nextPollTimeoutMillis);这个方法,这个就是线程挂起的native方法,我们发现里面有一个nextPollTimeoutMillis入参 ,其实这个方法的原理是这样的,如果nextPollTimeoutMillis=-1会一直等待;如果nextPollTimeoutMillis>0会等待nextPollTimeoutMillis时间;如果nextPollTimeoutMillis = 0;不会等待,我们再往下看,那是怎么实现线程挂起的呢?
我们先看到 int pendingIdleHandlerCount = -1; ; int nextPollTimeoutMillis = 0;这两个变量一开始pendingIdleHandlerCount为-1;nextPollTimeoutMillis为0;我们看到if (msg != null) {也就是msg不为空,来消息了,它还有一个if (now < msg.when)这个判断。msg.when是什么呢?其实msg.when是当前时间加上消息的延迟时间,在handle发送消息的时候会带过来(SystemClock.uptimeMillis() + delayMillis),这个判断的意思就是如果当前的时间now还没达到消息执行的时间when,然后他把nextPollTimeoutMillis赋值为msg.when - now,也就是消息的延迟时间,else不用说,就是执行这个消息


image.png

我们在看到下面的这段代码,我们的消息不为空,并且还没到消息的执行时间时走下面 if (pendingIdleHandlerCount <= 0) {这个判断,它是把阻塞标志mBlocked置为true(mBlocked这个标志我们在enqueueMessage方法里会讲到,大家留意下),然后再continue,就会退出当前的循环继续走循环的下一个,所以循环末尾的 nextPollTimeoutMillis = 0;不会走,所以调用前面的native休眠的方法,进入线程挂起。我们再分析下上面这个判断, if (pendingIdleHandlerCount < 0&& (mMessages == null || now < mMessages.when)) {看这个意思就是mMessages为空的时候走这个判断,然后把 mIdleHandlers.size()赋值给pendingIdleHandlerCount;因为大多数情况下 mIdleHandlers.size()是为0的,所以pendingIdleHandlerCount大多数情况下等于0,也就是大多数情况下会走下面pendingIdleHandlerCount <= 0这个if,
因为一开始判断我们知道msg为空的时候nextPollTimeoutMillis=-1

所以continue跳出循环走到native等待函数的时候native等待函数会一直等待。
所以经过上面的分析我们得出

结论二:、没有消息的时候。2、消息还没达到发送的时间的时候,线程都会被挂起

分析完next方法,我们再进入enqueueMessage方法看看


 if (p == null || when == 0 || when < p.when) {//当前要执行的消息(队头消息)为空,说明消息已经被取走了
                // New head, wake up the event queue if blocked.
                msg.next = p;
                mMessages = msg;//把新消息赋值给队头消息
                needWake = mBlocked;//如果线程是阻塞的,就把needWake置为true,在下面唤醒线程
            } 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) {
                nativeWake(mPtr);//native函数唤醒主线程,让主线程工作,不再等待
            }

我们直接看到 if (p == null || when == 0 || when < p.when) 这个判断的意思是:当前要执行的消息(队头消息)为空,说明消息已经被取走了,然后会把把新消息赋值给队头消息,然后把阻塞标志mBlocked的值赋给needWake,如果阻塞表示是ture,就会调用nativeWake唤醒线程,让线程不再等待。大家还记得么?在next取出方法的时候,如果没消息或者消息还没到达发送时间我们最后是把mBlocked置为true的,这里加入消息的方法enqueueMessage刚刚好利用了这个标志,如果当前线程的消息又来了,就把线程唤醒。这个配合就实现了整个handler的消息收发机制,也解答了loop里为什么Message msg = queue.next(); // might block这句话可能会阻塞的原因,这个实现机制是不是特别精辟。
到此,整个handler消息的收发机制都讲完了,相信大家都能回答为什么handler机制能实现线程间的内存共享这个问题了。

总结:

  • 涉及的几个重要的类:Handler、Message、MessageQueue、Looper、Thread、ThreadLocal

  • handler:发送Message

  • MessageQueue:存储消息的消息队列,可能会被阻塞

  • Looper:执行Looper.loop方法从MessageQueue里去消息

  • thread:每个线程对应一个Looper,

  • ThreadLocal:线程隔离类,

  • Message:用消息池,实现了消息复用,避免重复创建Message对象,避免内存抖动。

总体流程

例如现在我在子线程发送消息通知主线程更新UI,在子线程里会调用到handler.sendMessage方法,
内部会调用到messageQueue的enqueueMessage方法将消息入队列,因为在UI主线程的main方法里会去调用到Looper.loop方法,
在loop方法里会有一个死循环,一直调用MessageQueue的next方法不断从MessageQueue取出消息。
取出消息后通过handler的dispatchMessage方法返回到主线程的handler的handleMesage方法里。这样就实现了一个子线程向
主线程发送数据的过程。如果是在两个子线程间发送消息的话,记得在新建handler之前要调用Looper.prepare();,在创建完handler实例之后记得再调用Looper.loop();在主线程里不需要调用是因为Android在UI主线程(ActivityThread)的Main方法里已经帮我们做了Looper初始化的一些相关操作。

其实归结到一点就是MessageQueue,这个MessageQueue里每一个节点message是不是一个独立的内存,不管你在主线程还是子线程里创建的message,都是一个独立的线程,它们之间的通信,就是在于共享MessageQueue这片空间,为了实现线程间的隔离,使用了ThreadLocal;为了让MessageQueue能够被多个线程同时调度不会冲突,使用了synchronized线程锁;为了使MessageQueue中的message能够被最大程度的复用,使用了享元设计模式;然后整个MessageQueue的收发消息又是使用了生产者消费者模式。

looper.loop里开启一个死循环,为什么不会发生ANR呢?

如果looper.loop退出了,整个app应用就退出了,Android是由事件驱动的,
整个app就是因为looper.loop这个死循环,在不断的接受事件、处理事件,才有了app的各种功能。
ANR的意思是应用无响应,比如你现在往Looper.loop这个循环了塞了一个耗时操作,这个耗时一直在处理,
然后你又往里面塞了事件,这时候就会造成ANR。所以造成anr的原因是当前消息得不到及时的处理造成的。

looper.loop这个死循环不会耗费很多性能么?

其实在MessageQueue中,所有的消息都是按时间顺序排循的,如果当前的MessageQueue中没有消息,或者说队头的消息还没达到执行的时间,那么这个messageQueue就会被阻塞,释放当前的时间片,如果队头消息的执行时间到了,这个队列又会被唤醒,来执行消息。这个唤醒、阻塞机制是在native函数里也就是C++层里实现的。

大家如果觉得get到干货了,可以关注点赞一下,后续文章我还会继续分享、解读很多面试里特别关注的android源码,感谢大家!

你可能感兴趣的:(源码分析Handler中巧妙的设计模式)