【移動應用開發(fā)技術】Android中Handler機制的工作原理是什么_第1頁
【移動應用開發(fā)技術】Android中Handler機制的工作原理是什么_第2頁
【移動應用開發(fā)技術】Android中Handler機制的工作原理是什么_第3頁
【移動應用開發(fā)技術】Android中Handler機制的工作原理是什么_第4頁
【移動應用開發(fā)技術】Android中Handler機制的工作原理是什么_第5頁
已閱讀5頁,還剩7頁未讀 繼續(xù)免費閱讀

下載本文檔

版權(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. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

最新文檔

評論

0/150

提交評論