
Android跨行程要掌握的是Binder, 而同一行程中最重要的應該就是Handler 訊息通信機制了,我這么說,大家不知道是否認同,如果認同,還希望能給一個關注哈,
什么是Handler?
Handler主要用于異步訊息的處理:當發出一個訊息之后,首先進入一個訊息佇列,發送訊息的[函式]即刻回傳,而另外一個部分在訊息佇列中逐一將訊息取出,然后對訊息進行處理,也就是發送訊息和接收訊息不是同步的處理, 這種機制通常用來處理相對耗時比較長的操作,
Handler特點
-
傳遞Message,用于接受子執行緒發送的資料, 并用此資料配合主執行緒更新UI,
在Android中,對于UI的操作通常需要放在主執行緒中進行操作,如果在子執行緒中有關于UI的操作,那么就需要把資料訊息作為一個Message物件發送到訊息佇列中,然后,由Handler中的handlerMessage方法處理傳過來的資料資訊,并操作UI,當然,Handler物件是在主執行緒中初始化的,因為它需要系結在主執行緒的訊息佇列中,
類sendMessage(Message msg)方法實作發送訊息的操作, 在初始化Handler物件時重寫的handleMessage方法來接收Message并進行相關操作,
-
傳遞Runnable物件,用于通過Handler系結的訊息佇列,安排不同操作的執行順序,
Handler物件在進行初始化的時候,會默認的自動系結訊息佇列,利用類post方法,可以將Runnable物件發送到訊息佇列中,按照佇列的機制按順序執行不同的Runnable物件中的run方法,
Handler怎么用?
public class HandlerActivity extends AppCompatActivity {
private static final String TAG = "HandlerActivity";
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
testSendMessage();
}
public void testSendMessage() {
Handler handler = new MyHandler(this);
Message message = Message.obtain();
message.obj = "test handler send message";
handler.sendMessage(message);
}
//注1: 為什么要用靜態內部???
static class MyHandler extends Handler {
WeakReference<AppCompatActivity> activityWeakReference; // 注2:為何要用弱參考???
public MyHandler(AppCompatActivity activity) {
activityWeakReference = new WeakReference<>(activity);
}
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
Log.d(TAG, (String) msg.obj);
}
}
}
Handler原始碼怎么讀?
從使用方式的場景,咱們一步一步的探究里面是怎么實作的,還有上面的標注的兩點,在后面我都會介紹的,各位客官聽我慢慢道來,首先,看下四大金剛關系圖,文字表述再多,不如一張圖來的直接,

通過上圖就可以簡單看出Handler、MessageQueue、Message、Looper 這四者是怎么樣互相持有對方的,大概可以了解訊息的傳遞,
下面我們先來一張時序圖,看下訊息是怎么一步步發送出來的,

此刻,應該要開車了,前方高能!!!
- 進入的是Handler.sendMessage 方法
public final boolean sendMessage(@NonNull Message msg) {
return sendMessageDelayed(msg, 0);
}
- 接下來繼續呼叫Handler.sendMessageDelayed方法
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
- 接著走Handler.sendMessageAtTime 方法,這里面就要用到MessageQueue 物件了,此處說明一下,這個mQueue 是在哪里獲取到的,是在Handler 構造方法里,此處貼圖,從圖中可以看出
mLooper=Looper.myLooper()mQueue=mLooper.mQueueHandler 中的MessageQueue 是Looper 中持有的MessageQueue 物件 ,

