Hanlder異步消息自總結

由于在UI線程中不能做耗時長的操作,所以系統(tǒng)提供了Handler和AsyncTask來進行異步消息處理和任務;

異步消息處理機制Handler

Android中的異步消息處理主要由四個部分組成,Message、Handler、MessageQueue和Looper。

Message

Message是在線程之間傳遞的消息,它可以在內部攜帶少量的信息,用于在不同線程之間交換數(shù)據(jù)。

Handler

Handler顧名思義也就是處理者的意思,它主要是用于發(fā)送和處理消息的。發(fā)送消息一般是使用Handler的sendMessage()或者post()方法,而發(fā)出的消息經過一系列地輾轉處理后,最終會傳遞到Handler的handleMessage()方法中。

MessageQueue

MessageQueue是消息隊列的意思,它主要是用于存放所有的Handler發(fā)送的消息。這部分消息會一直存在于消息隊列中,等待被處理。每個線程中只有一個MessageQueue對象。

Looper

Looper調用Looper的loop()方法后,就會進入到一個無限循環(huán)當中,然后每當發(fā)現(xiàn)MessageQueue中存在一條消息,就會將它取出,并傳遞到Handler的handleMessage()方法中。每個線程中也只會有一個Looper對象。

Handler源碼分析:

Handler的構造函數(shù)

Handler的定義屬性:

finalMessageQueuemQueue;

finalLoopermLooper;

finalCallbackmCallback;

finalbooleanmAsynchronous;

IMessengermMessenger;

在Handler中定義了7個構造函數(shù),分別是:Handler()、Handler(Callback callback)、Handler(Looper

looper)、Handler(Looper looper, Callback callback)、Handler(boolean

async)、Handler(Callback callback, boolean async)、Handler(Looper

looper, Callback callback, boolean async);在上面我們已經說過Handler異步消息機制主要包含MessageQueue、Looper和Message三個部分,那么首先要分析的這三個屬性怎么賦值;

構造方法一:

public Handler(Looper looper, Callback callback, boolean async) {

mLooper = looper;

mQueue = looper.mQueue;//在Looper中進行分析

mCallback = callback;

mAsynchronous = async;

}

構造方法二:

publicHandler(Callback callback, boolean async) {

if (FIND_POTENTIAL_LEAKS) {

final Class 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());

}

}暫時不管

mLooper = Looper.myLooper();//在looper中分享

if (mLooper == null) {

throw new RuntimeException(

"Can't create handlerinside thread that has not called Looper.prepare()");

}

mQueue = mLooper.mQueue;

mCallback = callback;

mAsynchronous = async;

}

下面看下我們在使用Handler是常用的空參構造函數(shù)的調用

public Handler() {

this(null, false);//調用Handler(Callback callback, booleanasync)

}

可以看到最終會在構造函數(shù)中進行對mLooper和mQueue的賦值;

Handler消息初始化

在Handler中對消息的初始化主要是重載了5個obtainMessage()方法;分別是

publicfinalMessageobtainMessage()

{

returnMessage.obtain(this);

}

publicfinalMessageobtainMessage(intwhat)

{

returnMessage.obtain(this, what);

}

publicfinalMessageobtainMessage(intwhat, Object obj)

{

returnMessage.obtain(this, what,obj);

}

publicfinalMessageobtainMessage(intwhat,intarg1,intarg2)

{

returnMessage.obtain(this, what,arg1, arg2);

}

publicfinalMessageobtainMessage(intwhat,intarg1,intarg2, Object obj)

{

returnMessage.obtain(this, what,arg1, arg2, obj);

}

可以看到在創(chuàng)建消息Message時會傳進去一個參數(shù)this即當前Handler,這點很重要,我們知道在消息處理時要找到對應的處理消息的Handler;

Handler中的send方法

當handler在分線程進行完耗時的操作后并完成消息創(chuàng)建及配置,那么下面就進消息的發(fā)送;在handler中主要有:

空消息:

sendEmptyMessage(int what)

sendEmptyMessageDelayed(int what, long

delayMillis)

sendEmptyMessageAtTime(int what, long

uptimeMillis)

非空消息:

sendMessage(Message

msg)發(fā)送一個普通的消息。即延時為零的消息;’

