源碼講解Handler機制


從事Android開發(fā)經(jīng)常使用的Handler來進行異步操作套利,網(wǎng)上也有較多的說明著瓶,但是講解得都太淺了慎皱,只能知道期大概原理地来,本章從源碼角度來分析Hnadler機制壳贪。牽扯到的類有Handler密末,Message, MessageQueue, Looper告希。

Handler : 一個Handler允許在允許的線程中發(fā)送跟處理消息和Runnable對象樟凄。每個Handler對象跟單個線程以及線程中的消息隊列關聯(lián)在一起楞捂。

Message: 消息包含一個描述和任意的數(shù)據(jù)對象,可以使用Handler發(fā)送到消息隊列中薄坏。這個對象包含兩個額外的int字段和一個額外Object字段。

MessageQueue:用來存放線程放入的消息,要通過handler? sendMessage來添加消息寨闹,它的處理跟Looper有關颤殴。

Looper: 類用于運行一個消息循環(huán)的線程。

先分析Hanlder類:

其構造方法有好幾個:


public Handler() {}

publicHandler(Callback callback) {}

publicHandler(Looper looper) {}

publicHandler(booleanasync){}

publicHandler(Callback callback,booleanasync) {}

publicHandler(Looper looper, Callback callback,booleanasync) {}

需要注意的地方鼻忠,構造可以看出有一個Looper對象的構造涵但,此構造會把Hanlder跟Looper綁定,如果是子線程中的Looper那么Handler處理的消息也將在子線程中帖蔓。

Callback對象如果不為空的話矮瘟,將使用Callback來處理Message。

大家經(jīng)常使用有 sendEmpryMessage系列塑娇,SendMessage系列澈侠,post(Runnable)系列,而這些所有的發(fā)送消息的方法最后是都運行到 public boolean sendMessageAtTime(Message msg,long uptimeMillis)埋酬,在此方法內把Message 插入到MessageQueue中哨啃,并根據(jù)uptimeMillis來判斷插入的位置。

先分析一下各種send 跟 post 如何跳轉到sendMessageAtTime這個方法的写妥。

public final boolean sendEmptyMessage(intwhat){

return sendEmptyMessageDelayed(what,0);} //sendEmptyMessage(what) 其實跟sendEmptyMessageDelayed是一樣的拳球,只不過延時為0

再看public final boolean sendEmptyMessageDelayed(intwhat,long delayMillis) {

Message msg = Message.obtain();

msg.what= what;

return sendMessageDelayed(msg, delayMillis);}

把What進行了一下封裝,畢竟Message存的都是Message

public final boolean sendMessage(Message msg){ return sendMessageDelayed(msg,0);}

再看public final boolean sendMessageDelayed(Message msg,longdelayMillis){

if(delayMillis <0) { delayMillis = 0;}

return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);}

// 所謂延時就是在現(xiàn)在的基礎上 加上要延時的時間就是要執(zhí)行的時間

public boolean sendMessageAtTime(Message msg,longuptimeMillis) {

MessageQueue queue =mQueue;

if(queue ==null) {

RuntimeException e =newRuntimeException(

this+" sendMessageAtTime() called with no mQueue");

Log.w("Looper", e.getMessage(), e);

return false;

}

??? returnenqueueMessage(queue, msg, uptimeMillis);

}

sendMesage系列跟sendEmptyMessage系列到sendMessageAtTime()的核心代碼都已經(jīng)貢上珍特。

再看post系列public final boolean post(Runnable r){

return sendMessageDelayed(getPostMessage(r),0);}

private staticMessage getPostMessage(Runnable r) {

Message m = Message.obtain();

m.callback= r;

returnm;}

這個就不難理解了祝峻,獲取一個新的Message把Runnable放置到的Message中,再調用SendMessage系列的函數(shù)。

同樣的post系列就不多解析了莱找,上源碼 :

public final boolean postDelayed(Runnable r,longdelayMillis){

return sendMessageDelayed(getPostMessage(r), delayMillis);}

public final boolean postAtFrontOfQueue(Runnable r)

{ return sendMessageAtFrontOfQueue(getPostMessage(r));}

public final boolean sendMessageAtFrontOfQueue(Message msg) {

MessageQueue queue =mQueue;

if(queue ==null) {

RuntimeException e =newRuntimeException(

this+" sendMessageAtTime() called with no mQueue");

Log.w("Looper", e.getMessage(), e);

return false;

}

return enqueueMessage(queue, msg,0);

}? // 此處直接調用到 enqueueMessage(queue, msg,0) 沒有走到sendMessageAtTime()


現(xiàn)在來說下private boolean enqueueMessage(MessageQueue queue, Message msg,longuptimeMillis) {

msg.target=this;

if(mAsynchronous) {

msg.setAsynchronous(true);

}

return queue.enqueueMessage(msg, uptimeMillis);

} 最終將調用到消息隊列的enqueueMessage方法酬姆。

在講解enqueueMessage的方法之前,要先把Message這個類要說下奥溺,要處理的數(shù)據(jù)來源都是此類的辞色。


