Android 源码阅读 | Looper

515 阅读3分钟

Android 源码阅读.png

相关推荐:

类注释

Class used to run a message loop for a thread.  Threads by default do
not have a message loop associated with them; to create one, call
{@link #prepare} in the thread that is to run the loop, and then
{@link #loop} to have it process messages until the loop is stopped.

<p>Most interaction with a message loop is through the
{@link Handler} class.
  • Looper类通常用户为线程提供消息循环。
  • 线程默认下不会有消息循环,为了创建它,在线程里面调用 Looper.prepare ,接着调用 Looper.loop ,进行处理消息,直到循环停止。
  • 大多数与消息循环的交互都是通过 Handler 类进行的。
* <p>This is a typical example of the implementation of a Looper thread,
  * using the separation of {@link #prepare} and {@link #loop} to create an
  * initial Handler to communicate with the Looper.
  *
  * <pre>
  *  class LooperThread extends Thread {
  *      public Handler mHandler;
  *
  *      public void run() {
  *          Looper.prepare();
  *
  *          mHandler = new Handler() {
  *              public void handleMessage(Message msg) {
  *                  // process incoming messages here
  *              }
  *          };
  *
  *          Looper.loop();
  *      }
  *  }</pre>

在类注释里面,有一个使用 Looper 的例子,其核心是调用两个方法:Looper.prepare();Looper.loop();

prepare() & loop()

prepare()

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

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

在prepare方法中,对 ThreadLocal进行初始化,我们知道,ThreadLocal用于保存某个线程共享变量:对于同一个static ThreadLocal,不同线程只能从中get,set,remove自己的变量,而不会影响其他线程的变量。就是说,不同线程中,此处的 sThreadLocal 会是一个不同的变量。

而,prepare 方法还限制了,一个线程中只能有一个 looper。

loop()

public static void loop() {
   final Looper me = myLooper();
   //省略...
   for (;;) {
       Message msg = queue.next(); // might block
       //省略...
       // 1. msg.target,其实就是 handler 对象,调用分发方法
       try {
           msg.target.dispatchMessage(msg);
       } finally {
           if (traceTag != 0) {
               Trace.traceEnd(traceTag);
           }
       }
       //省略...
       // 2. 分发完成后,直接 recycle 回收 messageBean
       msg.recycleUnchecked();
   }
}
  • loop 调用后,进入一个for循环,无限循环
  • Message msg = queue.next(); 阻塞当前线程,直到获取到一个需要处理的消息 msg
  • 获取到待处理消息后,执行 msg.target.dispatchMessage(msg); 把消息分发到对应的handler上,回调 handler 的 handleMessage 方法,完成对消息的处理
  • 最后,回收消息 msg.recycleUnchecked();

构造方法

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

私有化构造函数,并且保留当前的线程引用和初始化一个 MessageQueue 消息队列。

prepareMainLooper()

private static Looper sMainLooper;  // guarded by Looper.class

/**
 * Initialize the current thread as a looper, marking it as an
 * application's main looper. The main looper for your application
 * is created by the Android environment, so you should never need
 * to call this function yourself.  See also: {@link #prepare()}
 */
public static void prepareMainLooper() {
    prepare(false);
    synchronized (Looper.class) {
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        sMainLooper = myLooper();
    }
}

定义了一个 sMainLooper ,这就是主线程的Looper,和上面子线程的looper是一样的初始化逻辑。

其他

quit方法是结束队列的循环,调用的是 mQueue 里面的方法,可以看 MessageQueue 里面的源码分析。

public void quit() {
    mQueue.quit(false);
}
public void quitSafely() {
    mQueue.quit(true);
}

总结

  • Looper是循环者,带动着整个MessageQueue进行循环
  • 通过ThreadLocal巧妙地让每一个字线程拥有自己唯一的Looper
  • Looper结束队列循环需要调用quit方法,quit方法会直接丢弃未到合适时间处理的消息。

码字不易,方便的话素质三连,或者关注我的公众号 技术酱,专注 Android 技术,不定时推送新鲜文章,如果你有好的文章想和大家分享,欢迎关注投稿!

技术酱