您好,登錄后才能下訂單哦!
Handler的原理是什么?能深入分析下 Handler的實現機制嗎?
面試官問該問題是想問清楚handler的源碼,handler機制如何實現,對消息泵Looper理不理解
(更多完整項目下載。未完待續。源碼。圖文知識后續上傳github。)
在多線程的應用場景中,將工作線程中需更新UI的操作信息 傳遞到 UI主線程,從而實現 工作線程對UI的更新處理,最終實現異步消息的處理
使用Handler的原因:將工作線程需操作UI的消息 傳遞 到主線程,使得主線程可根據工作線程的需求 更新UI,從而避免線程操作不安全的問題
在閱讀Handler
機制的源碼分析前,請務必了解Handler
的一些儲備知識:相關概念、使用方式 & 工作原理
#####2.1 相關概念
關于?Handler
?機制中的相關概念如下:
在下面的講解中,我將直接使用英文名講解,即?
Handler
、Message
、Message Queue
、Looper
,希望大家先熟悉相關概念2.2 使用方式
Handler
使用方式 因發送消息到消息隊列的方式不同而不同,共分為2種:使用Handler.sendMessage()
、使用Handler.post()
- 下面的源碼分析將依據使用步驟講解
在源碼分析前,先來了解Handler
機制中的核心類
Handler
機制 中有3個重要的類:
(Handler)
(MessageQueue)
(Looper)
下面的源碼分析將根據 Handler
的使用步驟進行
Handler
使用方式 因發送消息到消息隊列的方式不同而不同,共分為2種:使用Handler.sendMessage()
、使用Handler.post()
使用步驟
/**
* 此處以 匿名內部類 的使用方式為例
*/
// 步驟1:在主線程中 通過匿名內部類 創建Handler類對象
private Handler mhandler = new Handler(){
// 通過復寫handlerMessage()從而確定更新UI的操作
@Override
public void handleMessage(Message msg) {
...// 需執行的UI操作
}
};
// 步驟2:創建消息對象
Message msg = Message.obtain(); // 實例化消息對象
msg.what = 1; // 消息標識
msg.obj = "AA"; // 消息內容存放
// 步驟3:在工作線程中 通過Handler發送消息到消息隊列中
// 多線程可采用AsyncTask、繼承Thread類、實現Runnable
mHandler.sendMessage(msg);
// 步驟4:開啟工作線程(同時啟動了Handler)
// 多線程可采用AsyncTask、繼承Thread類、實現Runnable
/**
* 具體使用
*/
private Handler mhandler = new Handler(){
// 通過復寫handlerMessage()從而確定更新UI的操作
@Override
public void handleMessage(Message msg) {
...// 需執行的UI操作
}
};
/**
* 源碼分析:Handler的構造方法
* 作用:初始化Handler對象 & 綁定線程
* 注:
* a. Handler需綁定 線程才能使用;綁定后,Handler的消息處理會在綁定的線程中執行
* b. 綁定方式 = 先指定Looper對象,從而綁定了 Looper對象所綁定的線程(因為Looper對象本已綁定了對應線程)
* c. 即:指定了Handler對象的 Looper對象 = 綁定到了Looper對象所在的線程
*/
public Handler() {
this(null, false);
// ->>分析1
}
/**
* 分析1:this(null, false) = Handler(null,false)
*/
public Handler(Callback callback, boolean async) {
...// 僅貼出關鍵代碼
// 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()作用:獲取當前線程的Looper對象;若線程無Looper對象則拋出異常
// 即 :若線程中無創建Looper對象,則也無法創建Handler對象
// 故 若需在子線程中創建Handler對象,則需先創建Looper對象
// 注:可通過Loop.getMainLooper()可以獲得當前進程的主線程的Looper對象
// 2. 綁定消息隊列對象(MessageQueue)
mQueue = mLooper.mQueue;
// 獲取該Looper對象中保存的消息隊列對象(MessageQueue)
// 至此,保證了handler對象 關聯上 Looper對象中MessageQueue
}
在上述使用步驟中,并無 創建Looper對象 & 對應的消息隊列對象(MessageQueue)這1步
/**
* 源碼分析1:Looper.prepare()
* 作用:為當前線程(子線程) 創建1個循環器對象(Looper),同時也生成了1個消息隊列對象(MessageQueue)
* 注:需在子線程中手動調用該方法
*/
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()方法不能被調用兩次 = 1個線程中只能對應1個Looper實例
// 注:sThreadLocal = 1個ThreadLocal對象,用于存儲線程的變量
sThreadLocal.set(new Looper(true));
// 2. 若為初次Looper.prepare(),則創建Looper對象 & 存放在ThreadLocal變量中
// 注:Looper對象是存放在Thread線程里的
// 源碼分析Looper的構造方法->>分析a
}
/**
* 分析a:Looper的構造方法
**/
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
// 1. 創建1個消息隊列對象(MessageQueue)
// 即 當創建1個Looper實例時,會自動創建一個與之配對的消息隊列對象(MessageQueue)
mRun = true;
mThread = Thread.currentThread();
}
/**
* 源碼分析2:Looper.prepareMainLooper()
* 作用:為 主線程(UI線程) 創建1個循環器對象(Looper),同時也生成了1個消息隊列對象(MessageQueue)
* 注:該方法在主線程(UI線程)創建時自動調用,即 主線程的Looper對象自動生成,不需手動生成
*/
// 在Android應用進程啟動時,會默認創建1個主線程(ActivityThread,也叫UI線程)
// 創建時,會自動調用ActivityThread的1個靜態的main()方法 = 應用程序的入口
// main()內則會調用Looper.prepareMainLooper()為主線程生成1個Looper對象
/**
* 源碼分析:main()
**/
public static void main(String[] args) {
... // 僅貼出關鍵代碼
Looper.prepareMainLooper();
// 1. 為主線程創建1個Looper對象,同時生成1個消息隊列對象(MessageQueue)
// 方法邏輯類似Looper.prepare()
// 注:prepare():為子線程中創建1個Looper對象
ActivityThread thread = new ActivityThread();
// 2. 創建主線程
Looper.loop();
// 3. 自動開啟 消息循環 ->>下面將詳細分析
}
ActivityThread
的1個靜態的main()
;而main()
內則會調用Looper.prepareMainLooper()
為主線程生成1個Looper
對象,同時也會生成其對應的MessageQueue
對象
1.即 主線程的
Looper
對象自動生成,不需手動生成;而子線程的Looper對象則需手動通過Looper.prepare()
創建
2.在子線程若不手動創建Looper
對象 則無法生成Handler對象
Looper
& MessageQueue
對象后,則會自動進入消息循環:Looper.loop()
,即又是另外一個隱式操作。
此處主要分析的是Looper類中的loop()方法
/**
b. 子線程的消息循環允許退出:調用消息隊列MessageQueue的quit()
*/
public static void loop() {
...// 僅貼出關鍵代碼
// 1. 獲取當前Looper的消息隊列
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
// myLooper()作用:返回sThreadLocal存儲的Looper實例;若me為null 則拋出異常
// 即loop()執行前必須執行prepare(),從而創建1個Looper實例
final MessageQueue queue = me.mQueue;
// 獲取Looper實例中的消息隊列對象(MessageQueue)
// 2. 消息循環(通過for循環)
for (;;) {
// 2.1 從消息隊列中取出消息
Message msg = queue.next();
if (msg == null) {
return;
}
// next():取出消息隊列里的消息
// 若取出的消息為空,則線程阻塞
// ->> 分析1
// 2.2 派發消息到對應的Handler
msg.target.dispatchMessage(msg);
// 把消息Message派發給消息對象msg的target屬性
// target屬性實際是1個handler對象
// ->>分析2
// 3. 釋放消息占據的資源
msg.recycle();
}
}
/**
作用:出隊消息,即從 消息隊列中 移出該消息
*/
Message next() {
...// 僅貼出關鍵代碼
// 該參數用于確定消息隊列中是否還有消息
// 從而決定消息隊列應處于出隊消息狀態 or 等待狀態
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
// nativePollOnce方法在native層,若是nextPollTimeoutMillis為-1,此時消息隊列處于等待狀態
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
// 出隊消息,即 從消息隊列中取出消息:按創建Message對象的時間順序
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 {
// 若 消息隊列中已無消息,則將nextPollTimeoutMillis參數設為-1
// 下次循環時,消息隊列則處于等待狀態
nextPollTimeoutMillis = -1;
}
......
}
.....
}
}// 回到分析原處
/**
作用:派發消息到對應的Handler實例 & 根據傳入的msg作出對應的操作
*/
public void dispatchMessage(Message msg) {
// 1. 若msg.callback屬性不為空,則代表使用了post(Runnable r)發送消息
// 則執行handleCallback(msg),即回調Runnable對象里復寫的run()
// 上述結論會在講解使用“post(Runnable r)”方式時講解
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
// 2. 若msg.callback屬性為空,則代表使用了sendMessage(Message msg)發送消息(即此處需討論的)
// 則執行handleMessage(msg),即回調復寫的handleMessage(msg) ->> 分析3
handleMessage(msg);
}
}
/**
注:該方法 = 空方法,在創建Handler實例時復寫 = 自定義消息處理方式
**/
public void handleMessage(Message msg) {
... // 創建Handler實例時復寫
}
### 總結:
消息循環的操作 = 消息出隊 + 分發給對應的Handler實例
分發給對應的Handler的過程:根據出隊消息的歸屬者通過dispatchMessage(msg)
進行分發,最終回調復寫的handleMessage(Message msg)
,從而實現 消息處理 的操作
(dispatchMessage(msg))
,會進行1次發送方式的判斷:
若msg.callback
屬性不為空,則代表使用了post(Runnable r)
發送消息,則直接回調Runnable
對象里復寫的run()
若msg.callback
屬性為空,則代表使用了sendMessage(Message msg)
發送消息,則回調復寫的handleMessage(msg)
至此,關于步驟1的源碼分析講解完畢
/**
* 具體使用
*/
Message msg = Message.obtain(); // 實例化消息對象
msg.what = 1; // 消息標識
msg.obj = "AA"; // 消息內容存放
/**
* 源碼分析:Message.obtain()
* 作用:創建消息對象
* 注:創建Message對象可用關鍵字new 或 Message.obtain()
*/
public static Message obtain() {
// Message內部維護了1個Message池,用于Message消息對象的復用
// 使用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;
}
// 建議:使用obtain()”創建“消息對象,避免每次都使用new重新分配內存
}
// 若池內無消息對象可復用,則還是用關鍵字new創建
return new Message();
}
### 步驟3:在工作線程中 發送消息到消息隊列中
多線程的實現方式:
AsyncTask
、繼承Thread類、實現Runnable
/**
* 具體使用
*/
mHandler.sendMessage(msg);
/**
* 源碼分析:mHandler.sendMessage(msg)
* 定義:屬于處理器類(Handler)的方法
* 作用:將消息 發送 到消息隊列中(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. 獲取對應的消息隊列對象(MessageQueue)
MessageQueue queue = mQueue;
// 2. 調用了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
// 即 :把 當前的Handler實例對象作為msg的target屬性
msg.target = this;
// 請回憶起上面說的Looper的loop()中消息循環時,會從消息隊列中取出每個消息msg,然后執行msg.target.dispatchMessage(msg)去處理消息
// 實際上則是將該消息派發給對應的Handler實例
// 2. 調用消息隊列的enqueueMessage()
// 即:Handler發送的消息,最終是保存到消息隊列->>分析4
return queue.enqueueMessage(msg, uptimeMillis);
}
/**
* 分析4:queue.enqueueMessage(msg, uptimeMillis)
* 定義:屬于消息隊列類(MessageQueue)的方法
* 作用:入隊,即 將消息 根據時間 放入到消息隊列中(Message ->> MessageQueue)
* 采用單鏈表實現:提高插入消息、刪除消息的效率
*/
boolean enqueueMessage(Message msg, long when) {
...// 僅貼出關鍵代碼
synchronized (this) {
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
// 判斷消息隊列里有無消息
// a. 若無,則將當前插入的消息 作為隊頭 & 若此時消息隊列處于等待狀態,則喚醒
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. 判斷消息隊列里有消息,則根據 消息(Message)創建的時間 插入到隊列中
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對象的無限消息循環
// 不斷從消息隊列中取出Handler發送的消息 & 分發到對應Handler
// 最終回調Handler.handleMessage()處理消息
(更多完整項目下載。未完待續。源碼。圖文知識后續上傳github。)
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。