0%

Handler 源码分析

背景知识

进程与线程

类似其他操作系统的相应概念,Android 的进程拥有独立的虚拟内存空间,而线程与同一进程内的其他线程共享内存,只拥有各自的栈空间、程序计数器和寄存器等少量独占资源。
当 Android 启动一个之前未运行的应用的组件时,会为其启动一个包含单个主线程"main"的新的 Linux 进程。默认情况下,同一应用的所有组件会在此进程内启动,并且使用同一执行线程。
开发者可以人为安排组件在单独的进程中运行,并且为进程创建额外的线程。

Android 线程

每个 Android 应用都有一个主线程,也称界面线程,负责绘制 UI,处理用户交互以及接收生命周期事件。Android UI 操作不是线程安全的,这些操作必须在主线程执行。
为了不拖慢主线程,任何长时间运行的计算和操作都应在后台线程完成,避免 ANR。使用多线程还可以充分利用多核处理器的优势,通过并行提高运行速度。

  1. 创建线程的方法
    1. Java 的线程方法
      1. 重载 Thread.run() 方法
      2. 实现 Runnable 类
    2. Android 的线程方法
      1. AsyncTask
      2. IntentService
      3. HandlerThread
      4. Executors 中创建线程池 (newFixedThreadPool, newScheduledThreadPool, newWorkStealingPool)

从其他线程访问主线程的方法

  1. Activity.runOnUiThread(Runnable)
  2. View.post(Runnable)
  3. View.postDelayed(Runnable, long)
  4. 使用 Handler

Handler 介绍

Handler 是 Android 系统线程管理框架的一部分,使用消息队列实现线程之间的通信。
Handler 允许你发送和处理关联到线程的 MessageQueueMessageRunnable 对象。新的 Handler 会绑定到创建者线程的消息队列上。

消息驱动机制的四要素:

  1. 接收消息的“消息队列”:MessageQueue
  2. 阻塞式地从消息队列中接收消息并进行处理的“线程”:Thread & Looper
  3. 可发送的“消息的格式”: Message
  4. “消息发送函数”: Handler.post(Runnable) & Handler.sendMessage(Message)

Handler 的两大主要作用:

  1. 安排 MessageRunnable 在未来某个时刻运行。
  2. 安排在其他线程中执行的动作。

使用 Handler:

  • 接收者线程:

    1. 如果是子线程,判断其是否已有 Looper,如果没有,运行 Looper.prepare()
    2. 实例化 Handler 对象 mHandler,实现 handleMessage 方法(重载方法或实现 Callback 接口)。
    3. 如果接收者是子线程,调用 Looper.loop() 方法,使得 Looper 调用 handleMessage 方法对消息进行处理;如果是主线程,启动时已经调用了 Looper.prepare() Looper.loop() 方法。
    4. 如果子线程不需要再处理消息,调用 Looper.myLooper().quit() 退出消息轮询
  • 发送者线程:

    1. 创建 Message 对象,设置 Message 的参数
    2. 使用 mHandler.sendMessage(Message) 方法将 Message 传入 Handler 的消息队列

Handler 常用方法:post(Runnable), postAtTime(Runnable, long), postDelayed(Runnable, Object, long), sendEmptyMessage(int), sendMessage(Message), sendMessageAtTime(Message, long), sendMessageDelayed(Message, long)

Looper 介绍

Looper 负责处理 MessageQueue 中的 Message。

一个线程内最多只有一个 Looper 对象,否则会抛出异常。

使用 Looper:
1. 判定是否已有 LooperLooper.prepare()
2. 做一些准备工作
3. 调用Looper.loop(),线程进入阻塞态

Handler 源码分析

Handler 相关类型

Handler

frameworks/base/core/java/android/os/Handler.java

Handler 工作原理

重要成员变量:

1
2
3
4
5
6
7
8
@UnsupportedAppUsage
final Looper mLooper;
final MessageQueue mQueue;
@UnsupportedAppUsage
final Callback mCallback;
final boolean mAsynchronous;
@UnsupportedAppUsage
IMessenger mMessenger;

构造方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public Handler(@Nullable Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {...}

mLooper = Looper.myLooper();
if (mLooper == null) {...}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
@UnsupportedAppUsage
public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}

第一种构造方法首先获取当前线程的 Looper。如果当前线程没有初始化 Looper,则会抛出异常。第二种构造方法直接传入要使用的 Looper 作为参数。之后初始化 mCallbackmAsynchronous 成员。

其他方法

post 系列方法会调用 getPostMessage 方法将 Runnable 包装为 Message,然后使用 sendMessage 系列方法发送。