sendMessageDelayed(Message

msg, long delayMillis)發(fā)送一個延時消息;

sendMessageAtFrontOfQueue(Message

msg);

sendMessageAtTime(Message

msg, long uptimeMillis);

上述方法最終會調用Handler中的enqueueMessage(MessageQueue

queue, Message msg, long uptimeMillis)方法,enqueueMessage()又會調用MessageQueue的enqueueMessage(msg,

uptimeMillis)方法,最終將消息send到消息隊列中;

Handler中的Post方法

在Hanlder中處理定義了對消息的send方法之外,還定義了針對Runnable的post方法;相關方法:

post(Runnable r)調用方法sendMessageDelayed(getPostMessage(r),

0);

postAtTime(Runnable r, long uptimeMillis)調用sendMessageAtTime(getPostMessage(r),

uptimeMillis);

postAtTime(Runnable r, Object token, long

uptimeMillis) sendMessageAtTime(getPostMessage(r, token),

uptimeMillis);sendMessageAtTime(getPostMessage(r, token), uptimeMillis);

postDelayed(Runnable r, long delayMillis)調用sendMessageDelayed(getPostMessage(r),

delayMillis);

postAtFrontOfQueue(Runnable r)調用sendMessageAtFrontOfQueue(getPostMessage(r));

可以發(fā)現(xiàn)Handler的post方法最終是調用了相關send方法,但是所有的post方法同時調用的一個getPostMessage(r)來對消息的創(chuàng)建;實現(xiàn)如下

privatestaticMessagegetPostMessage(Runnable r) {

Message m = Message.obtain();

m.callback = r;

returnm;

}

可以看到在該方法中主要是通過將Runnable復制給m.callback來實現(xiàn);這將在對消息的分析是詳細介紹;

Handler中的消息處理過程

我們知道,Handler消息機制中只要消息隊列中存在消息,那么looper就不停的取出去消息并且將其交給Handler進行處理。這里我們暫時不關注looper是如何取出消息及分配消息,我們只關注消息是如何處理的;在Handler中對消息的處理只要一個方法:

publicvoiddispatchMessage(Message msg) {

if(msg.callback !=null) {

handleCallback(msg);

}else{

if(mCallback!=null){

if(mCallback.handleMessage(msg)) {

return;

}

}

handleMessage(msg);

}

}

在該方法中首先要判斷msg.callback !=null,而在上面我們已經分析過了只有在使用post相關的方法時msg.callback會被賦值;當msg.callback !=null時會調用handleCallback(msg):privatestaticvoidhandleCallback(Message message) {

message.callback.run();//即調用Runable的run方法

}

當msg.callback==null時首先要判斷的時mCallback!=null;在這里說明一下什么是mCallback:在Handler中定義這樣的一個final屬性:final Callback mCallback,那么什么是Callback呢?

publicinterfaceCallback {

public boolean handleMessage(Messagemsg);

}

在Handler中定義這樣一個接口,官方的定義是:

Callback interface you can use when

instantiating a Handler to avoid having to implement your own subclass of

Handler.

當mCallback!=null不為空時,如果public boolean handleMessage(Message

msg)方法返回的true,那么表示當前的message被處理,return;如果放回的絲false,那么在執(zhí)行完public boolean handleMessage(Message

msg)方法后會繼續(xù)執(zhí)行handleMessage(msg)方法,完成對數(shù)據(jù)的處理;

Handler中的消息撤回和消息隊列的判斷

Handler中定義的消息撤回的方法:

removeMessages(int what)根據(jù)what來撤回消息;

removeMessages(int what, Object object)根據(jù)what和object來撤回消息;

removeCallbacksAndMessages(Object token)根據(jù)token撤回message和callbacks,當token是null是撤回消息隊列的所有消息;

同時,handler中還定義了對消息隊列進行查詢的方法:

hasMessages(int what)

hasMessages(int what, Object object)

hasCallbacks(Runnable r)

Looper的實現(xiàn)

我們都知道handler的主要的任務就是進行線程間的通信,實現(xiàn)分線程和UI線程通信界面更新,但是我們發(fā)現(xiàn)在整個的handler中沒有關于UI線程相關的東西;那么下面我們繼續(xù)分析looper,希望能夠找到著相關的東西;和Handler分析過程一樣,我們首先看一下looper的構造函數(shù)和定義的屬性:

