手把手带你解析Handler源码

Handler概述

关于Handler的使用无非就是在线程1中发送一个message,线程2接收到到这个消息便会在线程2里执行任务代码。而咱们使用Handler最主要的目的就是在子线程中更新主线程的UI。因为AndroidUI是单线程模型,因此只有在主线程中才可以去更新UI,不然系统就会崩溃抛出异常。对于Handler的使用我这里就不在重复了,本次分析所使用的是Android8.1的源码,和旧版源码略有不一样,但大体思想是同样的,接下里就直接进入主题吧。面试

Handler源码分析

首先从Handler的构造方法开始数组

public Handler() {
        this(null, false);
    }
复制代码

调用了两个参数的构造方法。bash

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();//此处经过Looper.myLooper()获取一个Looper
        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.myLooper()去获取一个Looper,接着判断若是Looper为null的话,就抛出一个异常:async

"Can't create handler inside thread that has not called Looper.prepare()"ide

该异常告诉咱们没有调用Looper.prepare(),因此咱们在建立Handler以前必须调用该方法。 接着为Handler的成员变量mQueue赋值,所赋的值就是咱们从获取的Looper中取出来的。 接着咱们跳转进Looper.myLooper()看看到底是如何获取Looper的。oop

public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }
复制代码

经过一个ThreadLocal的对象去获取一个Looper,那这ThreadLocal是什么呢?源码分析

public class ThreadLocal<T> 
复制代码

ThreadLocal是一个泛型类,能够存储每一个线程独有的变量。好比你在线程1中经过ThreadLocal对象的set方法存储了一个String变量“abc”,在线程2中存储一个String变量“def”,那么在这两个线程中经过ThreadLocal对象的get方法获取该变量时会由于在不一样的线程中获取不一样的值。在线程1中就会获得“abc”,线程2中就会获得“def”。 因而可知,经过ThreadLocal去获取的Looper也是线程独有的,不一样的线程拥有不一样的Looper。 接着咱们看看ThreadLocal的set方法。ui

public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);//获取ThreadLocalMap
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }
复制代码

首先经过Thread.currentThread()获取当前线程,接着把当前线程传入getMap方法来获取一个当前线程的ThreadLocalMap对象。this

ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }
复制代码

getMap方法至关简洁,就是经过返回某线程Thread上的成员变量threadLocals来获取ThreadLocalMap的。也就是说每一个Thread内部都持有一个ThreadLocalMap对象用来存放线程间独立的变量。 而ThreadLocalMap其实就是ThreadLocal的一个静态内部类。在ThreadLocalMap的内部有一个数组。spa

private Entry[] table;
复制代码
static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
            Object value;

            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }

复制代码

一个弱引用对象的数组。每一个Entry对象持有一个ThreadLocal的弱引用,而值存放在value字段上。因此ThreadLocalMap存放了当前线程的独立变量,他们全都放在table字段中。

接着上面的 代码,若是map不为null的话就把值设置在这个ThreadLocalMap对象里 咱们来看看ThreadLocalMap的set方法。

private void set(ThreadLocal<?> key, Object value) {

            Entry[] tab = table;
            int len = tab.length;
            int i = key.threadLocalHashCode & (len-1);

            for (Entry e = tab[i];
                 e != null;
                 e = tab[i = nextIndex(i, len)]) {
                ThreadLocal<?> k = e.get();

                if (k == key) {
                    e.value = value;
                    return;
                }

                if (k == null) {
                    replaceStaleEntry(key, value, i);
                    return;
                }
            }

            tab[i] = new Entry(key, value);
            int sz = ++size;
            if (!cleanSomeSlots(i, sz) && sz >= threshold)
                rehash();
        }
复制代码

根据ThreadLocal的hash值和数组的长度作与运算获得的下标来做为value存放的位置。 若是下标所处的位置不为空,那说明当前下标不能够存放value,那就调用nextIndex来取得下一个下标,若是下一个下标所处的位置是null,那么就能够把value存放在当前下标位置。大体逻辑就是这样的。 接下来咱们再看看ThreadLocal的get方法。

public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }
复制代码