sendMessage 系列方法最终会调用 enqueueMessage 私有方法,最后调用 MessageQueue.enqueueMessage 方法加入消息队列。

removeMessagesremoveCallbacks 系列方法会调用 MessageQueue.removeMessages 方法从消息队列中移除指定消息。

obtainMessage 系列方法会调用 Message.obtian 系列方法,从消息池中获取一个 Message 对象。

1
2
3
4
5
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}

getPostMessage 方法将 Runnable 对象赋值给 Message 对象的 callback 成员变量,从而将其包装为 Message。

1
2
3
4
5
6
7
8
9
10
11
12
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}

dispatchMessage 方法处理消息的方式有三种:

  1. 先检查 Message 对象的 callback 是否非空,若是,表明这个 Message 是使用 post 方法发送的,运行该 Runnable。
  2. 否则,检查是否初始化了 mCallback 成员变量,若是,运行其中的 handleMessage 方法。mCallback 成员变量可以在调用 Handler 构造方法时传入 Callback 接口的实现来初始化。
  3. 如果没有实现 Callback 接口,或上一步方法返回值为 true,则运行 Handler 本身的 handleMessage 方法。

getMain 方法会返回静态成员变量 MAIN_THREAD_HANDLER 的值。如果该变量没有初始化,就创建一个使用主线程 Looper 对象初始化的 Handler 对象,对该变量赋值并返回。

createAsync 系列方法调用 Handler 对应构造方法,其中async=true

runWithScissors TODO

getIMessenger TODO

Looper

frameworks/base/core/java/android/os/Looper.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public final class Looper {
......
// sThreadLocal.get() will return null unless you've called prepare().
@UnsupportedAppUsage
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
@UnsupportedAppUsage
private static Looper sMainLooper; // guarded by Looper.class
private static Observer sObserver;

@UnsupportedAppUsage
final MessageQueue mQueue;
final Thread mThread;
......
}

构造方法

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

构造方法是私有方法,只能通过 Looper.prepare() 来初始化 Looper

接下来就看看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));
}

首先尝试获取静态线程局部变量 sThreadLocal,如果不为空,就说明已经创建过 Looper 对象,抛出RuntimeException;否则,就将 sThreadLocal 的值设置为新创建的 Looper 对象。

同一线程内的所有对象共享同一个静态变量 sThreadLocal,因此能保证一个线程至多只有一个 Looper 对象。

启动主线程相关代码:

frameworks/base/core/java/android/app/ActiviytThead.java

1
2
3
4
5
6
7
8
9
public static void main(String[] args) {
......
Looper.prepareMainLooper();
......
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
......
Looper.loop();
}
1
2
3
4
5
6
7
8
9
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}

主线程启动时调用 PrepareMainLooper() 方法。这个方法会调用 prepare(quitAllowed) 方法,其中 quitAllow=False,之后将 sMainLooper 赋值为自身的 Looper。其他线程可以使用 Looper.getMainLooper() 方法来访问主线程的 Looper。
实例化 Looper 之后,创建 ActivityThread 实例,将线程注册到系统服务,最后调用 Looper.loop() 进入消息处理循环。

至此,应用程序的启动过程就完成了。正常情况下主线程会一直处于消息循环中,这样应用程序组件就可以利用消息处理机制来实现业务逻辑。

消息循环

看看Looper.loop()怎样进行消息处理循环:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public static void loop() {
final Looper me = myLooper();
if (me == null) {...}
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);
......
} catch (Exception exception) {
......
} finally {...}
msg.recycleUnchecked();
}
}

loop() 方法会进入一个死循环,不断从 MessageQueue 取出消息,交给 Handler 处理。

如果消息队列为空,queue.next() 方法会阻塞,直到有消息进来,再取出消息返回。除非调用quit()quitSafely() 方法结束轮询,queue.next() 才会返回null,结束循环。

MessageQueue

frameworks/base/core/java/android/os/MessageQueue.java

frameworks/base/core/jni/android_os_MessageQueue.cpp

构造方法

1
2
3
4
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
mPtr = nativeInit();
}

可以看出主要工作在 nativeInit()函数进行,这是一个 JNI 方法,在讨论线程阻塞与唤醒时再回到这里。

插入与移除消息

消息队列以链表的方式储存,MessageQueue 的 mMessages 成员变量保存链表的第一个消息。

enqueueMessage 方法插入消息到队列,removeMessages 方法移除消息。插入和移除消息时会保证消息队列总是按 when 属性递增的顺序排列,也就是链表的头总是最紧急要处理的消息。

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
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {...}
if (msg.isInUse()) {...}