public final classMessageimplementsParcelable {

public int what;

public int arg1;

public int arg2;

public Object obj;

/*package*/long when;

/*package*/Bundle data;

/*package*/Handler target;

/*package*/Runnable callback;

// sometimes we store linked lists of these things

/*package*/Message next;

} what arg1 arg2 obj這幾個方法大家都熟悉,就是要處理的數(shù)據(jù)浮定,里面還有個Bundle data淫僻,大家也是可以用來通過 getData() /peekData() 跟 setData() 存取數(shù)據(jù)。 而when呢壶唤,就是enqueueMessage函數(shù)的最后一個參數(shù)雳灵,用來表示是什么時候要運行的,插入到MessageQueue跟MessageQueue中取出Message也是通過此變量來做判斷依據(jù)的。target 就是代表被哪個Handler放置到消息隊列中的闸盔,在Message被取出時悯辙,也就是由那個Handle對象來處理的。而callback呢迎吵? 在前面的代碼中應該看見過了( 見getPostMessage)躲撰,post的runnable就是賦值給了message的callback,具體什么時候被調用請往下看击费。

next變量 這個又是啥東西呢拢蛋,熟悉列表跟隊列的同志應該清楚,列表只用一個引用一個頭就好了蔫巩,那么MesageQueue也是一樣的谆棱,MessageQueue中其實只存了一個Message對象,遍歷是通過其next對象來遍歷的圆仔。

還有幾個大家可能使用到函數(shù)的代碼也貼一下源碼垃瞧,都比較簡單,就請各位自己品味了坪郭。

public Bundle getData() { // 得到bundle數(shù)據(jù)

if(data==null) {

data=newBundle();

}

return data;

}

public voidsetData(Bundle data) {

this.data= data;

}

publicBundle peekData() {

returndata;

}


public voidsendToTarget() {

target.sendMessage(this);

}


public staticMessage obtain() {? //從全局池返回一個新的消息實例个从。讓我們在許多情況下避免分 //配新對象。

synchronized(sPoolSync) {

if(sPool!=null) {

Message m =sPool;

sPool= m.next;

m.next=null;

m.flags=0;// clear in-use flag

sPoolSize--;

return m;

}

}

return newMessage();

}


現(xiàn)在說一下MessageQueue歪沃,通過Handle跟Message的一些說明嗦锐,那么了解MessageQueue就簡單多了,其實也是蠻復雜的沪曙,但是我們只關心我們要關心的就可以了奕污,只要看enqueueMessage這個方法就好了。

boolean enqueueMessage(Message msg, longwhen) {

// xxxx?

//xxx


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;

}

主要做的就是根據(jù)when來確定新插進來的Message要插入到那個地方(需要注意的MessageQueue只有隊列頭的引用)珊蟀,并給When賦值給Message的when菊值。


現(xiàn)在來看最終的Looper,里面主要有一個如何取出Message的方法育灸,比較復雜

looper里面有一個MessageQueue腻窒,調用Loop方法后就開啟了循環(huán)檢測看Loop方法

/**

* Run the message queue in this thread. Be sure to call

* {@link#quit()} to end the loop.

*/

public static voidloop() {

finalLooper me =myLooper();

if(me ==null) {

throw newRuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");

}

finalMessageQueue 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 longident = Binder.clearCallingIdentity();

for(;;) {

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);

}

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 longnewIdent = 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.recycleUnchecked();

}

}

可以看到在for循環(huán)是個死循環(huán)里面都要用了MessageQueue的Next方法,看MessageQueue的next方法

Message next() {

// Return here if the message loop has already quit and been disposed.

// This can happen if the application tries to restart a looper after quit

// which is not supported.

final longptr =mPtr;

if(ptr ==0) {

return null;

}

intpendingIdleHandlerCount = -1;// -1 only during first iteration

intnextPollTimeoutMillis =0;

for(;;) {

if(nextPollTimeoutMillis !=0) {

Binder.flushPendingCommands();

}

nativePollOnce(ptr, nextPollTimeoutMillis);

synchronized(this) {

// Try to retrieve the next message.? Return if found.

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

returnmsg;

}

}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

pendingIdleHandlerCount =mIdleHandlers.size();

}


}

mPendingIdleHandlers=mIdleHandlers.toArray(mPendingIdleHandlers);

}


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;

}

}


說實在話沒有看懂磅崭,但是結果就是Loop根據(jù)When循環(huán)出去MessageQueue里面的Message

取出后調用msg.target.dispatchMessage(msg);

也就是Handler里面的派發(fā)消息 dispatchMessage儿子。轉了半天終于回到了Handler。有幾個地方要注意的一個線程只有一個MessageQueue 一個Looper砸喻, 但是Handler跟Message是可以有多個柔逼。來看dispathMessage這個方法:

public voiddispatchMessage(Message msg) {

if(msg.callback!=null) {

handleCallback(msg);

}else{

if(mCallback!=null) {

if(mCallback.handleMessage(msg)) {

return;

}

}

handleMessage(msg);

}

private static voidhandleCallback(Message message) {

message.callback.run();

}

}

