首页 > 应用开发 > Java > Handler 源码解析(Java 层)
从很早开始就认识到 Handler 了,只不过那时修为尚浅,了解的不够深刻,也没有应用自如。不过随着工作时间的增长,对 Handler 又有了更深层次的认识,于是有了这篇博客,希望尽可能的总结出多的知识点。
Handler 在 Java 层源码主要有 4 个类:Looper、MessageQueue、Message、Handler。我归纳了他们的几个主要知识点:
Looper:sThreadLocal、Looper.loop();
Message:数据结构、消息缓存池;
MessageQueue:enqueueMessage、next、管道等待、同步消息隔离、idleHandler;
Handler:send/post、dispatchMessage 消息处理优先级。
static
final
ThreadLocal<Looper> sThreadLocal =
new
ThreadLocal<Looper>();
private
static
Looper sMainLooper;
// guarded by Looper.class
final
MessageQueue mQueue;
// sThreadLocal
private
static
void
prepare
(
boolean
quitAllowed)
{
if
(sThreadLocal.get() !=
null
) {
throw
Exception ... }
sThreadLocal.set(
new
Looper(quitAllowed));
}
public
static
@Nullable
Looper
myLooper
()
{
return
sThreadLocal.get();
}
// sMainLooper
public
static
void
prepareMainLooper
()
{
prepare(
false
);
synchronized
(Looper.class) {
if
(sMainLooper !=
null
) {
throw
Exception ...}
sMainLooper = myLooper();
}
}
public
static
Looper
getMainLooper
()
{
synchronized
(Looper.class) {
return
sMainLooper;
}
}
// mQueue
private
Looper
(
boolean
quitAllowed)
{
mQueue =
new
MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
public
static
@NonNull
MessageQueue
myQueue
()
{
return
myLooper().mQueue;
}
sThreadLocal:静态常量,保证一个线程只有一个 Looper;
sMainLooper:静态变量,在 prepareMainLooper 中赋值当前线程 Looper;
mQueue:变量,Looper 构造函数中初始化,因为一个线程只有一个 Looper,所以也同样只有一个 mQueue。
通过以上分析,我们可以总结出一下特性:
Looper、MessageQueue 是线程唯一的;
一个进程只有一个 sMainLooper;
根据 ThreadLocal 的特性,可通过 myLooper 方法获取当前线程的 Looper。
public
static
void
loop
()
{
final
Looper me = myLooper();
final
MessageQueue queue = me.mQueue;
for
(;;) {
Message msg = queue.next();
...
msg.target.dispatchMessage(msg);
...
msg.recycleUnchecked();
}
}
Looper.loop() 方法虽然看起来很多,其实他主要就做了三件事:
从消息队列中获取下一个消息;
msg.target 就是 handler,通过 dispatchMessage 方法把消息分发下去,这个方法下面会有说到;
消息回收,放到消息缓存池里。这里需要注意的是 Message 对象并没有释放,会缓存起来。
public
int
what, arg1, arg2;
public
Object obj;
public
Messenger replyTo;
int
flags;
long
when;
// 消息发送时间
Bundle data;
Handler target;
Runnable callback;
Message next;
private
static
final
Object sPoolSync =
new
Object();
private
static
Message sPool;
private
static
int
sPoolSize =
;
private
static
final
int
MAX_POOL_SIZE =
50
;
看到 next 变量,我们会想到单链表,其实 Message 就相当于单链表的 node,MessageQueue 就是一个单链表了,会持有表头的引用;
what、arg1、arg2、obj、data 就是我们发送的一些信息;
值得注意的是 target,他是 Handler 类型,就是本消息的 Handler,会在 Handler 发送消息的时候赋值;
后面的四个对象,都是和消息缓存池有关的。
public
static
Message
obtain
()
{
synchronized
(sPoolSync) {
if
(sPool !=
null
) {
Message m = sPool;
sPool = m.next;
m.next =
null
;
m.flags =
;
// clear in-use flag
sPoolSize--;
return
m;
}
}
return
new
Message();
}
void
recycleUnchecked
()
{
flags = FLAG_IN_USE;
what =
;
arg1 =
;
arg2 =
;
obj =
null
;
replyTo =
null
;
sendingUid = -
1
;
when =
;
target =
null
;
callback =
null
;
data =
null
;
synchronized
(sPoolSync) {
if
(sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool =
this
;
sPoolSize++;
}
}
}
事实上缓存池的数据结构也是一个链表,sPool 为链表头引用,最大容量为 50;
回收消息时,会把消息里面所有参数重置,并把当前消息设为链表头;
获取消息时,返回当前链表头,并把 next 置空。
boolean
enqueueMessage
(Message msg,
long
when)
{
synchronized
(
this
) {
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean
needWake;
if
(p ==
null
|| when ==
|| when < p.when) {
// 作为表头,如果队列是阻塞状态则需要唤醒
msg.next = p;
mMessages = msg;
needWake = mBlocked;
}
else
{
// 根据时间顺序,插入链表中间
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;
// 插入消息
prev.next = msg;
}
// We can assume mPtr != 0 because mQuitting is false.
if
(needWake) {
nativeWake(mPtr);
}
}
return
true
;
}
主要作为插入队列的方法,有下列几个特性:
把消息加入消息队列,如果当前表头为空,则把消息作为表头引用;如果不为空,则会根据时间的顺序,插入到对应的时间中;
nativeWake 是调用底层在管道中写操作以唤醒,在队列不是阻塞的状态下是不需要唤醒的;
另外注意其中用了 synchronized 关键字,说明消息队列的插入是线性安全的,删除也是线性安全的,之后我们会说到。
for
(;;) {
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized
(
this
) {
final
long
now = SystemClock.uptimeMillis();
Message prevMsg =
null
;
Message msg = mMessages;
if
(msg !=
null
&& msg.target ==
null
) {
// 如果有同步消息隔离,则会优先查找异步消息
do
{
prevMsg = msg;
msg = msg.next;
}
while
(msg !=
null
&& !msg.isAsynchronous());
}
if
(msg !=
null
) {
if
(now < msg.when) {
// 计算距离下一个消息的时间
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
{
// 没有更多消息的时候,nextPollTimeoutMillis 会置为 1。
nextPollTimeoutMillis = -
1
;
}
...
}
// 如果目前没有消息,已经处在空闲状态,则执行 idler.queueIdle
for
(
int
i =
; 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);
}
}
}
...
}
此方法会从消息队列中读取下一个消息返回,主要做了以下操作:
nativePollOnce 函数会调用底层管道操作函数,nextPollTimeoutMillis 为 -1 时,会阻塞,为 0 时不会阻塞,大于 0 时,会阻塞相应的时间;
如果有同步消息隔离,则会优先查找异步消息;
获取当前时间队列的消息,并返回;
如果队列没有任何消息,则会执行 idler.queueIdle,通知监听者当前队列处于空闲状态。
上面我们有提到了同步消息隔离,这里我们介绍一下。同步隔离,有时候也可以叫异步消息,说的是一个意思。在源码中主要用于优先更新 UI。
private
IdleHandler[] mPendingIdleHandlers;
public
int
postSyncBarrier
()
{
return
postSyncBarrier(SystemClock.uptimeMillis());
}
private
int
postSyncBarrier
(
long
when)
{
// 向消息队列中加入一个 handler 为空的消息
synchronized
(
this
) {
final
int
token = mNextBarrierToken++;
final
Message msg = Message.obtain();
msg.markInUse();
msg.when = when;
msg.arg1 = token;
Message prev =
null
;
Message p = mMessages;
if
(when !=
) {
while
(p !=
null
&& p.when <= when) {
prev = p;
p = p.next;
}
}
if
(prev !=
null
) {
// invariant: p == prev.next
msg.next = p;
prev.next = msg;
}
else
{
msg.next = p;
mMessages = msg;
}
return
token;
}
}
如上 postSyncBarrier 函数中会向消息队列中加入一个 handler(即 Message 的 target) 为空的消息作为标识。在我们上面 MessageQueue.next() 的函数中,当 msg.target == null 时,会优先获取异步消息并返回。
因此想要使用异步消息有两个条件:
消息为异步消息,即 msg.isAsynchronous() 返回 false;
需要获取当前队列并运行 postSyncBarrier() 函数。
Handler 还提供了消息队列空闲状态通知。
private
final
ArrayList<IdleHandler> mIdleHandlers =
new
ArrayList<IdleHandler>();
public
void
addIdleHandler
(@NonNull IdleHandler handler)
{
if
(handler ==
null
) {
throw
new
NullPointerException(
"Can't add a null IdleHandler"
);
}
synchronized
(
this
) {
mIdleHandlers.add(handler);
}
}
public
void
removeIdleHandler
(@NonNull IdleHandler handler)
{
synchronized
(
this
) {
mIdleHandlers.remove(handler);
}
}
IdleHandler 的源码比较简单,就是一个 ArrayList,然后进行增加删除操作。注意,这个也是线性安全的。
public
final
boolean
post
(Runnable r)
{
return
sendMessageDelayed(getPostMessage(r),
);
}
public
final
boolean
sendMessage
(Message msg)
{
return
sendMessageDelayed(msg,
);
}
private
static
Message
getPostMessage
(Runnable r)
{
Message m = Message.obtain();
m.callback = r;
return
m;
}
private
boolean
enqueueMessage
(MessageQueue queue, Message msg,
long
uptimeMillis)
{
msg.target =
this
;
if
(mAsynchronous) {
msg.setAsynchronous(
true
);
}
return
queue.enqueueMessage(msg, uptimeMillis);
}
sendMessage 和 post 最本质的区别是之后处理任务时的优先级,post 会处理 Runnable 中的任务,而 sendMessage 会回调给 handler 处理;
他们最终都会走 enqueueMessage 方法,并设置当前 Handler 为 msg.target。
public
void
dispatchMessage
(Message msg)
{
if
(msg.callback !=
null
) {
handleCallback(msg);
}
else
{
if
(mCallback !=
null
) {
if
(mCallback.handleMessage(msg)) {
return
;
}
}
handleMessage(msg);
}
}
任务执行时就会运行这个函数,主要是一个优先级的问题:
callback 优先级最高,也就是 post 发送的消息
mCallback.handleMessage(msg),优先级第二
handleMessage(msg),优先级第三
(原文完)
提前祝大家周末愉快,下周见。
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/69913485/viewspace-2642773/ ,如需转载,请注明出处,否则将追究法律责任。