【Android源码系列】消息机制:Handler源码解析

前言

这篇文章本来应该前几天就出炉的,不过笔者当时正看得起劲,头忽然昏的不行,心想难道是撸多了?咳咳..最后脑供血不足去医院检查了一下,做了脑部CT发现很健康,这就奇怪了。后来想想应该是当时门窗都关的太严实了,刚好外面装修的甲醛充满了房间..所以各位在学习的同时一定要锻炼身体啊(好像也没啥因果关系- -),不然什么都是浮云了….
进入正题,之前我们研究四大组件源码时,有一个H类大家印象一定很深刻(滑稽),每次AMS跨进程调用ApplicationThread的Binder方法时,都会调用H跟ActivityThread通信。这个H类就是我们今天的主角:Handler,通过他我们可以很方便的在线程间通信。相信大家对他再熟悉不过了,不过真的把他看透了吗??我们今天就把他扒光了看个清楚。
要了解Handler首先要知道ThreadLocal、Looper、MessageQueue这几个类,我们就一一道来。

二、ThreadLocal

ThreadLocal是一个线程内储存数据类,通过他可以在指定线程内存储数据。一般情况下我们很少见到他,下面我们通过一个栗子来认识认识他:

    private void testThreadLocal(){
        final ThreadLocal<Integer> mThreadLocal = new ThreadLocal<>();
        mThreadLocal.set(1);
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(mThreadLocal.get());

                mThreadLocal.set(2);
            }
        }).start();

        System.out.println(mThreadLocal.get());
    }

会输出什么呢?按理应该是输出
1
2
对吧?答案当然是错误的啦,正确输出应该是
null
1
这就是线程存储的意思了,虽然是同一个ThreadLocal对象,但是在不同线程里他储存着不同的值,我们的Looper就是这样的存储在相应的线程里,也就是我们所熟知的:必须要在每个线程中调用Looper.prepare()后才能使用Handler。
首先来看看他的set方法

    public void set(T value) {
        Thread currentThread = Thread.currentThread();
        Values values = values(currentThread);
        if (values == null) {
            values = initializeValues(currentThread);
        }
        values.put(this, value);
    }

首先获取当前线程,然后用当前线程获取Values对象,这个对象其实是Thread的成员,同时他也是ThreadLocal的内部类,具体作用和map类似,只是算法不一样而已,具体算法大家可以参考ThreadLocal.Values这个类,这里不深入探讨。
最后把ThreadLocal本身作为key,传入的参数作为Value保存了起来。再看看get的实现呢:

    public T get() {
        // Optimized for the fast path.
        Thread currentThread = Thread.currentThread();
        Values values = values(currentThread);
        if (values != null) {
            Object[] table = values.table;
            int index = hash & values.mask;
            if (this.reference == table[index]) {
                return (T) table[index + 1];
            }
        } else {
            values = initializeValues(currentThread);
        }

        return (T) values.getAfterMiss(this);
    }

我们不管具体储存方式,总之就是通过当前线程获取Values这个”map”,然后取出刚刚存储进去的值。
是不是看起来很简单,其实就是通过Thread里的Values来储存数据,你在哪个线程里调用,获取到的就是那个线程的Values,当然get、set操作的也就不是同一个Values了,这就可以解释为什么我们在不同线程获取到的值不同了。
这里简单的了解了一下ThreadLocal的功能和实现,有助于我们理解Handler的实现方式。如果想更深入理解ThreadLocal,笔者这里有一篇推荐文章:Java并发编程:深入剖析ThreadLocal

三、MessageQueue

稍微熟悉一点Handler的童鞋应该知道,Handler大概工作过程就是发送消息,然后把消息储存到队列中,一个一个执行,没有消息的时候就等待。这个队列,就是MessageQueue。
虽然MessageQueue名字是一个队列,但是他其实并不是一个数组或者集合,底层实现就是一个链式的数据结构。他主要有两个方法:enqueueMessage(插入消息)、next(处理消息),我们先来看看如何插入:

    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) {
            if (mQuitting) {
                IllegalStateException e = new IllegalStateException(
                        msg.target + " sending message to a Handler on a dead thread");
                Log.w("MessageQueue", e.getMessage(), e);
                msg.recycle();
                return false;
            }

            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) {
                nativeWake(mPtr);
            }
        }
        return true;
    }

这就是一个将Message插入数据”链”末端的操作,每一个Message都有一个next,这个next就是下一条消息,我们的消息被插入到最后一个Message的next里了。就像这样

这里写图片描述

接下来看看如何处理消息

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();
            }

            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) {
                    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 (false) Log.v("MessageQueue", "Returning message: " + msg);
                        return msg;
                    }
                } else {
                    // 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)) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                if (pendingIdleHandlerCount <= 0) {
                    // No idle handlers to run.  Loop and wait some more.
                    mBlocked = true;
                    continue;
                }

                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("MessageQueue", "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;
        }
    }

