前言
- 在
Android
開發(fā)的多線程應(yīng)用場景中袁勺,Handler
機(jī)制十分常用 - 今天麦箍,我將手把手帶你深入分析
Handler
機(jī)制的源碼腌紧,希望你們會(huì)喜歡
Anroid異步通信Handler系列文章
Android異步通信:Handler機(jī)制學(xué)習(xí)攻略
Android異步通信:Handler使用教程
Android異步通信:Handler工作原理
Android異步通信:Handler源碼分析
Android異步通信:詳解Handler內(nèi)存泄露的原因
目錄
1. Handler 機(jī)制簡介
定義
一套Android
消息傳遞機(jī)制作用
在多線程的應(yīng)用場景中,將工作線程中需更新UI
的操作信息 傳遞到 UI
主線程夜涕,從而實(shí)現(xiàn) 工作線程對UI
的更新處理颤专,最終實(shí)現(xiàn)異步消息的處理
- 為什么要用
Handler
消息傳遞機(jī)制
答:多個(gè)線程并發(fā)更新UI的同時(shí) 保證線程安全。具體描述如下
- 總結(jié)
使用Handler
的原因:將工作線程需操作UI
的消息 傳遞 到主線程钠乏,使得主線程可根據(jù)工作線程的需求 更新UI
栖秕,從而避免線程操作不安全的問題
2. 儲(chǔ)備知識
在閱讀Handler
機(jī)制的源碼分析前,請務(wù)必了解Handler
的一些儲(chǔ)備知識:相關(guān)概念晓避、使用方式 & 工作原理
2.1 相關(guān)概念
關(guān)于 Handler
機(jī)制中的相關(guān)概念如下:
在下面的講解中簇捍,我將直接使用英文名講解,即
Handler
俏拱、Message
暑塑、Message Queue
、Looper
锅必,希望大家先熟悉相關(guān)概念
2.2 使用方式
-
Handler
使用方式 因發(fā)送消息到消息隊(duì)列的方式不同而不同事格,共分為2種:使用Handler.sendMessage()
、使用Handler.post()
- 下面的源碼分析將依據(jù)使用步驟講解
若還不了解搞隐,請務(wù)必閱讀文章:Android:這是一份Handler消息傳遞機(jī)制 的使用教程
2.3 工作原理
- 理解
Handler
機(jī)制的工作原理驹愚,能很大程序幫助理解其源碼 - 具體請看文章:Android Handler:圖文解析 Handler通信機(jī)制 的工作原理
3. Handler機(jī)制的核心類
在源碼分析前,先來了解Handler
機(jī)制中的核心類
3.1 類說明
Handler
機(jī)制 中有3個(gè)重要的類:
- 處理器 類
(Handler)
- 消息隊(duì)列 類
(MessageQueue)
- 循環(huán)器 類
(Looper)
3.2 類圖
3.3 具體介紹
4. 源碼分析
- 下面的源碼分析將根據(jù)
Handler
的使用步驟進(jìn)行 -
Handler
使用方式 因發(fā)送消息到消息隊(duì)列的方式不同而不同劣纲,共分為2種:使用Handler.sendMessage()
逢捺、使用Handler.post()
若還不了解,請務(wù)必閱讀文章:Android:這是一份Handler消息傳遞機(jī)制 的使用教程
方式1:使用 Handler.sendMessage()
- 使用步驟
/**
* 此處以 匿名內(nèi)部類 的使用方式為例
*/
// 步驟1:在主線程中 通過匿名內(nèi)部類 創(chuàng)建Handler類對象
private Handler mhandler = new Handler(){
// 通過復(fù)寫handlerMessage()從而確定更新UI的操作
@Override
public void handleMessage(Message msg) {
...// 需執(zhí)行的UI操作
}
};
// 步驟2:創(chuàng)建消息對象
Message msg = Message.obtain(); // 實(shí)例化消息對象
msg.what = 1; // 消息標(biāo)識
msg.obj = "AA"; // 消息內(nèi)容存放
// 步驟3:在工作線程中 通過Handler發(fā)送消息到消息隊(duì)列中
// 多線程可采用AsyncTask癞季、繼承Thread類劫瞳、實(shí)現(xiàn)Runnable
mHandler.sendMessage(msg);
// 步驟4:開啟工作線程(同時(shí)啟動(dòng)了Handler)
// 多線程可采用AsyncTask、繼承Thread類绷柒、實(shí)現(xiàn)Runnable
- 源碼分析
下面志于,我將根據(jù)上述每個(gè)步驟進(jìn)行源碼分析
步驟1:在主線程中 通過匿名內(nèi)部類 創(chuàng)建Handler類對象
/**
* 具體使用
*/
private Handler mhandler = new Handler(){
// 通過復(fù)寫handlerMessage()從而確定更新UI的操作
@Override
public void handleMessage(Message msg) {
...// 需執(zhí)行的UI操作
}
};
/**
* 源碼分析:Handler的構(gòu)造方法
* 作用:初始化Handler對象 & 綁定線程
* 注:
* a. Handler需綁定 線程才能使用;綁定后废睦,Handler的消息處理會(huì)在綁定的線程中執(zhí)行
* b. 綁定方式 = 先指定Looper對象伺绽,從而綁定了 Looper對象所綁定的線程(因?yàn)長ooper對象本已綁定了對應(yīng)線程)
* c. 即:指定了Handler對象的 Looper對象 = 綁定到了Looper對象所在的線程
*/
public Handler() {
this(null, false);
// ->>分析1
}
/**
* 分析1:this(null, false) = Handler(null,false)
*/
public Handler(Callback callback, boolean async) {
...// 僅貼出關(guān)鍵代碼
// 1. 指定Looper對象
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
// Looper.myLooper()作用:獲取當(dāng)前線程的Looper對象郊楣;若線程無Looper對象則拋出異常
// 即 :若線程中無創(chuàng)建Looper對象憔恳,則也無法創(chuàng)建Handler對象
// 故 若需在子線程中創(chuàng)建Handler對象,則需先創(chuàng)建Looper對象
// 注:可通過Loop.getMainLooper()可以獲得當(dāng)前進(jìn)程的主線程的Looper對象
// 2. 綁定消息隊(duì)列對象(MessageQueue)
mQueue = mLooper.mQueue;
// 獲取該Looper對象中保存的消息隊(duì)列對象(MessageQueue)
// 至此净蚤,保證了handler對象 關(guān)聯(lián)上 Looper對象中MessageQueue
}
從上面可看出:
當(dāng)創(chuàng)建Handler
對象時(shí)钥组,則通過 構(gòu)造方法 自動(dòng)關(guān)聯(lián)當(dāng)前線程的Looper
對象 & 對應(yīng)的消息隊(duì)列對象(MessageQueue)
,從而 自動(dòng)綁定了 實(shí)現(xiàn)創(chuàng)建Handler
對象操作的線程那么今瀑,當(dāng)前線程的
Looper
對象 & 對應(yīng)的消息隊(duì)列對象(MessageQueue)
是什么時(shí)候創(chuàng)建的呢程梦?
在上述使用步驟中点把,并無 創(chuàng)建
Looper
對象 & 對應(yīng)的消息隊(duì)列對象(MessageQueue)
這1步
步驟1前的隱式操作1:創(chuàng)建循環(huán)器對象(Looper) & 消息隊(duì)列對象(MessageQueue)
- 創(chuàng)建Looper對象主要通過方法:Looper.prepareMainLooper()、Looper.prepare();
- 創(chuàng)建消息隊(duì)列對象(MessageQueue)方法:創(chuàng)建Looper對象時(shí)則會(huì)自動(dòng)創(chuàng)建屿附,即:創(chuàng)建循環(huán)器對象(Looper)的同時(shí)郎逃,會(huì)自動(dòng)創(chuàng)建消息隊(duì)列對象(MessageQueue)。
方法介紹如下:
- 源碼分析
/**
* 源碼分析1:Looper.prepare()
* 作用:為當(dāng)前線程(子線程) 創(chuàng)建1個(gè)循環(huán)器對象(Looper)挺份,同時(shí)也生成了1個(gè)消息隊(duì)列對象(MessageQueue)
* 注:需在子線程中手動(dòng)調(diào)用該方法
*/
public static final void prepare() {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
// 1. 判斷sThreadLocal是否為null褒翰,否則拋出異常
//即 Looper.prepare()方法不能被調(diào)用兩次 = 1個(gè)線程中只能對應(yīng)1個(gè)Looper實(shí)例
// 注:sThreadLocal = 1個(gè)ThreadLocal對象,用于存儲(chǔ)線程的變量
sThreadLocal.set(new Looper(true));
// 2. 若為初次Looper.prepare()匀泊,則創(chuàng)建Looper對象 & 存放在ThreadLocal變量中
// 注:Looper對象是存放在Thread線程里的
// 源碼分析Looper的構(gòu)造方法->>分析a
}
/**
* 分析a:Looper的構(gòu)造方法
**/
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
// 1. 創(chuàng)建1個(gè)消息隊(duì)列對象(MessageQueue)
// 即 當(dāng)創(chuàng)建1個(gè)Looper實(shí)例時(shí)优训,會(huì)自動(dòng)創(chuàng)建一個(gè)與之配對的消息隊(duì)列對象(MessageQueue)
mRun = true;
mThread = Thread.currentThread();
}
/**
* 源碼分析2:Looper.prepareMainLooper()
* 作用:為 主線程(UI線程) 創(chuàng)建1個(gè)循環(huán)器對象(Looper),同時(shí)也生成了1個(gè)消息隊(duì)列對象(MessageQueue)
* 注:該方法在主線程(UI線程)創(chuàng)建時(shí)自動(dòng)調(diào)用各聘,即 主線程的Looper對象自動(dòng)生成揣非,不需手動(dòng)生成
*/
// 在Android應(yīng)用進(jìn)程啟動(dòng)時(shí),會(huì)默認(rèn)創(chuàng)建1個(gè)主線程(ActivityThread躲因,也叫UI線程)
// 創(chuàng)建時(shí)早敬,會(huì)自動(dòng)調(diào)用ActivityThread的1個(gè)靜態(tài)的main()方法 = 應(yīng)用程序的入口
// main()內(nèi)則會(huì)調(diào)用Looper.prepareMainLooper()為主線程生成1個(gè)Looper對象
/**
* 源碼分析:main()
**/
public static void main(String[] args) {
... // 僅貼出關(guān)鍵代碼
Looper.prepareMainLooper();
// 1. 為主線程創(chuàng)建1個(gè)Looper對象,同時(shí)生成1個(gè)消息隊(duì)列對象(MessageQueue)
// 方法邏輯類似Looper.prepare()
// 注:prepare():為子線程中創(chuàng)建1個(gè)Looper對象
ActivityThread thread = new ActivityThread();
// 2. 創(chuàng)建主線程
Looper.loop();
// 3. 自動(dòng)開啟 消息循環(huán) ->>下面將詳細(xì)分析
}
總結(jié):
- 創(chuàng)建主線程時(shí)大脉,會(huì)自動(dòng)調(diào)用
ActivityThread
的1個(gè)靜態(tài)的main()
搞监;而main()
內(nèi)則會(huì)調(diào)用Looper.prepareMainLooper()
為主線程生成1個(gè)Looper
對象,同時(shí)也會(huì)生成其對應(yīng)的MessageQueue
對象
- 即 主線程的
Looper
對象自動(dòng)生成箱靴,不需手動(dòng)生成腺逛;而子線程的Looper
對象則需手動(dòng)通過Looper.prepare()
創(chuàng)建- 在子線程若不手動(dòng)創(chuàng)建
Looper
對象 則無法生成Handler
對象
根據(jù)
Handler
的作用(在主線程更新UI
),故Handler
實(shí)例的創(chuàng)建場景 主要在主線程生成
Looper
&MessageQueue
對象后衡怀,則會(huì)自動(dòng)進(jìn)入消息循環(huán):Looper.loop()
,即又是另外一個(gè)隱式操作安疗。
步驟1前的隱式操作2:消息循環(huán)
此處主要分析的是Looper
類中的loop()
方法
/**
* 源碼分析: Looper.loop()
* 作用:消息循環(huán)抛杨,即從消息隊(duì)列中獲取消息、分發(fā)消息到Handler
* 特別注意:
* a. 主線程的消息循環(huán)不允許退出荐类,即無限循環(huán)
* b. 子線程的消息循環(huán)允許退出:調(diào)用消息隊(duì)列MessageQueue的quit()
*/
public static void loop() {
...// 僅貼出關(guān)鍵代碼
// 1. 獲取當(dāng)前Looper的消息隊(duì)列
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
// myLooper()作用:返回sThreadLocal存儲(chǔ)的Looper實(shí)例怖现;若me為null 則拋出異常
// 即loop()執(zhí)行前必須執(zhí)行prepare(),從而創(chuàng)建1個(gè)Looper實(shí)例
final MessageQueue queue = me.mQueue;
// 獲取Looper實(shí)例中的消息隊(duì)列對象(MessageQueue)
// 2. 消息循環(huán)(通過for循環(huán))
for (;;) {
// 2.1 從消息隊(duì)列中取出消息
Message msg = queue.next();
if (msg == null) {
return;
}
// next():取出消息隊(duì)列里的消息
// 若取出的消息為空玉罐,則線程阻塞
// ->> 分析1
// 2.2 派發(fā)消息到對應(yīng)的Handler
msg.target.dispatchMessage(msg);
// 把消息Message派發(fā)給消息對象msg的target屬性
// target屬性實(shí)際是1個(gè)handler對象
// ->>分析2
// 3. 釋放消息占據(jù)的資源
msg.recycle();
}
}
/**
* 分析1:queue.next()
* 定義:屬于消息隊(duì)列類(MessageQueue)中的方法
* 作用:出隊(duì)消息屈嗤,即從 消息隊(duì)列中 移出該消息
*/
Message next() {
...// 僅貼出關(guān)鍵代碼
// 該參數(shù)用于確定消息隊(duì)列中是否還有消息
// 從而決定消息隊(duì)列應(yīng)處于出隊(duì)消息狀態(tài) or 等待狀態(tài)
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
// nativePollOnce方法在native層,若是nextPollTimeoutMillis為-1吊输,此時(shí)消息隊(duì)列處于等待狀態(tài)
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
// 出隊(duì)消息饶号,即 從消息隊(duì)列中取出消息:按創(chuàng)建Message對象的時(shí)間順序
if (msg != null) {
if (now < msg.when) {
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// 取出了消息
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 {
// 若 消息隊(duì)列中已無消息,則將nextPollTimeoutMillis參數(shù)設(shè)為-1
// 下次循環(huán)時(shí)季蚂,消息隊(duì)列則處于等待狀態(tài)
nextPollTimeoutMillis = -1;
}
......
}
.....
}
}// 回到分析原處
/**
* 分析2:dispatchMessage(msg)
* 定義:屬于處理者類(Handler)中的方法
* 作用:派發(fā)消息到對應(yīng)的Handler實(shí)例 & 根據(jù)傳入的msg作出對應(yīng)的操作
*/
public void dispatchMessage(Message msg) {
// 1. 若msg.callback屬性不為空茫船,則代表使用了post(Runnable r)發(fā)送消息
// 則執(zhí)行handleCallback(msg)琅束,即回調(diào)Runnable對象里復(fù)寫的run()
// 上述結(jié)論會(huì)在講解使用“post(Runnable r)”方式時(shí)講解
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
// 2. 若msg.callback屬性為空,則代表使用了sendMessage(Message msg)發(fā)送消息(即此處需討論的)
// 則執(zhí)行handleMessage(msg)算谈,即回調(diào)復(fù)寫的handleMessage(msg) ->> 分析3
handleMessage(msg);
}
}
/**
* 分析3:handleMessage(msg)
* 注:該方法 = 空方法涩禀,在創(chuàng)建Handler實(shí)例時(shí)復(fù)寫 = 自定義消息處理方式
**/
public void handleMessage(Message msg) {
... // 創(chuàng)建Handler實(shí)例時(shí)復(fù)寫
}
總結(jié):
- 消息循環(huán)的操作 = 消息出隊(duì) + 分發(fā)給對應(yīng)的
Handler
實(shí)例 - 分發(fā)給對應(yīng)的
Handler
的過程:根據(jù)出隊(duì)消息的歸屬者通過dispatchMessage(msg)
進(jìn)行分發(fā),最終回調(diào)復(fù)寫的handleMessage(Message msg)
然眼,從而實(shí)現(xiàn) 消息處理 的操作 - 特別注意:在進(jìn)行消息分發(fā)時(shí)
(dispatchMessage(msg))
艾船,會(huì)進(jìn)行1次發(fā)送方式的判斷:- 若
msg.callback
屬性不為空,則代表使用了post(Runnable r)
發(fā)送消息高每,則直接回調(diào)Runnable
對象里復(fù)寫的run()
- 若
msg.callback
屬性為空丽声,則代表使用了sendMessage(Message msg)
發(fā)送消息,則回調(diào)復(fù)寫的handleMessage(msg)
- 若
至此觉义,關(guān)于步驟1的源碼分析講解完畢雁社。總結(jié)如下
步驟2:創(chuàng)建消息對象
/**
* 具體使用
*/
Message msg = Message.obtain(); // 實(shí)例化消息對象
msg.what = 1; // 消息標(biāo)識
msg.obj = "AA"; // 消息內(nèi)容存放
/**
* 源碼分析:Message.obtain()
* 作用:創(chuàng)建消息對象
* 注:創(chuàng)建Message對象可用關(guān)鍵字new 或 Message.obtain()
*/
public static Message obtain() {
// Message內(nèi)部維護(hù)了1個(gè)Message池晒骇,用于Message消息對象的復(fù)用
// 使用obtain()則是直接從池內(nèi)獲取
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
// 建議:使用obtain()”創(chuàng)建“消息對象霉撵,避免每次都使用new重新分配內(nèi)存
}
// 若池內(nèi)無消息對象可復(fù)用,則還是用關(guān)鍵字new創(chuàng)建
return new Message();
}
步驟3:在工作線程中 發(fā)送消息到消息隊(duì)列中
多線程的實(shí)現(xiàn)方式:
AsyncTask
洪囤、繼承Thread
類徒坡、實(shí)現(xiàn)Runnable
/**
* 具體使用
*/
mHandler.sendMessage(msg);
/**
* 源碼分析:mHandler.sendMessage(msg)
* 定義:屬于處理器類(Handler)的方法
* 作用:將消息 發(fā)送 到消息隊(duì)列中(Message ->> MessageQueue)
*/
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
// ->>分析1
}
/**
* 分析1:sendMessageDelayed(msg, 0)
**/
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
// ->> 分析2
}
/**
* 分析2:sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis)
**/
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
// 1. 獲取對應(yīng)的消息隊(duì)列對象(MessageQueue)
MessageQueue queue = mQueue;
// 2. 調(diào)用了enqueueMessage方法 ->>分析3
return enqueueMessage(queue, msg, uptimeMillis);
}
/**
* 分析3:enqueueMessage(queue, msg, uptimeMillis)
**/
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
// 1. 將msg.target賦值為this
// 即 :把 當(dāng)前的Handler實(shí)例對象作為msg的target屬性
msg.target = this;
// 請回憶起上面說的Looper的loop()中消息循環(huán)時(shí),會(huì)從消息隊(duì)列中取出每個(gè)消息msg瘤缩,然后執(zhí)行msg.target.dispatchMessage(msg)去處理消息
// 實(shí)際上則是將該消息派發(fā)給對應(yīng)的Handler實(shí)例
// 2. 調(diào)用消息隊(duì)列的enqueueMessage()
// 即:Handler發(fā)送的消息喇完,最終是保存到消息隊(duì)列->>分析4
return queue.enqueueMessage(msg, uptimeMillis);
}
/**
* 分析4:queue.enqueueMessage(msg, uptimeMillis)
* 定義:屬于消息隊(duì)列類(MessageQueue)的方法
* 作用:入隊(duì),即 將消息 根據(jù)時(shí)間 放入到消息隊(duì)列中(Message ->> MessageQueue)
* 采用單鏈表實(shí)現(xiàn):提高插入消息剥啤、刪除消息的效率
*/
boolean enqueueMessage(Message msg, long when) {
...// 僅貼出關(guān)鍵代碼
synchronized (this) {
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
// 判斷消息隊(duì)列里有無消息
// a. 若無锦溪,則將當(dāng)前插入的消息 作為隊(duì)頭 & 若此時(shí)消息隊(duì)列處于等待狀態(tài),則喚醒
if (p == null || when == 0 || when < p.when) {
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
// b. 判斷消息隊(duì)列里有消息府怯,則根據(jù) 消息(Message)創(chuàng)建的時(shí)間 插入到隊(duì)列中
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p;
prev.next = msg;
}
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
// 之后刻诊,隨著Looper對象的無限消息循環(huán)
// 不斷從消息隊(duì)列中取出Handler發(fā)送的消息 & 分發(fā)到對應(yīng)Handler
// 最終回調(diào)Handler.handleMessage()處理消息
- 總結(jié)
Handler
發(fā)送消息的本質(zhì) = 為該消息定義target
屬性(即本身實(shí)例對象) & 將消息入隊(duì)到綁定線程的消息隊(duì)列中。具體如下:
至此牺丙,關(guān)于使用 Handler.sendMessage()
的源碼解析完畢
總結(jié)
下面则涯,將順著文章:Android Handler:圖文解析 Handler通信機(jī)制 的工作原理再理一次:
方式2:使用Handler.post()
- 使用步驟
// 步驟1:在主線程中創(chuàng)建Handler實(shí)例
private Handler mhandler = new mHandler();
// 步驟2:在工作線程中 發(fā)送消息到消息隊(duì)列中 & 指定操作UI內(nèi)容
// 需傳入1個(gè)Runnable對象
mHandler.post(new Runnable() {
@Override
public void run() {
... // 需執(zhí)行的UI操作
}
});
// 步驟3:開啟工作線程(同時(shí)啟動(dòng)了Handler)
// 多線程可采用AsyncTask、繼承Thread類冲簿、實(shí)現(xiàn)Runnable
- 源碼分析
下面粟判,我將根據(jù)上述每個(gè)步驟進(jìn)行源碼分析
實(shí)際上,該方式與方式1中的
Handler.sendMessage()
工作原理相同峦剔、源碼分析類似档礁,下面將主要講解不同之處
步驟1:在主線程中創(chuàng)建Handler實(shí)例
/**
* 具體使用
*/
private Handler mhandler = new Handler();
// 與方式1的使用不同:此處無復(fù)寫Handler.handleMessage()
/**
* 源碼分析:Handler的構(gòu)造方法
* 作用:
* a. 在此之前羊异,主線程創(chuàng)建時(shí)隱式創(chuàng)建Looper對象事秀、MessageQueue對象
* b. 初始化Handler對象彤断、綁定線程 & 進(jìn)入消息循環(huán)
* 此處的源碼分析類似方式1,此處不作過多描述
*/
步驟2:在工作線程中 發(fā)送消息到消息隊(duì)列中
/**
* 具體使用
* 需傳入1個(gè)Runnable對象易迹、復(fù)寫run()從而指定UI操作
*/
mHandler.post(new Runnable() {
@Override
public void run() {
... // 需執(zhí)行的UI操作
}
});
/**
* 源碼分析:Handler.post(Runnable r)
* 定義:屬于處理者類(Handler)中的方法
* 作用:定義UI操作宰衙、將Runnable對象封裝成消息對象 & 發(fā)送 到消息隊(duì)列中(Message ->> MessageQueue)
* 注:
* a. 相比sendMessage(),post()最大的不同在于睹欲,更新的UI操作可直接在重寫的run()中定義
* b. 實(shí)際上供炼,Runnable并無創(chuàng)建新線程,而是發(fā)送 消息 到消息隊(duì)列中
*/
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
// getPostMessage(r) 的源碼分析->>分析1
// sendMessageDelayed()的源碼分析 ->>分析2
}
/**
* 分析1:getPostMessage(r)
* 作用:將傳入的Runable對象封裝成1個(gè)消息對象
**/
private static Message getPostMessage(Runnable r) {
// 1. 創(chuàng)建1個(gè)消息對象(Message)
Message m = Message.obtain();
// 注:創(chuàng)建Message對象可用關(guān)鍵字new 或 Message.obtain()
// 建議:使用Message.obtain()創(chuàng)建窘疮,
// 原因:因?yàn)镸essage內(nèi)部維護(hù)了1個(gè)Message池袋哼,用于Message的復(fù)用,使用obtain()直接從池內(nèi)獲取闸衫,從而避免使用new重新分配內(nèi)存
// 2. 將 Runable對象 賦值給消息對象(message)的callback屬性
m.callback = r;
// 3. 返回該消息對象
return m;
} // 回到調(diào)用原處
/**
* 分析2:sendMessageDelayed(msg, 0)
* 作用:實(shí)際上涛贯,從此處開始,則類似方式1 = 將消息入隊(duì)到消息隊(duì)列蔚出,
* 即 最終是調(diào)用MessageQueue.enqueueMessage()
**/
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
// 請看分析3
}
/**
* 分析3:sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis)
**/
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
// 1. 獲取對應(yīng)的消息隊(duì)列對象(MessageQueue)
MessageQueue queue = mQueue;
// 2. 調(diào)用了enqueueMessage方法 ->>分析3
return enqueueMessage(queue, msg, uptimeMillis);
}
/**
* 分析4:enqueueMessage(queue, msg, uptimeMillis)
**/
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
// 1. 將msg.target賦值為this
// 即 :把 當(dāng)前的Handler實(shí)例對象作為msg的target屬性
msg.target = this;
// 請回憶起上面說的Looper的loop()中消息循環(huán)時(shí)弟翘,會(huì)從消息隊(duì)列中取出每個(gè)消息msg,然后執(zhí)行msg.target.dispatchMessage(msg)去處理消息
// 實(shí)際上則是將該消息派發(fā)給對應(yīng)的Handler實(shí)例
// 2. 調(diào)用消息隊(duì)列的enqueueMessage()
// 即:Handler發(fā)送的消息骄酗,最終是保存到消息隊(duì)列
return queue.enqueueMessage(msg, uptimeMillis);
}
// 注:實(shí)際上從分析2開始稀余,源碼 與 sendMessage(Message msg)發(fā)送方式相同
從上面的分析可看出:
- 消息對象的創(chuàng)建 = 內(nèi)部 根據(jù)
Runnable
對象而封裝; - 發(fā)送到消息隊(duì)列的邏輯 = 方式1中
sendMessage(Message msg)
趋翻。
下面睛琳,我們重新回到步驟1前的隱式操作2:消息循環(huán),即Looper
類中的loop()
方法
/**
* 源碼分析: Looper.loop()
* 作用:消息循環(huán)踏烙,即從消息隊(duì)列中獲取消息师骗、分發(fā)消息到Handler
* 特別注意:
* a. 主線程的消息循環(huán)不允許退出,即無限循環(huán)
* b. 子線程的消息循環(huán)允許退出:調(diào)用消息隊(duì)列MessageQueue的quit()
*/
public static void loop() {
...// 僅貼出關(guān)鍵代碼
// 1. 獲取當(dāng)前Looper的消息隊(duì)列
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
// myLooper()作用:返回sThreadLocal存儲(chǔ)的Looper實(shí)例宙帝;若me為null 則拋出異常
// 即loop()執(zhí)行前必須執(zhí)行prepare()丧凤,從而創(chuàng)建1個(gè)Looper實(shí)例
final MessageQueue queue = me.mQueue;
// 獲取Looper實(shí)例中的消息隊(duì)列對象(MessageQueue)
// 2. 消息循環(huán)(通過for循環(huán))
for (;;) {
// 2.1 從消息隊(duì)列中取出消息
Message msg = queue.next();
if (msg == null) {
return;
}
// next():取出消息隊(duì)列里的消息
// 若取出的消息為空,則線程阻塞
// 2.2 派發(fā)消息到對應(yīng)的Handler
msg.target.dispatchMessage(msg);
// 把消息Message派發(fā)給消息對象msg的target屬性
// target屬性實(shí)際是1個(gè)handler對象
// ->>分析1
// 3. 釋放消息占據(jù)的資源
msg.recycle();
}
}
/**
* 分析1:dispatchMessage(msg)
* 定義:屬于處理者類(Handler)中的方法
* 作用:派發(fā)消息到對應(yīng)的Handler實(shí)例 & 根據(jù)傳入的msg作出對應(yīng)的操作
*/
public void dispatchMessage(Message msg) {
// 1. 若msg.callback屬性不為空步脓,則代表使用了post(Runnable r)發(fā)送消息(即此處需討論的)
// 則執(zhí)行handleCallback(msg),即回調(diào)Runnable對象里復(fù)寫的run()->> 分析2
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
// 2. 若msg.callback屬性為空浩螺,則代表使用了sendMessage(Message msg)發(fā)送消息(即此處需討論的)
// 則執(zhí)行handleMessage(msg)靴患,即回調(diào)復(fù)寫的handleMessage(msg)
handleMessage(msg);
}
}
/**
* 分析2:handleCallback(msg)
**/
private static void handleCallback(Message message) {
message.callback.run();
// Message對象的callback屬性 = 傳入的Runnable對象
// 即回調(diào)Runnable對象里復(fù)寫的run()
}
至此,你應(yīng)該明白使用 Handler.post()
的工作流程:與方式1(Handler.sendMessage())
類似要出,區(qū)別在于:
- 不需外部創(chuàng)建消息對象鸳君,而是內(nèi)部根據(jù)傳入的
Runnable
對象 封裝消息對象 - 回調(diào)的消息處理方法是:復(fù)寫
Runnable
對象的run()
二者的具體異同如下:
總結(jié)
至此,關(guān)于使用 Handler.post()
的源碼解析完畢患蹂,總結(jié)如下:
下面或颊,將順著文章:Android Handler:圖文解析 Handler通信機(jī)制 的工作原理再理一次:
至此砸紊,關(guān)于Handler
機(jī)制的源碼全部分析完畢。
5. 總結(jié)
本文詳細(xì)分析了Handler
機(jī)制的源碼囱挑,文字總結(jié) & 流程圖如下:
下一篇文章我將對講解Android Handler
的相關(guān)知識醉顽,感興趣的同學(xué)可以繼續(xù)關(guān)注Carson_Ho的簡書
Anroid異步通信Handler系列文章
Android異步通信:Handler機(jī)制學(xué)習(xí)攻略
Android異步通信:Handler使用教程
Android異步通信:Handler工作原理
Android異步通信:Handler源碼分析
Android異步通信:詳解Handler內(nèi)存泄露的原因
歡迎關(guān)注Carson_Ho的簡書
不定期分享關(guān)于安卓開發(fā)的干貨,追求短平挑、平游添、快,但卻不缺深度通熄。