synchronized (this) {
if (mQuitting) {...}

msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
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;
}

// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}

mBlocked = true 时,以下几种情况下,插入消息会设置 needWake = true,唤醒接收者线程:

  1. 消息队列为空(p == null)
  2. 消息设置为立即处理(when == 0)
  3. 消息预定的时刻早于队列头部的消息(when < p.when)
  4. 消息队列被同步屏障暂停,而插入的消息是最早一条要处理的异步消息

如果需要唤醒,会调用 nativeWake(mPtr) 方法唤醒线程。我们待会讨论这个方法。

插入与移除同步屏障

postSyncBarrier 方法会插入一条同步屏障(Sync Barrier)消息到消息队列。next 方法读取到同步屏障消息后,会停止处理同步消息,只处理异步消息。如果不设置生效时间或设置为 0,屏障将立即生效。该方法返回一个 token,用于调用 removeSyncBarrier 方法移除该同步屏障。

removeSyncBarrier 方法会移除插入的同步屏障消息,使消息队列继续处理同步消息。

取下一条消息

MessageQueue.next() 方法将下一条待处理的消息返回给 Looper。其内部实现了阻塞线程、同步屏障、定时处理消息、处理空闲情况等机制。没有消息时,该方法会阻塞线程,直到新的消息到达,或者定时器到期。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Message next() {
// Return here if the message loop has already quit and been disposed.
// This can happen if the application tries to restart a looper after quit
// which is not supported.
final long ptr = mPtr;
if (ptr == 0) {
return null;
}

int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}

nativePollOnce(ptr, nextPollTimeoutMillis);
......

首先获取 NativeMessageQueue 对象的指针。如果该指针为空,说明 Native 对象没有正确初始化,返回 null 结束消息循环。

接下来进入取待处理消息的循环。

若下次查询的超时时间不为0,Binder.flushPendingCommands();(?)

调用 JNI 方法 nativePollOnce,这个方法就是没有消息时阻塞的源头,先放在一边。

接着看下面的过程,仍在 for 循环内:

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
47
48
49
50
51
52
53
54
55
56
57
58
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Got a message.
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}

// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose();
return null;
}

// If first time idle, then get the number of idlers to run.
// Idle handles only run if the queue is empty or if the first message
// in the queue (possibly a barrier) is due to be handled in the future.
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
// No idle handlers to run. Loop and wait some more.
mBlocked = true;
continue;
}

if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}

mMessages 成员变量作为链表头,是这一轮循环搜索消息的起始点,临时变量msg 保存要取出的消息。

  1. 第 6 到 12 行,如果 msg.target 为空,说明这是一个由 postSyncBarrier 方法设置的同步屏障(Sync Barrier)消息,那么快进 msg 到下一个异步消息。

  2. 接下来,如果msg不为空,首先判断消息的唤醒时间是否已到,没有到就设置下次唤醒时间为该唤醒时间。否则,从消息队列链表中取出msg

  • 如果之前有快进过(prevMsg != null),prevMsg 指向下一个消息。
  • 否则,mMessages 向前移动。

最后返回当前消息 msg
如果上一步判断当前消息为空,设置 nextPollTimeoutMillis = -1, 执行nativePollOnce 方法时将一直阻塞,直到有新的消息到达。

  1. 36 到 39 行,如果 mQuitting 变量设置为 true, 现在就是处理退出请求的时候:扔掉剩余请求,并返回 null。

  2. 44 行开始,就是处理 IdleHandler 的代码。如果第一次遇到空闲(没有消息要处理)的情况,初始化 pendingIdleHandlerCount 计数。

  3. 接下来,如果没有 IdleHandler 需要运行,设置mBlocked = true,这会通知 enqueueMessage 方法在加入消息时唤醒当前线程。
    这一节的最后,如果之前没有初始化 mPendingIdleHandlers,执行初始化。
    离开临界区,继续处理 IdleHandlerr:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // release the reference to the handler

boolean keep = false;
try {
keep = idler.queueIdle();
} catch (Throwable t) {...}

if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}

// Reset the idle handler count to 0 so we do not run them again.
pendingIdleHandlerCount = 0;

// While calling an idle handler, a new message could have been delivered
// so go back and look again for a pending message without waiting.
nextPollTimeoutMillis = 0;

遍历要处理的 IdleHandler,执行其 queueIdle() 方法。如果该方法返回 false,表明整个消息循环中该方法只需执行一次,就从 mIdleHandlers 中移除对应的 IdleHandler。

最后重设 pendingIdleHandlerCount 计数为 0,这样下个循环就不会再次处理 IdleHandler 相关逻辑。