以上代碼的意思是? 如果Message中的callback不為空,就調用callback中的run方法割岛,也就是Post里面的那個Runable

如果Handler里面的Callback(前面構造有說)不為空愉适,就是調用CallBack里面的handleMessage() 方法, 如果不是以上2種情況就是Handler自己要重載處理handleMessage()的方法

從SendMessage到HandlerMesage的方法都已經(jīng)說完了癣漆∥蹋總體來說

1.Handler發(fā)送各種消息, 然后對各種消息進行封裝成Message對象

2.根據(jù)時間把Mesage插入到Looper的MessageQueue中惠爽,并把Message標上時間

3.loop循環(huán)取出Message(此步驟最為復雜了癌蓖,好幾個地方?jīng)]有想通,里面也調用了好幾個native方法)

4.取出之后對Message進行派發(fā)處理婚肆。

打個比喻:有3個人(Hanlder)H1租副,H2,H3, 他們有一些包裹(Message)较性,每個包裹上都標注了什么時候取出來用僧,H1, H2赞咙,H3把他們的包裹存進銀行(MessageQueue)里永毅,存進銀行的時候包裹上被標記是誰(H1, H2, H3)的包裹,并按照要取出的時間的順序進行了排序人弓,銀行的工作人員(Loop)沼死,會不斷的檢查包裹是否要取出來了,到了指定的時間取出時間崔赌,包裹被工作人員打開意蛀。根據(jù)包裹上標記的,寄送給被標記的人H1 H2 或者H3健芭。在H1 H2 或者H3收到之后县钥,查看包裹里面是否寫有包裹里面的東西要給誰(Message的CallBack),如果包裹上寫了要給誰慈迈,那么就給那個人處理(Message的CallBack也就運行了)若贮。如果沒有寫給誰出來省有,那么H1看下是否有認識的人(H1的Callback)能處理這個包裹,如果有那就給CallBack處理谴麦,如果沒有那么就只能自己來出來了(要重載HandleMessage方法)蠢沿。

另外要注意的是post 的runnable的那個Message的what方法是沒有賦值的,也就是默認值0, 所以最好大家自己發(fā)送message的時候不要把message的what賦值為0 或者不賦值匾效,以免引起不必要的麻煩舷蟀。

Handler是可以跟子線程綁定在一起的,并不是只能運行在UI線程中的面哼,Android的UI線程默認是調用了prepare()跟Loop()的野宜。

例如 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();

?? ? }

插個圖:解析 2個Handler對象H1 H2向MessageQueue發(fā)送Message,此時MessageQueue中已經(jīng)有3個Message了其中有一個來自H1 2個來自H2.而且H1的一個Message被取出了魔策,根據(jù)Message的信息做對應的處理匈子。


不得不吐槽一下簡書的排版,代碼copy過來的排版真不好處理(不知道是不是我會用)闯袒,希望官方給一個好的排版旬牲。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市搁吓,隨后出現(xiàn)的幾起案子原茅,更是在濱河造成了極大的恐慌,老刑警劉巖堕仔,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件擂橘,死亡現(xiàn)場離奇詭異,居然都是意外死亡摩骨,警方通過查閱死者的電腦和手機通贞,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來恼五,“玉大人昌罩,你說我怎么就攤上這事≡致” “怎么了茎用?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長睬罗。 經(jīng)常有香客問我轨功,道長,這世上最難降的妖魔是什么容达? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任古涧,我火速辦了婚禮,結果婚禮上花盐,老公的妹妹穿的比我還像新娘羡滑。我一直安慰自己菇爪,他們只是感情好,可當我...
    茶點故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布柒昏。 她就那樣靜靜地躺著凳宙,像睡著了一般。 火紅的嫁衣襯著肌膚如雪昙楚。 梳的紋絲不亂的頭發(fā)上近速,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天诈嘿,我揣著相機與錄音堪旧,去河邊找鬼。 笑死奖亚,一個胖子當著我的面吹牛淳梦,可吹牛的內容都是我干的。 我是一名探鬼主播昔字,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼爆袍,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了作郭?” 一聲冷哼從身側響起陨囊,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎夹攒,沒想到半個月后蜘醋,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡咏尝,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年压语,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片编检。...
    茶點故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡胎食,死狀恐怖,靈堂內的尸體忽然破棺而出允懂,到底是詐尸還是另有隱情厕怜,我是刑警寧澤,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布蕾总,位于F島的核電站酣倾,受9級特大地震影響,放射性物質發(fā)生泄漏谤专。R本人自食惡果不足惜躁锡,卻給世界環(huán)境...
    茶點故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望置侍。 院中可真熱鬧映之,春花似錦拦焚、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至蠢甲,卻和暖如春僵刮,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背鹦牛。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工搞糕, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人曼追。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓窍仰,卻偏偏與公主長得像,于是被迫代替她去往敵國和親礼殊。 傳聞我的和親對象是個殘疾皇子驹吮,可洞房花燭夜當晚...
    茶點故事閱讀 44,713評論 2 354

推薦閱讀更多精彩內容