从源码角度分析Handler机制和底层实现

我们都知道,Android UI是线程不安全的,如果在子线程中尝试进行UI操作,程序就会崩溃,解决的方案应该也是很清楚,就是创建一个Message对象,然后借助Handler发送出去,之后在Handler的handleMessage()方法中获得刚才发送的Message对象,然后在这里进行UI操作就不会再出现崩溃了。这种处理方式被称为异步消息处理线程。会用,不是我们的最终目标,要弄懂它们的原理才是,只有明白了它的原理,用起来才能得心应手,出了问题也比较容易快速定位。
关于在怎么用真实源码来讲清楚,网上一搜一大把,我不想重复造轮子,我今天就用我的方法来解读源码,我先讲原理。
首先从哪里讲起呢?首先要读懂handler机制,你得知道handler本身是个什么东西,它是个通讯工具,看过抗战题材的影视剧同学都知道,那个嘀嘀哒哒的,电报机。
那它能收命令也能发命令。命令是什么?就是message,那messagequeen顾名思义自然就是命令队列了。注意,不是集合,是队列。就是电报机发出的命令一个个排好队等着,发一个就入队一个,嗯,没错就是那个你们看到的那个queue.enqueueMessage(msg, uptimeMillis);uptimeMillis这个参数是可以空的。
那么,它是怎么出去的呢?肯定不是自己进自己出啊,那还有什么意义呢?需要借助另外一个重要的东西:Looper,看到名字,先不要胆怯,核心代码都是外国人写的,去百度翻译一下先,loop:环、回路、圈的意思,想象一下,再对应前面,我们有消息队列,而且知道它通过handler可以不断入队,那looper肯定就是出队啊,怎么出呢?先别急,我们要得知道它是从怎么来的。这就真的看源码了,ActivityThread中的main()

public static void main(String[] args) {
    SamplingProfilerIntegration.start();
    CloseGuard.setEnabled(false);
    Environment.initForCurrentUser();
    EventLogger.setReporter(new EventLoggingReporter());
    Process.setArgV0("<pre-initialized>");
    Looper.prepareMainLooper();
    ActivityThread thread = new ActivityThread();
    thread.attach(false);
    if (sMainThreadHandler == null) {
        sMainThreadHandler = thread.getHandler();
    }
    AsyncTask.init();
    if (false) {
        Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread"));
    }
    Looper.loop();
    throw new RuntimeException("Main thread loop unexpectedly exited");
}

第七行,Looper.prepareMainLooper()就是制造出一个looper,倒数第二行的 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
            final Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }

            final long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;

            final long traceTag = me.mTraceTag;
            if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
                Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
            }
            final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
            final long end;
            try {
                msg.target.dispatchMessage(msg);
                end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            if (slowDispatchThresholdMs > 0) {
                final long time = end - start;
                if (time > slowDispatchThresholdMs) {
                    Slog.w(TAG, "Dispatch took " + time + "ms on "
                            + Thread.currentThread().getName() + ", h=" +
                            msg.target + " cb=" + msg.callback + " msg=" + msg.what);
                }
            }

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

看到这里就应该知道,这looper就是一个消息处理中心,它负责管理消息队列,负责让消息发出去,msg.target.dispatchMessage(msg)

public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

handleMessage(msg)是不是很熟悉?不用怀疑,就是我们写的那个,它又转回来了。到此,整个流程就完成了。
细心一点的人就会问啦,那个子线程里面的looper是怎么跑到ui线程里面去的呢?
答案就是ThreadLocal

public static final void prepare() {
    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    sThreadLocal.set(new Looper());
}

sThreadLocal.set(new Looper()) 设置了looper

public static final Looper myLooper() {
    return (Looper)sThreadLocal.get();
}

myLooper()就是拿到looper。

推荐阅读更多精彩内容