【移動(dòng)應(yīng)用開發(fā)技術(shù)】Android的Handler Looper Message機(jī)制應(yīng)用實(shí)例與詳解(二)_第1頁
【移動(dòng)應(yīng)用開發(fā)技術(shù)】Android的Handler Looper Message機(jī)制應(yīng)用實(shí)例與詳解(二)_第2頁
【移動(dòng)應(yīng)用開發(fā)技術(shù)】Android的Handler Looper Message機(jī)制應(yīng)用實(shí)例與詳解(二)_第3頁
【移動(dòng)應(yīng)用開發(fā)技術(shù)】Android的Handler Looper Message機(jī)制應(yīng)用實(shí)例與詳解(二)_第4頁
【移動(dòng)應(yīng)用開發(fā)技術(shù)】Android的Handler Looper Message機(jī)制應(yīng)用實(shí)例與詳解(二)_第5頁
已閱讀5頁,還剩6頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(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ì)自己和他人造成任何形式的傷害或損失。

評(píng)論

0/150

提交評(píng)論