版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡介
【移動(dòng)應(yīng)用開發(fā)技術(shù)】Android的HandlerLooperMessage機(jī)制應(yīng)用實(shí)例與詳解(二)
上一篇博文給出了Android中基于HandlerLooper機(jī)制實(shí)現(xiàn)線程間通信的兩個(gè)典型實(shí)例。本文將對(duì)該機(jī)制的基本原理進(jìn)行較深入的研究。個(gè)人認(rèn)為,學(xué)好Android編程最好的老師就是Android的源代碼,下面將基于Android-19的源碼進(jìn)行分析,重點(diǎn)闡述分析思路。
要分析HandlerLooper機(jī)制,自然想到去看Handler類和Looper類的源碼(分別位于Handler.java和Looper.java兩個(gè)文件中)。簡單閱讀兩個(gè)類的描述后,在Looper類的描述中能找到以下一段示例代碼。*
<p>This
is
a
typical
example
of
the
implementation
of
a
Looper
thread,
*
using
the
separation
of
{@link
#prepare}
and
{@link
#loop}
to
create
an
*
initial
Handler
to
communicate
with
the
Looper.
*
*
<pre>
*
class
LooperThread
extends
Thread
{
*
public
Handler
mHandler;
*
*
public
void
run()
{
*
Looper.prepare();
*
*
mHandler
=
new
Handler()
{
*
public
void
handleMessage(Message
msg)
{
*
//
process
incoming
messages
here
*
}
*
};
*
*
Looper.loop();
*
}
*
}</pre>
*/
這段代碼給出了HandlerLooper機(jī)制實(shí)現(xiàn)進(jìn)程間通信的三大基本步驟,包括Looper的兩個(gè)函數(shù)prepare()和loop(),以及Handler的handleMessage函數(shù)。上一篇博文中實(shí)例二模擬子線程向UI主線程傳遞信息的程序就基本上是直接copy這段示例代碼實(shí)現(xiàn)的。
先看第一個(gè)步驟:調(diào)用Looper.prepare()函數(shù),猜測(cè)應(yīng)該是創(chuàng)建Looper對(duì)象,做些初始化工作。代碼如下:/**
Initialize
the
current
thread
as
a
looper.
*
This
gives
you
a
chance
to
create
handlers
that
then
reference
*
this
looper,
before
actually
starting
the
loop.
Be
sure
to
call
*
{@link
#loop()}
after
calling
this
method,
and
end
it
by
calling
*
{@link
#quit()}.
*/
public
static
void
prepare()
{
prepare(true);
}
直接調(diào)用了重載函數(shù)prepare(true);//ThreadLocal實(shí)例為多個(gè)線程共享,但保證每個(gè)線程的存儲(chǔ)空間相互獨(dú)立
static
final
ThreadLocal<Looper>
sThreadLocal
=
new
ThreadLocal<Looper>();
//消息隊(duì)列
final
MessageQueue
mQueue;
//當(dāng)前線程引用
final
Thread
mThread;
private
static
void
prepare(boolean
quitAllowed)
{
//保證一個(gè)線程最多只能創(chuàng)建一個(gè)Looper對(duì)象。
if
(sThreadLocal.get()
!=
null)
{
throw
new
RuntimeException("Only
one
Looper
may
be
created
per
thread");
}
//創(chuàng)建Looper對(duì)象并存儲(chǔ)到當(dāng)前線程獨(dú)立的存儲(chǔ)空間中
sThreadLocal.set(new
Looper(quitAllowed));
}
private
Looper(boolean
quitAllowed)
{
//創(chuàng)建消息隊(duì)列(線程間即通過該消息隊(duì)列實(shí)現(xiàn)通信)
mQueue
=
new
MessageQueue(quitAllowed);
//獲得當(dāng)前線程的引用
mThread
=
Thread.currentThread();
}
看到這里明白了,Looper的prepare函數(shù)實(shí)際上創(chuàng)建了Looper對(duì)象并把對(duì)象保存到當(dāng)前線程獨(dú)立的存儲(chǔ)空間中。這里,Looper的構(gòu)造函數(shù)是私有的,所以外部無法直接通過newLooper()隨意創(chuàng)建Looper對(duì)象。而只能通過Looper的prepare()函數(shù)創(chuàng)建。這樣做能夠保證對(duì)于某一個(gè)線程,最多只會(huì)創(chuàng)建一個(gè)Looper對(duì)象的實(shí)例,這實(shí)際上就是設(shè)計(jì)模擬中的單體模式。此外,在Looper的私有構(gòu)造函數(shù)中還創(chuàng)建了消息隊(duì)列并獲得當(dāng)前線程(即創(chuàng)建Looper的線程)的引用。
先跳過Handler.handleMessage(Messagemsg),直接看Looper.loop()的實(shí)現(xiàn)。/**
*
Run
the
message
queue
in
this
thread.
Be
sure
to
call
*
{@link
#quit()}
to
end
the
loop.
*/
public
static
void
loop()
{ui
//myLooper()函數(shù)通過sThreadLocal.get()判斷當(dāng)前線程是否已經(jīng)創(chuàng)建了Looper的實(shí)例
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();
//這里開始無限循環(huán)
for
(;;)
{
//從消息隊(duì)列中取出一條消息
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
Printer
logging
=
me.mLogging;
if
(logging
!=
null)
{
logging.println(">>>>>
Dispatching
to
"
+
msg.target
+
"
"
+
msg.callback
+
":
"
+
msg.what);
}
//這里是關(guān)鍵,調(diào)用了dispatchMessage函數(shù)對(duì)從消息隊(duì)列取出的msg進(jìn)行分派
msg.target.dispatchMessage(msg);
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);
}
//消息使命完成,將其放回消息池
msg.recycle();
}
}
到這里,Looper扮演的角色已經(jīng)明朗了,主要就是通過loop()函數(shù)中的那個(gè)無限循環(huán)不斷從消息隊(duì)列中取出消息,并通過dispatchMessage()方法將消息派送出去。那么消息隊(duì)列中的消息從哪里來,又會(huì)被派送到哪里呢?
先來分析第一個(gè)問題,消息從哪里來。上一篇博文的實(shí)例中,消息源線程均通過調(diào)用Handler的sendMessage()函數(shù)來發(fā)送消息。進(jìn)入Handler.java文件看其中的sendMessage()函數(shù)。
public
final
boolean
sendMessage(Message
msg)
{
return
sendMessageDelayed(msg,
0);
}
這里調(diào)用了sendMessageDelayed,延時(shí)0毫秒。public
final
boolean
sendMessageDelayed(Message
msg,
long
delayMillis)
{
if
(delayMillis
<
0)
{
delayMillis
=
0;
}
return
sendMessageAtTime(msg,
SystemClock.uptimeMillis()
+
delayMillis);
}
進(jìn)一步調(diào)用了sendMessageAtTime,在當(dāng)前時(shí)刻發(fā)出。public
boolean
sendMessageAtTime(Message
msg,
long
uptimeMillis)
{
//獲得消息隊(duì)列的引用
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);
}
再看enqueueMessage函數(shù)。private
boolean
enqueueMessage(MessageQueue
queue,
Message
msg,
long
uptimeMillis)
{
//設(shè)置target
handler
msg.target
=
this;
if
(mAsynchronous)
{
msg.setAsynchronous(true);
}
//將消息插入到消息隊(duì)列中
return
queue.enqueueMessage(msg,
uptimeMillis);
}
看到這里已經(jīng)明朗了,消息源線程通過Handler.sendMessage發(fā)送消息,實(shí)際上就是把消息插入了與之關(guān)聯(lián)的消息隊(duì)列中。在enqueueMessage函數(shù)中有一條關(guān)鍵語句msg.target=this,通過這條語句就把Handler和Looper關(guān)聯(lián)起來了(在Looper.loop()的循環(huán)中就是通過msg.target屬性找到發(fā)送消息的Handler并調(diào)用其dispatchMessage()函數(shù)派發(fā)消息的).
搞清楚了消息從哪里來,接下來分析消息被派發(fā)到哪里,接著看dispatchMessage()函數(shù)的實(shí)現(xiàn)。/**
*
Handle
system
messages
here.
*/
public
void
dispatchMessage(Message
msg)
{
//若消息對(duì)象實(shí)現(xiàn)了其中的Runnable接口,調(diào)用對(duì)應(yīng)的回調(diào)函數(shù),即為message.callback.run())
if
(msg.callback
!=
null)
{
handleCallback(msg);
}
else
{
//若實(shí)現(xiàn)了Handler類的Callback接口,調(diào)用接口的回調(diào)函數(shù)
if
(mCallback
!=
null)
{
if
(mCallback.handleMessage(msg))
{
return;
}
}
//將消息返回Handler的消息處理回調(diào)函數(shù)(是Handler的成員函數(shù),示例代碼中回調(diào)的就是該函數(shù))
handleMessage(msg);
}
}
可見,dispatchMessage函數(shù)中根據(jù)msg,handler對(duì)象的設(shè)置情況調(diào)用相應(yīng)的消息處理回調(diào)函數(shù),我們只需要在這個(gè)回調(diào)函數(shù)中添加代碼,就可以進(jìn)行消息處理。示例代碼的第二個(gè)步驟中的handleMessage函數(shù)就是在這里被回調(diào)的。
下面回到示例代碼中的第二個(gè)步驟:*
mHandler
=
new
Handler()
{
*
public
void
handleMessage(Message
msg)
{
*
//
process
incoming
messages
here
*
}
*
};
這段代碼創(chuàng)建了Handler的對(duì)象,并覆蓋了其中的HandleMessage方法,用戶可以添加自己的消息處理函數(shù)。
handleMessage回調(diào)函數(shù)上面已經(jīng)分析過了,下面主要看看創(chuàng)建handler對(duì)象時(shí)都做了哪些事情。轉(zhuǎn)入Handler.java文件,看Handler的構(gòu)造函數(shù)(找不帶參數(shù)那個(gè))。/**
*
Default
constructor
associates
this
handler
with
the
{@link
Looper}
for
the
*
current
thread.
*
*
If
this
thread
does
not
have
a
looper,
this
handler
won't
be
able
to
receive
messages
*
so
an
exception
is
thrown.
*/
public
Handler()
{
this(null,
false);
}
調(diào)用了下面的雙參數(shù)的構(gòu)造函數(shù)。final
Looper
mLooper;
final
MessageQueue
mQueue;
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());
}
}
//獲得當(dāng)前線程Looper對(duì)象的引用
mLooper
=
Looper.myLooper();
if
(mLooper
==
null)
{
throw
new
RuntimeException(
"Can't
create
handler
inside
thread
that
has
not
called
Looper.prepare()");
}
//獲得Looper關(guān)聯(lián)的消息隊(duì)列
mQueue
=
mLooper.mQueue;
mCallback
=
callback;
mAsynchronous
=
async;
}
到這里發(fā)現(xiàn)Handler的構(gòu)造函數(shù)主要做了兩件事:1)獲得當(dāng)前線程Looper對(duì)象的應(yīng)用.2)獲得與Looper關(guān)聯(lián)的消息隊(duì)列的引用。到這里,Handler、Looper、消息隊(duì)列三者就已經(jīng)關(guān)聯(lián)起來了。
下面通過一個(gè)示意圖對(duì)上面的分析進(jìn)行總結(jié)。
由圖可見,基于HandlerLooper機(jī)制傳遞消息主要包括以下幾個(gè)步驟。
(1)目標(biāo)線程調(diào)用Looper.prepare()創(chuàng)建Looper對(duì)象和消息隊(duì)列。
(2)目標(biāo)線程通過newHandler()創(chuàng)建handler對(duì)象,將Handler,Looper,消息隊(duì)列三者關(guān)聯(lián)起來。并覆蓋其handleMessage函數(shù)。
(3)目標(biāo)線程調(diào)用Looper.loop()監(jiān)聽消息隊(duì)列。
(4)消息源線程調(diào)用Handler.sendMessage發(fā)送消息。
(5)消息源線程
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 條碼數(shù)據(jù)采集終端規(guī)范(征求意見稿)-編制說明
- 2024版《5年高考3年模擬》語文分層講練05-專題五 古代詩歌閱讀
- 《為了忘卻的記念》課件+2024-2025學(xué)年統(tǒng)編版高中語文選擇性必修中冊(cè)
- 2孩的離婚協(xié)議書范本
- 2個(gè)娃兒的離婚協(xié)議書范文范本
- 《計(jì)算機(jī)網(wǎng)絡(luò)安全防護(hù)技術(shù)(第二版)》 課件 第2章-任務(wù)2.4 安全區(qū)域間通過NAT訪問
- 溶液配制基礎(chǔ)
- 餐飲行業(yè)就業(yè)指導(dǎo)
- 綜藝節(jié)目制作技巧
- 河南青桐鳴2025屆高三十月大聯(lián)考(高三)語文含答案
- 高中數(shù)學(xué)-1.1 集合的概念教學(xué)設(shè)計(jì)學(xué)情分析教材分析課后反思
- 建筑工地安全生產(chǎn)
- 慢病知識(shí)問答大全
- 產(chǎn)褥期及產(chǎn)褥期疾病課件
- 傳感器原理及應(yīng)用電容式傳感器演示文稿
- 大學(xué)生心理健康教育全套PPT完整教學(xué)課件
- 念奴嬌·赤壁懷古省賽一等獎(jiǎng)-完整版課件
- 飛盤教案完整版
- 變壓器單相變壓器單相變壓器的負(fù)載運(yùn)行
- 變壓器容量的選擇與計(jì)算
- 統(tǒng)編版四年級(jí)語文上冊(cè)第三單元教材分析
評(píng)論
0/150
提交評(píng)論