总结一下整个方法的行为:

MessageQueue.next

线程阻塞与唤醒

初始化 NativeMessageQueue

前面 MessageQueue 初始化时,调用 nativeInit() 初始化其 C++ 对象:

1
2
3
4
5
6
7
8
9
10
static jlong android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) {
NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();
if (!nativeMessageQueue) {
jniThrowRuntimeException(env, "Unable to allocate native queue");
return 0;
}

nativeMessageQueue->incStrong(env);
return reinterpret_cast<jlong>(nativeMessageQueue);
}

新建了 NativeMessageQueue 对象并增加其强引用计数,返回对象指针。

1
2
3
4
5
6
7
8
NativeMessageQueue::NativeMessageQueue() :
mPollEnv(NULL), mPollObj(NULL), mExceptionObj(NULL) {
mLooper = Looper::getForThread();
if (mLooper == NULL) {
mLooper = new Looper(false);
Looper::setForThread(mLooper);
}
}

构造方法尝试获取当前线程的 C++ 层 Looper 对象,如果没有获取到,则新建一个并绑定到当前线程。

来看看这个 C++ 层的 Looper:
system/core/libutils/include/utils/Looper.h
system/core/libutils/Looper.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Looper::Looper(bool allowNonCallbacks)
: mAllowNonCallbacks(allowNonCallbacks),
mSendingMessage(false),
mPolling(false),
mEpollRebuildRequired(false),
mNextRequestSeq(0),
mResponseIndex(0),
mNextMessageUptime(LLONG_MAX) {
mWakeEventFd.reset(eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC));
LOG_ALWAYS_FATAL_IF(mWakeEventFd.get() < 0, "Could not make wake event fd: %s", strerror(errno));

AutoMutex _l(mLock);
rebuildEpollLocked();
}

构造方法使用 eventFd 系统调用获取了 mWakeEventFd,作为后续 epoll 用于唤醒的文件描述符。

有了 Fd,再进入 rebuildEpollLocked() 调用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void Looper::rebuildEpollLocked() {
// Close old epoll instance if we have one.
......

// Allocate the new epoll instance and register the wake pipe.
mEpollFd.reset(epoll_create1(EPOLL_CLOEXEC));
LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance: %s", strerror(errno));

struct epoll_event eventItem;
memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union
eventItem.events = EPOLLIN;
eventItem.data.fd = mWakeEventFd.get();
int result = epoll_ctl(mEpollFd.get(), EPOLL_CTL_ADD, mWakeEventFd.get(), &eventItem);
......
}

在第 6 行,通过系统调用 epoll_create1 初始化一个 epoll 实例,之后创建 epoll_event 结构 eventItem 并设置 events 属性,将 fd 设置为之前创建的 mWakeEventFd

在第 13 行,通过系统调用 epoll_ctleventItem 注册到 epoll。

epoll 允许我们对多个文件描述符进行监听。注册监听的 fd 之后,调用 epoll_wait 函数,当 fd 指向的对象数据可用时,epoll_wait 函数就会返回,同时从传入的events指针返回发生改变的 fd 对应的 eventItem

阻塞

前面说过如果消息队列没有消息,线程就会被阻塞。阻塞的调用路径是 Looper.loop() -> MessageQueue.next() -> MessageQueue.nativePollOnce(long , int)。我们从 nativePollOnce 开始继续追踪。

JNI 方法 nativePollOnce 调用 NativeMessageQueue::pollOnce 方法,进一步调用 Looper::pollOnce 方法。

1
2
3
4
5
6
7
int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {
int result = 0;
for (;;) {
......
result = pollInner(timeoutMillis);
}
}