稍微有些长,但是其实就是一个无限循环地获取消息的操作,整个for循环里只有两处return(结束循环)的操作,第一个是获取到消息并返回,第二就是mquit为ture,也就是退出操作时。也就是说,当有消息发送过来时,就返回给调用方(其实就是Looper),不然就阻塞。

四、Looper

我们知道,在使用Handler之前,必须调用Looper.prepare,然后还需要调用Looper.loop来启动消息循环。这里面都做了些什么操作?先看prepare

    public static void prepare() {
        prepare(true);
    }

    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对象保存了一个在sThreadLocal里,也就是当前线程里,相当于给Thread绑定了一个Looper。
接着看loop

    public static void loop() {
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        final MessageQueue queue = me.mQueue;

        // 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 (;;) {
            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);
            //...省略部分
        }
    }

1、取出刚刚保存的Looper,Looper里有一个MessageQueue
2、调用MessageQueue的next方法,刚刚我们分析过,如果没有消息,将阻塞,同时这里也会阻塞
3、如果有消息返回调用msg.target.dispatchMessage(msg),其内部就会调用我们平时重写的方法啦,这里先卖个关子,后面再看。
4、处理完成后继续回到第一步,无限循环下去。
这里就可以很好的解释Handler的跨线程通信了,因为Looper.loop在哪里调用,获取的就是那个线程的Looper,也就是那个线程的MessageQueue。什么?还不理解?OK,接下来看完我们今天的主角,相信你就知道了。

五、Handler

先来看看他的构造方法

    public Handler(Callback callback, boolean async) {
        if (FIND_POTENTIAL_LEAKS) {
            final Class<? extends Handler> klass = getClass();
            if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                    (klass.getModifiers() & Modifier.STATIC) == 0) {
                Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                    klass.getCanonicalName());
            }
        }

        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

嗯嗯,果然绑定了当前线程的Looper,也就意味着,无论你在哪里使用Handler,他的Looper都是创建他那个线程里的Looper,当然,消息也就会在那个线程里处理,因为loop方法是永远执行在那个线程里的,跨线程通信也就实现了。是不是豁然开朗?无论你是不是,反正我就当是了。
这里解开了我心中谜团,接下来该看看Handler是如何将消息传过来的。平时我们发消息有两种方式
handler.sendMessage(what)
handler.post(runnable)
查看源码,发现他们最终都走到了enqueueMessage方法,只是post方法给message设置了callback而已。我们先看看enqueueMessage

    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

就干了一件事,把消息插入队列中了,这样我们的Handler工作就完成了。
等等,刚刚留下的坑要填上,msg.target.dispatchMessage(msg)这个方法会调用我们实现的方法,现在来看看

    /**
     * Handle system messages here.
     */
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

我们一句一句来
1、如果msg的callback不为空走handleCallback方法,这个callback刚刚一句说过了,是post方式才传入的,所以如果是post就走这里,启动runnable。
2、mCallback不为空就走mCallback.handleMessage(msg),这个mCallback是什么鬼?其实,Handler还有一种实现方式,大家看文章开头(看个毛啊),算了我再放出来一次:Handler的构造方法

    public Handler(Callback callback, boolean async) {
        //省略了....
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

所以,这个mCallback其实是在创建时传入的,如果你喜欢,你还可以这么写:

                Handler handler =new Handler(new Handler.Callback() {
                    @Override
                    public boolean handleMessage(Message msg) {
                        return false;
                    }
                });

用了这么久的Handler第一次知道他的实现方式还有第三种(可能是笔者太菜),侧面反映出源码的重要性。这个实现方式有一个返回值,如果为true,就不会再去调用handleMessage,反之还会继续调用,仔细想想,Handler这里又给我们多了很多操作空间哦。

3、handleMessage…不说了。

六、总结与扩展

1、使用ThreadLocal将Looper与线程绑定,Handler与Looper绑定,使Handler的dispatchMessage方法始终执行在指定线程里,从而达到调换线程的目的。
2、老生常谈:注意Handler内存泄漏。还要注意的是,由于在子线程开启Looper后会无限循环地去获取消息,所以并不建议在子线程开启,或者说,用完一定要记得回收,调用removeCallbacksAndMessages传入null既可删除所有消息。
Looper的退出:Looper.quit会结束循环接收消息,Looper.quitSafely会处理完消息后自动退出。
3、记得我刚接触Android时,面试官问我:Handler的handleMessage和post方式有什么不同,我当时确实不知,后来面试官告诉我,一个是异步,一个是同步..我当时信以为真。现在仔细想想,同步?WTF?同步你用Handler?
好的,这里我也挖同样的坑,看完源码后大家可以仔细想想,Handler的handleMessage和post方式有什么不同??(滑稽)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值