首先经过getMap获取到ThreadLocalMap,接着经过ThreadLocalMap.getEntry这个方法获取到存放在ThreadLocal当中的值。 到此咱们能够作一下总结:

在每一个Thread的内部都有一个ThreadLocalMap,这个ThreadLocalMap里有一个名为table的Entry数组,因此ThreadLocal里存放的变量才独立于每一个线程。咱们往ThreadLocal里存放的对象都是存放进这个Entry数组的(经过hash计算来决定下标值)。 因此把Looper存放在ThreadLocal当中就能够保证每一个线程的Looper是独立存在的。

当Handler获取了Looper之后就能够正常工做了。咱们使用Handler时通常会调用Handler的sendMessage方法

public final boolean sendMessage(Message msg){
        return sendMessageDelayed(msg, 0);
}
复制代码

咱们跳进sendMessageDelayed,发现最终会调用

public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        MessageQueue queue = mQueue;
        if (queue == null) {
            RuntimeException e = new RuntimeException(
                    this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
            return false;
        }
        return enqueueMessage(queue, msg, uptimeMillis);
    }
复制代码

这里的mQueue就是咱们构造方法里Looper的mQueue,因此Looper是必须的。 看看最后一行enqueueMessage。

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

在这里msg.targe指向了this,也就是咱们当前的Handler。 发现最后是调用的MessageQueue的enqueueMessage,咱们继续跟踪。

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(TAG, 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) {
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                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;
            }
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }
复制代码

说是消息队列,其实MessageQueue是个链表,由于对于链表来讲插入和删除操做的时间复杂度是O(1),因此很是适合用来作消息队列,在enqueueMessage方法里把新消息插入队尾,也就是下面这两行代码。

msg.next = p; 
prev.next = msg;
复制代码

咱们经过sendMessage方法把一个Message放进消息队列中,那么是谁来处理消息队列中的消息呢。那就须要Looper登场了,前面说过一个线程若是须要建立一个Handler那么就必需要有一个Looper。咱们来看看Looper的构造方法。

private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }
复制代码

在Looper的构造方法里会新建一个MessageQueue,因此这个MessageQueue和Looper是绑定了的,同时会获取建立Looper的那个线程。要使用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;
        Binder.clearCallingIdentity();
        final long ident = Binder.clearCallingIdentity();

        for (;;) {
            Message msg = queue.next(); // 从消息队列中获取消息
            if (msg == null) {
                return;
            }

            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);
            }
            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();
        }
    }
复制代码

调用loop方法后会启动一个无限循环,每次循环都会调用以下方法。从消息队列中获取一个Message,若是消息队列中没有消息能够取了,该方法可能会阻塞。

Message msg = queue.next();
复制代码

接着会调用

msg.target.dispatchMessage(msg);
复制代码

这个target就是以前enqueueMessage时设置的handler,经过调用handler的dispatchMessage执行咱们的任务。

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

若是建立Message时传入了callback那就调用handleCallback执行任务,不然查看mCallback是否能够调用,能够则调用。mCallback是继承Handler时可选的传入参数。

到此一个完整的Handler机制原理就讲解完毕了。

Handler流程图.png

前面说过一个线程中必需要有一个Looper才能使用Handler,不然就会奔溃。那为何咱们在主线程中可使用Handler呢?这是由于主线程ActivityThread已经帮咱们作好了Looper的初始化工做。

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

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

        Process.setArgV0("<pre-initialized>");

        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");
    }
复制代码

使用了Looper.prepareMainLooper();来初始化Looper,并使用了Looper.loop()开启了消息循环队列。 还有一点须要注意的是,在使用Handler的时候,若是消息都处理完毕了,应该调用Loop.quit或者Looper.quitSafely方法来中止轮询。不然因为Looper会不断的轮询MessageQueue致使该Looper所在的线程没法被释放。

总结

Handler在近几年来已经算是很是基础的知识了,在面试当中也是高频题,关于Handler的使用实际上是很容易形成内存泄漏的,这里就不在多说了,有兴趣的朋友能够搜索一下Handler内存泄漏相关的知识。

相关文章
相关标签/搜索