Android消息机制(二) Looper源码阅读

1,174 阅读5分钟

什么是Looper

android源码上Looper类的注释为:

  • 用于为线程运行消息循环的类,在默认情况下线程是没有与其关联的消息循环的;可以通过在线程中调用Looper.prepare()创建一个与线程绑定的消息循环,让其处理消息,直到循环停止。
  • 大多数情况下与Looper(消息循环) 的交互都是通过 Handler类进行的
  • 下述代码展示了一个典型的线程创建Looper的实例:
class LooperThread extends Thread {
    public Handler mHandler;
    
    public void run() {
        Looper.prepare();
        mHandler = new Handler() {
            public void handleMessage(Message msg) {
                //处理传入进来的消息
            }
        }
        Looper.loop();
    }
}

Looper作用

  • 此类用于准备线程的消息循环类(prepare(boolean quitAllowed)) 、开始消息循环(loop())、消息循环的退出(quit() /quitSafely())。

Looper源码解析

在Looper作用中提到了其三个作用和对应的方法,下面分别来看这些方法的实现:

  1. 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) );
    }

上述代码调用了 ThreadLocal.set() 保存了Looper实例

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

Looper的构造方法中对MessageQueue和Thread进行了实例化。

  1. loop()

/**
     * Run the message queue in this thread. Be sure to call
     * {@link #quit()} to end the 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;
        
        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); 
            } 
            msg.recycleUnchecked();
        }
    }

上述代码是经过删减留下的主要逻辑代码,其中可以看到:

  • 获取当前的Looper实例进行非空判断。
  • 获取当前Looper中的MessageQueue。
  • 通过一个死循环调用queue.next方法取出Message
  • 当Message为空时表明MessageQueue正在退出,则跳出循环
  • 否则继续往下通过Message.target获取Handler,调用其dispatchMessage将Message分发给Handler处理
  • 最后调用Message的 recycleUnchecked() 方法将其内容清除并放入消息池中循环使用(节省了Message创建以及销毁的时间,并提高了内存的使用率);
  1. quit() /quitSafely()

/**
     * Quits the looper.
     * <p>
     * Causes the {@link #loop} method to terminate without processing any
     * more messages in the message queue.
     * </p><p>
     * Any attempt to post messages to the queue after the looper is asked to quit will fail.
     * For example, the {@link Handler#sendMessage(Message)} method will return false.
     * </p><p class="note">
     * Using this method may be unsafe because some messages may not be delivered
     * before the looper terminates.  Consider using {@link #quitSafely} instead to ensure
     * that all pending work is completed in an orderly manner.
     * </p>
     *
     * @see #quitSafely
     */
    public void quit() {
        mQueue.quit(false);
    }
  /**
     * Quits the looper safely.
     * <p>
     * Causes the {@link #loop} method to terminate as soon as all remaining messages
     * in the message queue that are already due to be delivered have been handled.
     * However pending delayed messages with due times in the future will not be
     * delivered before the loop terminates.
     * </p><p>
     * Any attempt to post messages to the queue after the looper is asked to quit will fail.
     * For example, the {@link Handler#sendMessage(Message)} method will return false.
     * </p>
     */
    public void quitSafely() {
        mQueue.quit(true);
    }

上述quit()方法会将消息队列中的所有消息移除;而quitSafely()则是将消息队列中所有的延迟消息移除,及时消息继续处理结束后再退出。上述两个方法在调用过后,再调用Handler的sendMessage则会返回false,也就是不能再发送消息了。

  1. 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。

  1. ThreadLocal

    perpare() 中我们看到调用了ThreadLocal的set方法保存了当前线程的Looper实例,下面展示set的代码
/**
     * Sets the current thread`s copy of this thread-local variable
     * to the specified value.  Most subclasses will have no need to
     * override this method, relying solely on the {@link #initialValue}
     * method to set the values of thread-locals.
     *
     * @param value the value to be stored in the current thread`s copy of
     *        this thread-local.
     */
    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

可以看出会先获取当前线程的ThreadLocalMap(此类为ThreadLocal的静态内部类),然后根据判断结果通过不同的方法保存value值;下面展示ThreadLocalMap的set方法


        /**
         * 该数组根据需要调整大小.
         * 该数组长度总是为2的幂数.
         */
           private Entry[] table;
           
        private void set(ThreadLocal<?> key, Object value) {

            // We don`t use a fast path as with get() because it is at
            // least as common to use set() to create new entries as
            // it is to replace existing ones, in which case, a fast
            // path would fail more often than not.

            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();
        }
  • 首先循环Entry数组(Entry为继承弱引用的类)
    • 判断是否有与key相等的值,若有则替换value值并返回;
    • 判断是k值是非为空,若为空则将其替换为key并返回
  • new个新的Entry值赋给tab[i]
  • 上述Entry结构为
        static class Entry extends WeakReference<ThreadLocal<?>> {
            /**
             * The value associated with this ThreadLocal.
             */
            Object value;

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

myLooper() 中我们看到调用了ThreadLocal的get方法获取了当前线程的Looper实例,下面展示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();
    }

可以看出与set()方法类似首先会取出当前线程的ThreadLocalMap,然后将匹配的Entry取出,通过之前代码我们知道Looper的实例赋值给了value的,因此e.value的值即为当前线程的Looper。

总结

  • 阅读Looper源码得知,我们可以通过Looper与Handler的组合让线程一直保持着待命的状态,当有消息时能及时处理消息。
  • looper是存储在当前线程的ThreadLocalMap中的,ThreadLocal的get和set方法都是操作当前线程的ThreadLocalMap的,因此在不同的线程中调用ThreadLocal的get和set方法操作的对象是不同的。