在Android系统中,Handler机制应用较广,尤其是在App层面,基本每个App都会用到。使用的场景主要是向主线程发送事件,更新UI。但大家真的了解Handler机制吗?看一下面的几个问题是否可以回答:
a.Handler是如何实现多个线程之前事件传递的?
b.Handler、Message、MessageQueue、Looper相互之间的数量比是多少,都是1:1吗?
c.每个变量运行的线程是那个?
d.ThreadLocal是怎么回事,在Handler机制中起什么作用?
能准确回答上述问题,说明对Handler机制的理解已相当到位了。
下面论述一下Handler机制各类的实现原理,从而揭露内部的工作流程。(源码环境为Android8.0)
1. 先来看Handler类:
1.1 构造方法:
Handler的构造方法总共有如下几个,最终实现为下面两个方法:
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;
}
public
Handler(Looper looper, Callback callback,
boolean
async) {
mLooper
= looper;
mQueue
= looper.mQueue;
mCallback
= callback;
mAsynchronous
= async;
}
最终都调至Handler(Callback callback, boolean async)和Handler(Looper looper, Callback callback, boolean async),看一下源码可知,主要为初始化几个变量:mLooper、mQueue、mCallback,mAsynchronous,前三个变量非常重要,有以下几个结论:
a.一个Handler中含有一个Looper
b.Handler中持有的MessageQueue源于Looper中,与Looper中该变量行为一致
c.一个Handler中含有一个Callback
Handler中还有如下几个重要的方法,提供了Handler对外的能力:
1.2 obtainMessage方法,对外提供一个获取Message缓存池中Message的方法,避免应用产生过多的Message对象。根据参数的不同,重载有多个该方法,实现原理都一样,以其中之一分析一下:
//
obtainMessage@Handler.java
public final
Message obtainMessage()
{
return
Message.
obtain
(
this
);
}
//
obtain@Message.java
public static
Message obtain(Handler h) {
Message m =
obtain
();
m.target = h;
return
m;
}
// obtain
@Message.java
public static
Message obtain() {
synchronized
(
sPoolSync
) {
if
(
sPool
!=
null
) {
Message m =
sPool
;
sPool
= m.next;
m.next =
null
;
m.flags =
0
;
// clear in-use flag
sPoolSize
--;
return
m;
}
}
return new
Message();
}
sPool提供复用功能的实现,其存储结构为链表,通过sPoolSize的大小及SPool头部指针位置作到这一点,感兴趣的同学可以研究下
1.3 post方法、sendMessage方法,Handler提供了多个post方法,按参数不同,提供了诸如指定时间、延时的功能,基本功能是一样的,以其中某一个方法说一下其实现:
public final boolean
post(Runnable r)
{
return
sendMessageDelayed(
getPostMessage
(r),
0
);
}
private static
Message getPostMessage(Runnable r) {
Message m = Message.
obtain
();
m.callback = r;
return
m;
}
从getPostMessage方法可以看出,传入的Runnable被Message所持有。
public final boolean
sendMessageDelayed(Message msg,
long
delayMillis)
{
if
(delayMillis <
0
) {
delayMillis =
0
;
}
return
sendMessageAtTime(msg, SystemClock.
uptimeMillis
() + delayMillis);
}
public boolean
sendMessageAtTime(Message msg,
long
uptimeMillis) {
MessageQueue queue =
mQueue
;
if
(queue ==
null
) {
RuntimeException e =
new
RuntimeException(
this
+
" sendMessageAtTime() called with no mQueue"
);
Log.
w
(
"Looper"
, e.getMessage(), e);
return false
;
}
return
enqueueMessage(queue, msg, uptimeMillis);
}
private boolean
enqueueMessage(MessageQueue queue, Message msg,
long
uptimeMillis) {
msg.target =
this
;
if
(
mAsynchronous
) {
msg.setAsynchronous(
true
);
}
return
queue.enqueueMessage(msg, uptimeMillis);
}
由上面可知如下结论:
a.post方法的实现是与sendMessage一致的,最终都调至sendMessage中
b.干了个什么事呢:得到一个message,将它的callback设为传入的callback(如果有的话),将它的target设为自身,然后调用MessageQueue的enqueueMessage处理该Message,实现原理讲到MessageQueue再行讨论。
1.4 dispatchMessage方法,该方法从Handler发起流程中难以看到在什么地方调用了,是一个消息处理时的回调,先讲一下该方法的逻辑,后面再说调用时机
public void
dispatchMessage(Message msg) {
if
(msg.callback !=
null
) {
handleCallback
(msg);
}
else
{
if
(
mCallback
!=
null
) {
if
(
mCallback
.handleMessage(msg)) {
return
;
}
}
handleMessage(msg);
}
}
private static void
handleCallback(Message message) {
message.callback.run();
}
从上面的逻辑中看到如下结论:
消费该Hanlder事件总共有三种可能的途径,分别为Message中的callback、Handler所持有的Callback对象、Handler的自身的handleMessage方法(一般写法是构造匿名子类,实现该方法),三种途径的优先级从高到低。
Handler在该机制中的作用为:
a.对外提供可复用的Message
b.对外提供发消息的接口
c.对外提供回调的实现,三种方式
2.Looper类
在主线程中使用Handler,看不到Looper,因为系统已经帮我们自动生成了Looper,在工作线程中使用Handler则必须先调用Looper的prepare生成Looper,否则会报错,原因如下:
测试代码:
Thread
t =
new
Thread
(
new
Runnable() {
@Override
public void
run() {
Handler handler =
new
Handler();
}
});
t.start();
运行后报错:
java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
Handler最终会调至(1.1中第一个构造方法)
if
(
mLooper
==
null
) {
throw new
RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()"
);
}
因此正确的写法为:
Thread
t =
new
Thread
(
new
Runnable() {
@Override
public void
run() {
Looper.
prepare
();
Handler handler =
new
Handler();
// ignore
Looper.
loop
();
}
});
2.1 prepare方法:
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变量定义如下:
static final
ThreadLocal<Looper>
sThreadLocal
=
new
ThreadLocal<Looper>();
使用ThreadLocal类对Looper进行了一层封装。
像c++的内联函数一样,此处我们直接内联一下ThreadLocal这个类,看一下该类的作用,先从Looper中跳出去一下
--------------------------------此处插入ThreadLocal类----------------------------------
3. ThreadLocal类
ThreadLocal达到的目的是:每一个ThreadLocal的变量在多个线程中都有一份copy,相互间独立存储,互不影响,但变量名只有一个。也就是说对于该变量的使用方来讲,看起来像是只定义了一个变量,但实际上在多线程环境中,有互不影响的、每个线程都有一份的对象存在。如何作到这一点呢,是通过其对外提供的get方法和set方法做到的。
3.1 先来看set方法:
public void
set(
T
value) {
Thread t = Thread.
currentThread
();
// 第一步,拿到每个线程所对应的ThreadLocalMap
ThreadLocalMap map = getMap(t);
// 第二步,将该值存入或更新map的对应位置
if
(map !=
null
)
map.set(
this
, value);
else
createMap(t, value);
}
ThreadLocalMap
getMap(Thread t) {
return
t.threadLocals;
}
void
createMap(Thread t,
T
firstValue) {
t.threadLocals =
new
ThreadLocalMap(
this
, firstValue);
}
// ThreadLocalMap的构造方法
ThreadLocalMap
(ThreadLocal<?> firstKey, Object firstValue) {
table
=
new
Entry[
INITIAL_CAPACITY
];
int
i = firstKey.threadLocalHashCode & (
INITIAL_CAPACITY
-
1
);
table
[i] =
new
Entry(firstKey, firstValue);
size
=
1
;
setThreshold(
INITIAL_CAPACITY
);
}
//
set@ThreadLocalMap
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();
}
上述逻辑第一步拿到每个线程的ThreadLocalMap对象
// threadlocals#Thread
ThreadLocal
.ThreadLocalMap
threadLocals
=
null
;
也就是说每个线程维护一个ThreadLocalMap来存储该线程所用到的所有ThreadLocal变量,ThreadLocalMap的实现原理与HashMap基本一致,之前讨论过HashMap的实现原理,感兴趣的同学对比一下源码就能明白。
第二步中将传入的值存入ThreadLocalMap的对应位置,该位置的key由ThreadLocal本身的hash值去运算得到的。从上面的代码中可以看出。每一个线程持有一个ThreadLocalMap,set的value将和ThreadLocal自身组成key-value结构存在于该Map中。
3.2 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();
}
private
T
setInitialValue() {
T
value = initialValue();
Thread t = Thread.
currentThread
();
ThreadLocalMap map = getMap(t);
if
(map !=
null
)
map.set(
this
, value);
else
createMap(t, value);
return
value;
}
理解了set方法之后,get方法就较为简单了,反向拿到存入的值即可,ThreadLocal对象本身在多个线程中只有一份,但通过其set和get,可以得到其包装对象在每个线程中只有一份。
--------------------------ThreadLocal分析完毕,回至Looper----------------------------------
Looper的prepare为一个静态方法,目的是可以生成每个线程都有一份的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));
}
2.2 Looper构造方法:
private
Looper(
boolean
quitAllowed) {
mQueue
=
new
MessageQueue(quitAllowed);
mThread
= Thread.
currentThread
();
}
持有一个MessageQueue对象,持有当前的线程对象
2.3 loop方法
public static void
loop() {
// 第一步:myLooper的实现看下方,得到当前线程的Looper
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
();
// 第二步:定义一个无限循环体,遍历MessageQueue,取其中的有效Message,进行处理,处理语名为:msg.target.dispatchMessage(msg);该方法请看上面1.4
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
final
Printer logging = me.mLogging;
if
(logging !=
null
) {
logging.println(
">>>>> Dispatching to "
+ msg.target +
" "
+
msg.callback +
": "
+ msg.
what
);
}
final long
slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
final long
traceTag = me.mTraceTag;
if
(traceTag !=
0
&& Trace.
isTagEnabled
(traceTag)) {
Trace.
traceBegin
(traceTag, msg.target.getTraceName(msg));
}
final long
start = (slowDispatchThresholdMs ==
0
) ?
0
: SystemClock.
uptimeMillis
();
final long
end;
try
{
msg.target.dispatchMessage(msg);
end = (slowDispatchThresholdMs ==
0
) ?
0
: SystemClock.
uptimeMillis
();
}
finally
{
if
(traceTag !=
0
) {
Trace.
traceEnd
(traceTag);
}
}
if
(slowDispatchThresholdMs >
0
) {
final long
time = end - start;
if
(time > slowDispatchThresholdMs) {
Slog.w(
TAG
,
"Dispatch took "
+ time +
"ms on "
+ Thread.
currentThread
().getName() +
", h="
+
msg.target +
" cb="
+ msg.callback +
" msg="
+ msg.
what
);
}
}
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
);
}
// 此处实现Message的循环利用
msg.recycleUnchecked();
}
}
public static
@Nullable Looper myLooper() {
return
sThreadLocal
.get();
}
在loop的处理中,有一点需重点关注,loop在线程中是一个无限循环,跳出该循环的条件为MessageQueue的next返回值为空,看一下4.2的next方法实现,返回空依赖于mQuitting变量,也就是说在线程中不需要一直等待事件时,要把MessageQueue该变量置为true,设置入口为
quit@Looper
2.4 quit方法
public void
quit() {
mQueue
.quit(
false
);
}
//
quit@MessageQueue
void
quit(
boolean
safe) {
if
(!
mQuitAllowed
) {
throw new
IllegalStateException(
"Main thread not allowed to quit."
);
}
synchronized
(
this
) {
if
(
mQuitting
) {
return
;
}
mQuitting
=
true
;
if
(safe) {
removeAllFutureMessagesLocked();
}
else
{
removeAllMessagesLocked();
}
// We can assume mPtr != 0 because mQuitting was previously false.
nativeWake
(
mPtr
);
}
}
总结一下:Looper在Handler机制中起的作用是:
a.提供每个线程唯一的实例
b.触发MessageQueue的循环遍历
4. MessageQueue类
顾名思义,该类需提供三个能力:
a.Message队列存储
b.Message入队
c.Message出队
Message队列以链表形式存储,其头部指针存放于mMessages
4.1 enqueueMessage方法,该方法用于Message入队
boolean
enqueueMessage(Message msg,
long
when) {
if
(msg.target ==
null
) {
throw new
IllegalArgumentException(
"Message must have a target."
);
}
if
(msg.isInUse()) {
throw new
IllegalStateException(msg +
" This message is already in use."
);
}
synchronized
(
this
) {
if
(
mQuitting
) {
IllegalStateException e =
new
IllegalStateException(
msg.target +
" sending message to a Handler on a dead thread"
);
Log.
w
(
TAG
, e.getMessage(), e);
msg.recycle();
return false
;
}
msg.markInUse();
msg.when = when;
Message p =
mMessages
;
boolean
needWake;
// 以下为入队的关键代码,找到链表的队尾,放入该Message
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;
// 注意,入队的时候,已经按时间次序将Message插入队列中的合适位置
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
;
}
需要注意,在上面的入队处理中,要按Handler传入的执行时间插入
enqueueMessage调用的入口请查看1.3
4.2 next方法,该方法用于MessageQueue出队
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);
synchronized
(
this
) {
// Try to retrieve the next message. Return if found.
final long
now = SystemClock.
uptimeMillis
();
Message prevMsg =
null
;
// 第一步:拿到链表的头部
Message msg =
mMessages
;
// 第二步:取到最早入队的有效的Message
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
);
}
// Run the idle handlers.
// We only ever reach this code block during the first iteration.
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) {
Log.
wtf
(
TAG
,
"IdleHandler threw exception"
, 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
;
}
}
next方法的调用入口位于2.3中
小结一下,通过上面的代码讲解,可以得到以下几个结论:
1. 首先Handler对象本身位于构造Handler的当前线程,他的事件要发往那个线程,取决于构造方法传 的Looper变量
2. MessageQueue与Looper位于Handler所要发往的工作线程中,Looper持有MessageQueue对象,MessageQueue中看不到Looper
3. Looper与线程为1对1的关系,也就是说一个线程只有一个Looper,一个Looper只服务于一个线程,达到这个目的是通过ThreadLocal包装Looper实现的
4. 从线程的角度讲,Handler对象自身与线程没有任何关系,它是通过Looper持有的MessageQueue实现向Looper所服务的线程事件队列(MessageQueue)插入事件(Message)的
看一下文章开头所说的几个问题是不是已经有了答案:
a.Handler是如何实现多个线程之前事件传递的?
请看上面总结的第4点
b.Handler、Message、MessageQueue、Looper相互之间的数量比是多少,都是1:1吗?
Handler n--1 Looper(以主线程为例,主线程的Looper可以定义非常多Handler)
Looper持有一个MessageQueue(不可逆,看上面总结的第2点)
MessageQueue 1--n Message
c.每个变量运行的线程是那个?
Handler位于定义线程、Looper、MessageQueue位于工作线程,线程的轮询依赖于Looper.loop
d.ThreadLocal是怎么回事,在Handler机制中起什么作用?
可查看代码分析部分的第3部分