版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領
文檔簡介
【移動應用開發(fā)技術】Android中Handler機制的工作原理是什么
本篇文章為大家展示了Android中Handler機制的工作原理是什么,內(nèi)容簡明扼要并且容易理解,絕對能使你眼前一亮,通過這篇文章的詳細介紹希望你能有所收獲。Looper在使用Handler之前,我們必須得初始化Looper,并讓Looper跑起來。Looper.prepare();
...
Looper.loop();執(zhí)行上面兩條語句之后,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的初始化會創(chuàng)建一個MessageQueue。創(chuàng)建完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。這可以在開發(fā)中幫助我們發(fā)現(xiàn)一些潛在的性能問題。可惜的是,設置閾值的方法是隱藏的,無法直接調(diào)用,所以這里就不放出代碼了,感興趣的朋友自己翻一下源碼吧。簡化后的代碼可以看出邏輯十分簡單,可以說Looper在當中扮演著搬磚工的角色,從MessageQueue中取出Message,然后交給Handler去分發(fā),再去MessageQueue中取出Message...無窮無盡,就像愚公移山一樣??吹竭@里,應該多多少少會覺得有點不對勁,因為這里是一個死循環(huán),按道理來說會一直占著CPU資源的,并且消息也總有處理完的時候,難道處理完就從消息隊列返回Null,然后Looper結(jié)束嗎?顯然不是,注意看注釋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時會延時指定的時間激活一次線程,讓代碼繼續(xù)執(zhí)行下去。這里涉及到了底層的pipe管道和epoll機制,就不再講下去了(其實是因為講不下去了)。這也就可以回答上面的問題了,當沒有消息的時候只需要讓線程掛起就行了,這樣可以保證不占用CPU資源的同時保住Looper的死循環(huán)。然后我們來看消息是如何取出來的。MessageQueue中有一個Message,Message類中又有一個Message成員next,可以看出Message是一個單鏈表結(jié)構(gòu)。消息的順序是根據(jù)時間先后順序排列的。一般來說,我們要取的Message就是第一個(這里先不考慮異步消息,關于異步消息以后會講到的,又成功給自己挖了一個坑哈哈),如果當前時間大于等于Message中指定的時間,那么將消息取出來,返回給Looper。由于此時nextPollTimeoutMillis的值為0,所以當前面的消息處理完之后,Looper就又來取消息了。如果當前的時間小于Message中指定的時間,那么設置nextPollTimeoutMillis值以便下次喚醒。還有另外一種當前已經(jīng)沒有消息了,nextPollTimeoutMillis會被設置為-1,也就是掛起線程。別急,還沒那么快呢,接著往下看。緊接著的邏輯是判斷當前有沒有IdleHandler,沒有的話就continue,該掛起就掛起,該延時就延時,有IdleHandler的話會執(zhí)行它的queueIdle()方法。這個IdleHandler是干什么的呢?從名字應該也能猜出個一二來,這里就不再展開講了。關于它的一些妙用可以看我之前寫的Android啟動優(yōu)化之延時加載。執(zhí)行完queueIdle()方法后,會將nextPollTimeoutMillis置為0,重新看一下消息隊列中有沒有新的消息。Handler上面將取消息的流程都講清楚了,萬事俱備,就差往消息隊列中添加消息了,該我們最熟悉的Handler出場了。Handler往隊列中添加消息,主要有兩種方式:Handler.sendXXX();
Handler.postXXX();第一種主要是發(fā)送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發(fā)送消息的時候,會通過SystemClock.uptimeMillis()獲取一個開機時間,然后MessageQueue就會根據(jù)這個時間來對Message進行排序。所以enqueueMessage()方法中就分了兩種情況,一種是直接可以在隊頭插入的。一種是排在中間,需要遍歷一下,然后尋一個合適的坑插入。when==0對應的是Handler的sendMessageAtFrontOfQueue()和postAtFrontOfQueue()方法。needWake的作用是根據(jù)情況喚醒Looper線程。上面有一點還沒有講,就是Looper從MessageQueue中取出Message后,會交由Handler進行消息的分發(fā)。public
void
dispatchMessage(Message
msg)
{
if
(msg.callback
!=
null)
{
handleCallback(msg);
}
else
{
if
(mCallback
!=
null)
{
if
(mCallback.handleMessage(msg))
{
return;
}
}
handleMessage(msg);
}
}優(yōu)先級順序是Message自帶的callback,接著是Handler自帶的callback,最后才是handleMessage()這個回調(diào)。ThreadLocal還記得Looper中有一個ThreadLocal吧,把它放到最后來講是因為它可以單獨拿出來講,不想在上面干擾到整個流程。ThreadLocal是一個數(shù)據(jù)存儲類,它最神奇的地方就是明明是同一個ThreadLocal對象,但是在不同線程中可以存儲不同的對象,比如說在線程A中存儲了"Hello",而在線程B中存儲了"World"。它們之間互相不干擾。在Handler機制中,由于一個Looper對應著一個線程,所以將Looper存進ThreadLocal最合適不過了。ThreadLocal比價常用的就set()和get()方法。分別來看看怎么實現(xiàn)的吧。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. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
- 6. 下載文件中如有侵權(quán)或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 人教版初中語文七下《駱駝祥子》基礎復習必刷題(附答案)
- 2026年靈臺縣人民法院招聘備考題庫有答案詳解
- 2026年西安長安大學工程設計研究院有限公司招聘備考題庫含答案詳解
- 2026年欽州市交通運輸局機關及局屬事業(yè)單位編外工作人員和“12328”熱線工作人員招聘8人備考題庫及參考答案詳解一套
- 2025年企業(yè)檔案管理與信息化手冊
- 2025年法律法規(guī)查詢與適用指南
- 2025年大學漢語言文學(現(xiàn)當代文學)試題及答案
- 2026年智慧物流倉儲機器人報告及未來五至十年供應鏈優(yōu)化報告
- 2026年建筑行業(yè):3D打印結(jié)構(gòu)技術創(chuàng)新與綠色建筑行業(yè)創(chuàng)新報告
- 2025年鄉(xiāng)村文化節(jié)五年活動風險管理報告
- 2025年體育教師個人年終述職報告
- 實際問題與一次函數(shù)課件2025-2026學年人教版八年級數(shù)學下冊
- 2025年天津科技大學毛澤東思想和中國特色社會主義理論體系概論期末考試模擬題及答案1套
- 2024年鹽城市體育局直屬事業(yè)單位招聘真題
- 南方航空安全員培訓
- 2025-2026學年嶺南美版(新教材)初中美術七年級上冊期末綜合測試卷及答案
- DB11∕T 2398-2025 水利工程巡視檢查作業(yè)規(guī)范
- 2025秋國家開放大學《政府經(jīng)濟學》期末機考精準復習題庫
- 2025-2026學年教科版(新教材)二年級上冊科學全冊知識點梳理歸納
- MDT在老年髖部骨折合并癥患者中的應用策略
- PCB設計規(guī)范-MD元器件封裝庫尺寸要求
評論
0/150
提交評論