Looper定義的相關的熟悉如下

staticfinalThreadLocalsThreadLocal=newThreadLocal();

privatestaticLoopersMainLooper;// guarded by Looper.class

finalMessageQueuemQueue;

finalThreadmThread;

然后查找Looper的構造函數(shù),結果發(fā)現(xiàn)了:

privateLooper(booleanquitAllowed) {

mQueue=newMessageQueue(quitAllowed);//在消息隊列中分析

mThread= Thread.currentThread();

}

私有的構造函數(shù),有一種單例的感覺;接著卻找到了這樣一個方法prepare(),

publicstaticvoidprepare(){

prepare(true);

}

privatestaticvoidprepare(booleanquitAllowed) {

if(sThreadLocal.get() !=null) {

thrownewRuntimeException("Only one Looper may be created per thread");

}

sThreadLocal.set(newLooper(quitAllowed));

}

可以new Looper的,接著分析,什么是sThreadLocal,找到ThreadLocal中的set和get方法,其中set()的作用是Sets the value of this variable for the

current thread,即將looper與sThreadLocal當前線程綁定;通過prepare克可以創(chuàng)建looper,同時Looper中還定義了myLooper()方法獲取當前線程的looper;

publicstaticLoopermyLooper() {

returnsThreadLocal.get();

}

接下來就該從消息隊列中獲取消息了,即調用loop()方法;

publicstaticvoidloop() {

final Looper me = myLooper();

finalMessageQueue queue = me.mQueue;

…..

for(;;) {

Message msg = queue.next();// might block

if(msg ==null) {

// No message indicates that the

message queue is quitting.

return;

}

……

msg.target.dispatchMessage(msg);

……

}

}

主要是通過一個死循環(huán)不停的從消息隊列中取出消息,然后執(zhí)行msg.target.dispatchMessage(msg)方法即handler. dispatchMessage(msg)方法,由handler去完成消息的處理;

同時,除了消息的獲取之外,Looper還定義了兩個退出的方法quit()和quitSafely();這兩個方法都調用了Messagequeue的quit()方法,,這兩個方法的區(qū)別會在下面解析

Messagequeue時進行說明

publicvoidquit() {

mQueue.quit(false);

}

publicvoidquitSafely() {

mQueue.quit(true);

}

主線程Looper的問題,我們在UI使用Handler進行線程通信時沒有手動的創(chuàng)建Loop而是由于主線的looper系統(tǒng)已經為我們創(chuàng)建好;

消息隊列的實現(xiàn)

老規(guī)矩首先分析一下MessageQueue的構造函數(shù),發(fā)現(xiàn)

// True if the message queue can be quit.

privatefinalbooleanmQuitAllowed;

MessageQueue(booleanquitAllowed) {

mQuitAllowed= quitAllowed;

mPtr=nativeInit();

}

結合MessageQueue和Looper的構造函數(shù)和prepare()方法,我們可以知道我們自己創(chuàng)建的MessageQueue都是可以quit,但是在Looper中還定義這么一個方法:

publicstaticvoidprepareMainLooper(){

prepare(false);//這個地方為false;

synchronized(Looper.class) {

if(sMainLooper!=null) {

thrownewIllegalStateException("The main Looper has already been prepared.");

}

sMainLooper=myLooper();

}

}

下面分析一下MessageQueue的enqueueMessage()方法和next();

booleanenqueueMessage(Message msg,longwhen) {

…….

msg.when = when;

Message p =mMessages;

booleanneedWake;

if(p ==null|| when == 0 || when

//

New head, wake up the event queue if blocked.

msg.next = p;

mMessages= msg;

needWake =mBlocked;

}

else

{

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;

}

if(needWake) {

nativeWake(mPtr);

}

}

returntrue;

}

消息隊列消息的添加主要是進行相應的判斷并且循環(huán)整個消息隊列來進行按照時間的插入;

