Android Handler源码浅析

Android Handler源码浅析

相关对象

  1. Handler可以看作CEO,负责消息的处理和发送,Handler发送消息给MessageQueue,,然后Looper取出其中的消息给Handler。
  2. Looper,Handler的管家,可以看作秘书,负责管理MessageQueue,她会不断的从MessageQueue中取出消息,交给Handler处理。
  3. MessageQueue是存放消息的队列,负责存放Handler发来的消息。

Looper部分源码讲解

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));
}
    static final ThreadLocal sThreadLocal = new ThreadLocal();

Looper实例化是在Looper.prepare()方法中。

小插曲

ThreadLocal是一个和多线程并发相关的类。当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。

ThreadLocal维护了一个Map集合,其中键为当前线程,值就是不同线程的变量。

ThreadLocal中的方法不多。

public T get() {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null)
            return (T)e.value;
    }
    return setInitialValue();
}
public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
}

set方法很容易理解,首先获取当前线程,再用当前线程当键来获取map集合,如果map为空,则重新创建一个map并设置值进去,否则的话,直接设置值进去。get同理。

回到主题。所以Looper的prepare只能获取当前线程的Looper对象,并且如果已经存在一个Looper对象则会报异常,所以这也说明了一个线程中只能存在一个Looper对象。并且prepare只能调用一次。

private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}
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;
        }
        try {
            msg.target.dispatchMessage(msg);
        } finally {
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }
        msg.recycleUnchecked();
    }
}

Looper中创建了一个MessageQueue,并且在Looper的loop方法中,开启无限循环去遍历MessageQueue,如果消息不为空,则将消息发送给Handler。这个msg.target其实就是Handler对象。

我们一般都会重写Handler的handleMessage方法,其中缘由也在源码里。
我们不管sendEmptyMessage也好,postDelayed也罢,最终都会调用到一个方法。

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

msg.target = this。这句话中的this就是Handler对象。
同时把消息放入MessageQueue中。

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

在Looper的loop方法中,她把消息用dispatchMessage传了过来。在dispatchMessage中。优先级有三种。优先级最高的是message自己的callback,然后是Handler的callback,当两者都没有的时候,就由我们重写来处理。这也是我们最常用的一种方法。

那么Looper是什么时候启动的呢?

答案在ActivityThread中。

public static void main(String[] args) {
    //部分代码
    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.prepare()等价。这个ActivityThread也就是我们所说的UI线程,Looper在UI线程启动的时候,就一直在后台默默工作了。

思考,Handler怎么做到线程切换处理对象的?

因为不同线程共享内存,Handler在A线程发送了一个消息,然后主线程的Looper在主线程把消息取了出来,同时交给了Handler,所以Handler一取一存就做到了线程切换。

小结

其实看源码本没有那么复杂,有时候根本不需要抽丝剥茧一条条代码看,代码量很多,难度确实是有的。但是我们只要有一个目标,有一条主线,只寻找最关键的代码,就能很快的理清它的脉络。

你可能感兴趣的:(Android Handler源码浅析)