注1 為啥要用靜態內部類---->如果我們使用Handler 類,沒有用static 關鍵字修飾的話,則會輸出Log: The following Handler class should be static or leaks might occur: 會提示你可能會引起記憶體泄漏,因此在注1 處我用了static 修飾,
好,這里就說這么多,接著開車:
public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
- 接著時序圖上的流程走,此時要進入到MessageQueue.enqueueMessage 方法中,該方法就是將msg 物件存入到MessageQueue 佇列中,注意此處,將該handler 物件賦值給了msg.target,這個后面會用到的,很關鍵,
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis); //3,即將進入MessageQueue.enqueueMessage 方法,
}
- 接著來看MessageQueue.enqueueMessage 方法,該方法就是按照時間的順序插入到Message 這個鏈表結構的資料物件中去,
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) { //4. 后面說明,這個也就是四大金剛圖里的msg.target 所持有的Handler 物件,
throw new IllegalArgumentException("Message must have a target.");
}
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 {
// 鏈表的插入操作,不太熟悉的可以看看資料結構,(此處是根據時間來排序的)
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); //畫重點,此處喚醒等待的next 方法,
}
}
return true;
}
此時,一條訊息就相當于入隊了, MessageQueue 從名稱來看是佇列,實際上,使用的還是Message.next 指標來進行操作的,也即是鏈表的操作,訊息的入隊完成,后面將會介紹該訊息是怎么發送出去的,
Loop.loop方法,敲重點,省略了部分代碼,只關注核心代碼,這里用到了死回圈,不停的獲取Message 物件,獲取到之后直接呼叫Message.target 變數所持有的Handler 物件,然后呼叫Handler.dispatchMessage 方法,這樣就完成了訊息的分發,
public static void loop() {
final Looper me = myLooper();
...
final MessageQueue queue = me.mQueue;
...
for (;;) {
Message msg = queue.next(); // might block //7.通過MessageQueue.next()方法獲取Message物件,
...
final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
final long end;
try {
msg.target.dispatchMessage(msg);
end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
...
msg.recycleUnchecked();
}
}
7-8. MessageQueue.next() 方法獲取Message 物件,
Message next() {
...
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) { //死回圈
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(ptr, nextPollTimeoutMillis); // 5: 避免了阻塞的關鍵點,釋放資源,處于等待,疑點:處于等待,肯定需要一個東西來喚醒它,上面第5步分析enqueueMessage的時候有行代碼if (needWake) {
nativeWake(mPtr); //畫重點,此處喚醒等待的next 方法,
} ,
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) { //******此條件可以先不看,因為通過Handler 發送的訊息target 都會持有Handler,該邏輯不會觸發,訊息同步屏障的時候會優先觸發該邏輯,
// 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) { //查找當前的msg 物件,
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;
}
...
nextPollTimeoutMillis = 0;
}
}
- Handler.dispatchMessage 方法,此處有判斷,如果在Activity中使用view.post方法呼叫的時候,就會走到handleCallback 回呼中,通過sendMessagexxx函式發送訊息的就會走到handleMessage回呼中去,
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
該方法會會將msg 物件發送到客戶端定義Handler 的地方,重寫的handleMessage 方法,至此,Handler 發送訊息的流程大致介紹完成,
總結
Handler 發送訊息的時候,在Handler.enqueueMessage 方法中,將該Handler 物件添加到Message中的target 屬性中,這樣就完成了Message 持有Handler 的操作,為最后Message.target.dispatchMessage 做了保證,然后將該Message 物件放入到MessageQueue中的Message.next 中去,完成了訊息鏈表的添加;而這個MessageQueue 是Looper 中所持有的物件,這樣就可以通過Looper類通過對MessageQueue.next()---->Message.next()--->Message.target.dispatchMessage(msg)完成了訊息的分發,

知識點補充
- Looper 物件是怎么new 出來的?

上圖看出是在應用程式行程的ActivityThread 類中的main() 函式中呼叫了Looper.prepareMainLooper() 方法,就new 出來了主執行緒中的Looper.

上圖也看出,這個Looper.prepareMainLooper()方法是系統呼叫的,開發者不能再次呼叫了,否則會拋出例外,

prepare這個方法真正的new Looper 了,接著來看看Looper 的建構式

此處創建了MessageQueue, Handler 中的MessageQueue 就是這塊創建的,
- 為什么將Looper 保存在ThreadLocal 中?
ThreadLocal:執行緒的變數副本,每個執行緒隔離.我的理解就是,ThreadLocal 內部使用了當前執行緒為Key,需要存盤的物件為Value,通過字典保存起來的,這樣客戶端在獲取的時候,當前執行緒就只會獲取一份保存的Value.回到Looper中,就可以知道一個執行緒里按理說就會只有一個Looper,
- Message 為什么推薦使用obtain() 方式獲取Message物件,而不推薦使用new Message()?
這里涉及到池的技術的應用: Message中維護了一個訊息池,訊息使用完就會回收,減少物件創建和銷毀的開銷;java 當中的執行緒池也是用到了該思想,
- 同步屏障:
同步屏障機制的作用,是讓這個繪制訊息得以越過其他的訊息,優先被執行,系統中UI繪制會使用到同步屏障,開發中基本用不到,核心代碼: 先設定一個target=null 的訊息,插入到訊息鏈表的頭部,

然后在MessageQueue.next 中 優先查找同步屏障中的訊息asyncHronous 設定為true的異步訊息,

- Handler為什么會導致記憶體泄漏以及解決方案?
Handler導致記憶體泄漏一般發生在發送延遲訊息的時候,當Activity關閉之后,延遲訊息還沒發出,那么主執行緒中的MessageQueue就會持有這個訊息的參考,而這個訊息是持有Handler的參考,而handler作為匿名內部類持有了Activity的參考,所以就有了以下的一條參考鏈,
解決:1.使用靜態內部類,如果要呼叫Activity中的方法,就可以在靜態內部類中設定一個
WeakReference activityWeakReference; 參考,
2.在Activity銷毀的時候,即onDestory()方法中呼叫handler.removeCallbacks,移除runnable,
結尾
OK,本次的Android進階技術之Handler到此就全部寫完了,希望喜歡的朋友不要吝嗇你的贊,你的評論,點贊,收藏就是對我最大的支持,記得關注我哦,咱們文章每日都會更新,感謝大家的觀看,
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/445921.html
標籤:其他