最后来到 pollInner

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int Looper::pollInner(int timeoutMillis) {
......
struct epoll_event eventItems[EPOLL_MAX_EVENTS];
int eventCount = epoll_wait(mEpollFd.get(), eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
......

for (int i = 0; i < eventCount; i++) {
int fd = eventItems[i].data.fd;
uint32_t epollEvents = eventItems[i].events;
if (fd == mWakeEventFd.get()) {
if (epollEvents & EPOLLIN) {
awoken();
} else {...}
} else {...}
}
......
}

第4行就是关键所在。通过执行系统调用 epoll_wait ,线程将会阻塞,直到注册的 fd 有新数据或者到达超时时间才会返回。前面 MessageQueue 初始化时注册了 mWakeEventFd ,当它有新数据时 epoll_wait 就会返回,解除阻塞。由此推测,唤醒线程的方法 nativeWake 正是通过向 mWakeEventFd 写数据的操作,来解除阻塞,实现其功能的。这里写入的数据并不重要,只是利用 epoll 机制提供的阻塞和唤醒功能。

解除阻塞后,接下来进入事件处理的过程。遍历返回的 eventItems,检查是否有 fd 与 mWakeEventFd 相同。如果有,执行 awoken 方法。该方法不断读取 mWakeEventFd 以清空其内容,便于下次使用。

这里省略了大量与处理 Native 层消息相关的代码,因为这与本次主题无关。

唤醒

nativeWake 用于唤醒功能,调用 NativeMessageQueue::wake(),进一步调用 Looper::wake()。正如之前预测的,该方法向 mWakeEventFd 写入数据,实现其唤醒的功能。

调用链

MessageQueue

loop

sendMessage

Q&A

Q: Looper 如何保证线程唯一的?

A: Looper 的构造方法是私有的,只能通过 Looper.prepare() 创建,通过 Looper.myLooper() 获取。

Looper.prepare() 会检查静态线程局部变量 sThreadLocal 的值是否已设定,只有未设定时才会将其设为新建的Looper 对象,否则抛出异常。同一线程内的所有对象共享同一个静态变量sThreadLocal ,因此能保证一个线程至多只有一个 Looper 对象。

Q: ThreadLocal如何实现数据隔离的?与加锁实现的区别是什么?

A: 每个 Thread 对象中保存一个 ThreadLocalMap 对象,由 ThreadLocal 中的方法操纵。这个 Map 的 key 是 ThreadLocal 的弱引用,value 就是储存的对象。

当调用 ThreadLocal.get() 方法时,先判断该 ThreadLocalMap 是否非空,再使用 ThreadLocal 对象作为 key 查询并返回储存的对象。查询用的 hash code 是在ThreadLocal 对象初始化时调用 threadLocalHashCode 生成的。若 ThreadLocalMap 为null,或没有与 key 对应的对象,则调用 setInitialValue() 设初值并返回该值。

ThreadLocal 和 synchonized 都用于解决多线程并发访问,但是 ThreadLocal 与 synchronized 有本质的区别。

  1. 机制不同
    • synchronized 是利用锁的机制,使变量或代码块在某一时该只能被一个线程访问。
    • ThreadLocal 为每一个线程都提供了变量的副本,使得每个线程在某一时间访问到的并不是同一个对象。
  2. 数据共享状态不同
    • synchronized 仅提供一份数据,使得多个线程间通信时能够获得数据共享。
    • ThreadLocal 为每个线程提供一份数据,隔离了多个线程对数据的数据共享。

Q: 想在消息队列阻塞前处理一个事件应该如何实现?

A: 调用 next 方法取下一条消息时,消息队列会在当前消息为空时阻塞,以等待新的消息。通过 addIdleHandler 方法,添加处理空闲状态的代码,可以在下次循环到 nativePollOnce 阻塞之前执行期望的操作。

Q: 主线程与其他线程的不同之处有哪些?

A: 主线程在应用启动时由 Android 系统启动,子线程由应用开发者主动开启。主线程会在启动时调用 Looper.prepareMainLooper()Looper.loop() 方法,进入消息循环。主线程进入 Looper.loop() 方法后不会退出消息循环,子线程可以通过 Looper.quit() 退出消息循环。主线程负责处理 UI 事件和 Broadcast 消息,若超时未响应会触发 ANR。

Q: MessageRunnable 在 Handler 中什么区别?

A: 逻辑是一样的,post 方法中的 Runnable 会被封装成 Message,再用 SendMessage 方法发送。

Q: Handler 如何实现消息定时处理?

A: 这一功能是在 MessageQueue.next() 方法实现的。MessageQueue 取出消息返回前,会对比该消息的 when 属性与当前时间。如果还没有到设定的时刻,就会设置nextPollTimeoutMillis变量,使得线程在给定的时刻唤醒。否则,就会返回该消息到 Looper,Looper 再调用 dispatchMessage 交给 Handler 处理。

参考文献

  1. Android SDK, android.os.Handler
  2. Android Developers Documentation, https://developer.android.com/reference/android/os/Handler
  3. Android 源码分析 --Handler 机制的实现与工作原理,https://juejin.im/post/5910522f1b69e6006858b830
  4. Android 消息机制(一)消息队列的创建与循环的开始 Looper与MessageQueue, https://www.viseator.com/2017/10/22/android_event_1/
  5. Android Handler的使用方式和注意事项, https://juejin.im/post/5910533dac502e006cfe01cd
  6. 理解 Java 中的 ThreadLocal, https://droidyue.com/blog/2016/03/13/learning-threadlocal-in-java/