Handler、Looper、MessageQueue

Posted by Jfson on 2017-05-15

源码:Handler、Looper、MessageQueue 初始化

  • 1.在 UI 线程创建 Handler,通常直接new Handler;

    1
    2
    3
    4
    5
    6
    private Handler mHandler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
    super.handleMessage(msg);
    }
    }

    UI Thread 初始Handler化时 对Looper进行初始化过程, main()是在 UI Thread 启动时调用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    public static void main(String[] args) {
    SamplingProfilerIntegration.start();
    CloseGuard.setEnabled(false);
    Environment.initForCurrentUser();
    EventLogger.setReporter(new EventLoggingReporter());
    Security.addProvider(new AndroidKeyStoreProvider());
    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"));
    }
    Looper.loop();
    throw new RuntimeException("Main thread loop unexpectedly exited");
    }

    查看一下代码,主要关注一下:Looper.prepareMainLooper();

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    public static void prepareMainLooper() {
    prepare(false);
    synchronized (Looper.class) {
    if (sMainLooper != null) {
    throw new IllegalStateException("The main Looper has already been prepared.");
    }
    sMainLooper = myLooper();
    }
    }
    public static void prepare() {
    prepare(f);
    }
    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));
    _ }

    以上是UI Thread 初始化new Handler 调用过程

  • 2.接下来看一下 Other Thread 初始化调用。

    1
    2
    3
    4
    5
    6
    7
    Looper.prepare();
    private Handler mHandler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
    super.handleMessage(msg);
    }
    }

    查看下Looper.prepare();

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    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));
    _ }
  • 3.其他用法: 在其他地方需要用到Handler,并且需要刷新UI时,不通过Looper.prepare();调用,通过Looper.getMainLooper()也可以;
` Handler mHandler = new Handler(Looper.getMainLooper());

class Looper{
    public static Looper getMainLooper() {
        synchronized (Looper.class) {
            return sMainLooper;
        }
    }
} `
  • 4.MessageQueue 初始化

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

    Looper 在初始化时创建一个关联MessageQueue,一个线程中对应一个Looper & MessageQueue

  • Handler 初始化

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    // 常用构造
    public Handler(Callback callback) {
    this(callback, false);
    }
    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();
    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;
    }
从这里我们基本可以看到Handler 初始化时,关联了线程唯一的Looper & MessageQueue。
  • UI Thread 和 其他 Thread 初始完Looper和MessageQueue后,会调用Looper.loop(),来轮询分发消息。

  • 5.梳理一下调用关系,来张流程图理解一下;

    • UI Thread:
      ActivityThread.main() –>Looper.prepareMainLooper() –> prepare(false) –> new Looper(quitAllowed) –> new MessageQueue(quitAllowed)

    • Other Thread:
      prepare() –> prepare(true) –> new Looper(quitAllowed) –> new MessageQueue(quitAllowed)

Handler-Looper-MessageQueue流程图

异步消息

  • 1.调用,存储消息

    `
    mHandler.sendMessage(new Message());
    mHandler.post();
    mHandler.postDelay();
        `
    

    追踪一下不难发现,最后都走的一个地方

1
2
3
4
5
6
7
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;msg.target就是Handler自己,而MessageQueue就是Looper中关联的对象,而enqueueMessage()中是对message保存,进行Message.next()按时间排序。
  • 2.消费
    Looper.loop()是对MessageQueue的消费
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
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
Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
msg.target.dispatchMessage(msg);
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();
}
}

看到loop()中,添加了一个死循环,不断去轮训MessageQueue中的队列是否为null,返回或者取出来继续执行 msg.target.dispatchMessage(msg);在最开始我们看到msg.target就是Handler本身

1
2
3
4
5
6
7
8
public static Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
if (msg.what == 101) {
Log.i(TAG, "接收到handler消息...");
}
}
};

而handleMessage就是我们重写的回调方法。

postDelay是如何delay的?

MessageQueue 就是消息队列,即存放多条消息 Message 的容器,它采用的是单向链表数据结构,而非队列。它的 next() 指向链表的下一个 Message 元素。

从入队消息 enqueueMessage() 的实现来看,它的主要操作其实就是单链表的插入操作,这里就不做过多的解释了,我们可能应该更多的关心它的出队操作方法 next():

next() 方法其实很长,不过我们仅仅贴了极少的一部分,可以看到,里面不过是有一个 for (;;) 的无限循环,循环体内部调用了一个 nativePollOnce(long, int) 方法。这是一个 Native 方法,实际作用是通过 Native 层的 MessageQueue 阻塞当前调用栈线程 nextPollTimeoutMillis 毫秒的时间。

下面是 nextPollTimeoutMillis 取值的不同情况的阻塞表现:

  • 1.小于 0,一直阻塞,直到被唤醒;
  • 2.等于 0,不会阻塞;
  • 3.大于 0,最长阻塞 nextPollTimeoutMillis 毫秒,期间如被唤醒会立即返回。

可以看到,最开始 nextPollTimeoutMillis 的初始化值是 0,所以不会阻塞,会直接去取 Message 对象,如果没有取到 Message 对象数据,则直接会把 nextPollTimeoutMillis 置为 -1,此时满足小于 0 的条件,会被一直阻塞,直到其他地方调用另外一个 Native 方法 nativeWake(long) 进行唤醒。如果取到值的话,会直接把得到的 Message 对象进行返回。

原来,nativeWake(long) 方法在前面的 MessageQueue#enqueueMessage 方法有个调用,调用时机是在 MessageQueue 入队消息的过程中。

现在已经知道:Handler 发送了 Message,消息用 MessageQueue 进行存储,使用 MessageQueue#enqueueMessage 方法进行入队,使用 MessageQueue#next 方法进行轮训消息。这就不免抛出了一个问题,MessageQueue#next 方法是谁调用的?没错,就是 Looper。

总结:

  • 1.looper内的for不断去取MessageQueue 队列的数据
  • 2.MessageQueue的 next() 不断拿消息时会读取delayTime来调用native做消息阻塞(主线程不会anr,说明这个native应该是个类似计时器的东西)
  • 3.一张图梳理一下流程
    流程图

pv UV: