版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
【移动应用开发技术】Android中Handler机制的工作原理是什么
本篇文章为大家展示了Android中Handler机制的工作原理是什么,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。Looper在使用Handler之前,我们必须得初始化Looper,并让Looper跑起来。Looper.prepare();
...
Looper.loop();执行上面两条语句之后,Looper就可以跑起来了。先来看看对应的源码: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));
}
private
Looper(boolean
quitAllowed)
{
mQueue
=
new
MessageQueue(quitAllowed);
mThread
=
Thread.currentThread();
}必须保证一个线程中有且只有一个Looper对象,所以在初始化Looper的时候,会检查当前线程有没有Looper对象。Looper的初始化会创建一个MessageQueue。创建完Looper后会放到ThreadLocal中去,关于ThreadLocal,后面会说到。public
static
void
loop()
{
//
判断当前线程有没有初始化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;
...
for
(;;)
{
Message
msg
=
queue.next();
//
might
block
if
(msg
==
null)
{
//
No
message
indicates
that
the
message
queue
is
quitting.
return;
}
...
final
long
traceTag
=
me.mTraceTag;
if
(traceTag
!=
0
&&
Trace.isTagEnabled(traceTag))
{
Trace.traceBegin(traceTag,
msg.target.getTraceName(msg));
}
try
{
//
target指的是Handler
msg.target.dispatchMessage(msg);
}
finally
{
if
(traceTag
!=
0)
{
Trace.traceEnd(traceTag);
}
}
...
msg.recycleUnchecked();
}
}方法比较长,所以只把最核心的代码放了出来。省略掉的代码中有一个比较有意思的:我们可以指定一个阈值比如说200,当Message的处理超过200ms时,就会输出Log。这可以在开发中帮助我们发现一些潜在的性能问题。可惜的是,设置阈值的方法是隐藏的,无法直接调用,所以这里就不放出代码了,感兴趣的朋友自己翻一下源码吧。简化后的代码可以看出逻辑十分简单,可以说Looper在当中扮演着搬砖工的角色,从MessageQueue中取出Message,然后交给Handler去分发,再去MessageQueue中取出Message...无穷无尽,就像愚公移山一样。看到这里,应该多多少少会觉得有点不对劲,因为这里是一个死循环,按道理来说会一直占着CPU资源的,并且消息也总有处理完的时候,难道处理完就从消息队列返回Null,然后Looper结束吗?显然不是,注意看注释mightblock。MessageQueue答案就在MessageQueue里面,直接来看一下next():Message
next()
{
...
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;
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;
}
}代码有点长,这次不打算省略掉一些了,因为这里面还有一个小彩蛋。方法中最重要的应该就是这一行了nativePollOnce(ptr,
nextPollTimeoutMillis);简单来说,当nextPollTimeoutMillis==-1时,挂起当前线程,释放CPU资源,当nextPollTimeoutMillis>=0时会延时指定的时间激活一次线程,让代码继续执行下去。这里涉及到了底层的pipe管道和epoll机制,就不再讲下去了(其实是因为讲不下去了)。这也就可以回答上面的问题了,当没有消息的时候只需要让线程挂起就行了,这样可以保证不占用CPU资源的同时保住Looper的死循环。然后我们来看消息是如何取出来的。MessageQueue中有一个Message,Message类中又有一个Message成员next,可以看出Message是一个单链表结构。消息的顺序是根据时间先后顺序排列的。一般来说,我们要取的Message就是第一个(这里先不考虑异步消息,关于异步消息以后会讲到的,又成功给自己挖了一个坑哈哈),如果当前时间大于等于Message中指定的时间,那么将消息取出来,返回给Looper。由于此时nextPollTimeoutMillis的值为0,所以当前面的消息处理完之后,Looper就又来取消息了。如果当前的时间小于Message中指定的时间,那么设置nextPollTimeoutMillis值以便下次唤醒。还有另外一种当前已经没有消息了,nextPollTimeoutMillis会被设置为-1,也就是挂起线程。别急,还没那么快呢,接着往下看。紧接着的逻辑是判断当前有没有IdleHandler,没有的话就continue,该挂起就挂起,该延时就延时,有IdleHandler的话会执行它的queueIdle()方法。这个IdleHandler是干什么的呢?从名字应该也能猜出个一二来,这里就不再展开讲了。关于它的一些妙用可以看我之前写的Android启动优化之延时加载。执行完queueIdle()方法后,会将nextPollTimeoutMillis置为0,重新看一下消息队列中有没有新的消息。Handler上面将取消息的流程都讲清楚了,万事俱备,就差往消息队列中添加消息了,该我们最熟悉的Handler出场了。Handler往队列中添加消息,主要有两种方式:Handler.sendXXX();
Handler.postXXX();第一种主要是发送Message,第二种是Runnable。无论是哪种方式,最终都会进入到MessageQueue的enqueueMessage()方法。boolean
enqueueMessage(Message
msg,
long
when)
{
...
synchronized
(this)
{
...
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;
}一般情况下,我们通过Handler发送消息的时候,会通过SystemClock.uptimeMillis()获取一个开机时间,然后MessageQueue就会根据这个时间来对Message进行排序。所以enqueueMessage()方法中就分了两种情况,一种是直接可以在队头插入的。一种是排在中间,需要遍历一下,然后寻一个合适的坑插入。when==0对应的是Handler的sendMessageAtFrontOfQueue()和postAtFrontOfQueue()方法。needWake的作用是根据情况唤醒Looper线程。上面有一点还没有讲,就是Looper从MessageQueue中取出Message后,会交由Handler进行消息的分发。public
void
dispatchMessage(Message
msg)
{
if
(msg.callback
!=
null)
{
handleCallback(msg);
}
else
{
if
(mCallback
!=
null)
{
if
(mCallback.handleMessage(msg))
{
return;
}
}
handleMessage(msg);
}
}优先级顺序是Message自带的callback,接着是Handler自带的callback,最后才是handleMessage()这个回调。ThreadLocal还记得Looper中有一个ThreadLocal吧,把它放到最后来讲是因为它可以单独拿出来讲,不想在上面干扰到整个流程。ThreadLocal是一个数据存储类,它最神奇的地方就是明明是同一个ThreadLocal对象,但是在不同线程中可以存储不同的对象,比如说在线程A中存储了"Hello",而在线程B中存储了"World"。它们之间互相不干扰。在Handler机制中,由于一个Looper对应着一个线程,所以将Looper存进ThreadLocal最合适不过了。ThreadLocal比价常用的就set()和get()方法。分别来看看怎么实现的吧。public
void
set(T
value)
{
Thread
t
=
Thread.currentThread();
ThreadLocalMap
map
=
getMap(t);
if
(map
!=
null)
map.set(this,
value);
else
c
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 公共设施管理与维护操作手册(标准版)
- 车站人员考勤管理制度
- 财务管理制度
- 办公室员工培训课程更新制度
- 办公室出差与报销管理制度
- 2026年锡山城发集团公开招聘5人备考题库及完整答案详解1套
- 人教版初中语文七下《骆驼祥子》基础复习必刷题(附答案)
- 2026年葫芦岛市南票区政府专职消防队员招聘37人备考题库及参考答案详解一套
- 关于选聘“警民联调”室专职人民调解员20人的备考题库参考答案详解
- 2026年灵台县人民法院招聘备考题库有答案详解
- 2025年体育教师个人年终述职报告
- 实际问题与一次函数课件2025-2026学年人教版八年级数学下册
- 2025年天津科技大学毛泽东思想和中国特色社会主义理论体系概论期末考试模拟题及答案1套
- 2024年盐城市体育局直属事业单位招聘真题
- 南方航空安全员培训
- 2025-2026学年岭南美版(新教材)初中美术七年级上册期末综合测试卷及答案
- DB11∕T 2398-2025 水利工程巡视检查作业规范
- 2025秋国家开放大学《政府经济学》期末机考精准复习题库
- 2025-2026学年教科版(新教材)二年级上册科学全册知识点梳理归纳
- MDT在老年髋部骨折合并症患者中的应用策略
- PCB设计规范-MD元器件封装库尺寸要求
评论
0/150
提交评论