Message next() {

intpendingIdleHandlerCount = -1;// -1 only during

first iteration

intnextPollTimeoutMillis = 0;

for(;;) {

if(nextPollTimeoutMillis != 0) {

Binder.flushPendingCommands();

}

// We

can assume mPtr != 0 because the loop is obviously still running.

// The

looper will not call this method after the loop quits.

nativePollOnce(mPtr, nextPollTimeoutMillis);

synchronized(this) {

//Try to retrieve the next message.Returnif found.

finallongnow = SystemClock.uptimeMillis();

Message prevMsg =null;

Message msg =mMessages;

if(msg !=null&& msg.target ==null) {

//Stalled by a barrier.Find the nextasynchronous message in the queue.

do{

prevMsg = msg;

msg = msg.next;

}while(msg !=null&& !msg.isAsynchronous());

}

if(msg !=null) {

if(now < msg.when) {

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(false)Log.v("MessageQueue","Returning message: "+ msg);

msg.markInUse();

returnmsg;

}

}else{

//

No more messages.

nextPollTimeoutMillis = -1;

}

//

Process the quit message now that all pending messages have been handled.

if(mQuitting) {

dispose();

returnnull;

}

if(pendingIdleHandlerCount < 0

&& (mMessages==null|| now

pendingIdleHandlerCount =mIdleHandlers.size();

}

if(pendingIdleHandlerCount <= 0) {

mBlocked=true;

continue;

}

if(mPendingIdleHandlers==null) {

mPendingIdleHandlers=newIdleHandler[Math.max(pendingIdleHandlerCount, 4)];

}

mPendingIdleHandlers=mIdleHandlers.toArray(mPendingIdleHandlers);

}

for(inti = 0; i

finalIdleHandleridler =mPendingIdleHandlers[i];

mPendingIdleHandlers[i]=null;//

release the reference to the handler

booleankeep =false;

try{

keep = idler.queueIdle();

}catch(Throwable t) {

Log.wtf("MessageQueue","IdleHandler threw exception", t);

}

if(!keep) {

synchronized(this) {

mIdleHandlers.remove(idler);

}

}

}

pendingIdleHandlerCount = 0;

nextPollTimeoutMillis = 0;

}

}

voidquit(booleansafe) {

if(!mQuitAllowed) {

thrownewRuntimeException("Main thread not allowed to quit.");

}//主線程不可退出

synchronized(this) {

if(mQuitting) {

return;

}

mQuitting=true;

if(safe) {

removeAllFutureMessagesLocked();//處理完所有的消息后提出

}else{

removeAllMessagesLocked();//直接退出,不再關注消息隊列是否處理完

}

// We

can assume mPtr != 0 because mQuitting was previously false.

nativeWake(mPtr);

}

}

Message的實現(xiàn)

對于message的分析,主要是關注兩點

Handlertarget;

Runnablecallback;

Message的創(chuàng)建主要是調用了其靜態(tài)方法obtain():

* Return a newMessage instance from the global pool. Allows us to

avoidallocating new objects in many cases.

publicstaticMessageobtain() {

synchronized(sPoolSync) {

if(sPool!=null){

Message m =sPool;

sPool= m.next;

m.next=null;

sPoolSize--;

returnm;

}

}

returnnewMessage();

}

從整個Messge池中返回一個新的Message實例,在許多情況下使用它,因為它能避免分配新的對象

在上面我們已經知道在handler進行消息初始化的時候會調用obtain(Handler h)等方法其最終會通過obtain()進行消息返回并且對消息的target進行賦值;這也就是是在Loop()方法中進行消息分配的原因;Handler中的其他消息的初始化大家可以自己對應源碼去看一下;

我們在Handler的分配的時候曾經有過這樣一個判斷

if(msg.callback !=null) {

handleCallback(msg);

}

下面我們看一下msg.callback,在消息初始化的時候,當我們傳入一個Runnable時,

callback

Runnable that will execute when the message is handled.也就是說再消息發(fā)送出去后會在執(zhí)行。

publicstaticMessageobtain(Handler h, Runnable callback) {

Message m =obtain();

m.target= h;

m.callback= callback;

returnm;

}

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,825評論 6 546
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,814評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,980評論 0 384
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 64,064評論 1 319
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,779評論 6 414
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 56,109評論 1 330
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,099評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,287評論 0 291
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 49,799評論 1 338
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,515評論 3 361
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,750評論 1 375
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,221評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,933評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,327評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,667評論 1 296
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,492評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,703評論 2 380

推薦閱讀更多精彩內容