前言
在日常開發中,我們勢必會使用到子線程和UI線程的通信,而起着橋梁作用的就是我們常用的
Handler
。但是他的内部是怎麼運作的?運作的過程中存在什麼問題?需要我們注意,本文将會詳細講解。
解析Handler
從圖中我們就可以知道了,整個
Handler
工作組成的包括了
Handler
、
Looper
、
MessageQueue
、
Message
這四個部分。
MessageQueue和Message分别隻是一個隊列和消息實體類,自然不再多說。而Handler和Looper的具體是怎樣的呢?
在我的模拟Handler項目中,已經比較清晰的闡述了整個架構的工作流程,接下裡就是結合SDK代碼的一份解析了。
整個Handler往簡單了來說其實就幹了兩件事情:
- 發送消息
- 處理消息
發送消息
涉及到的三個函數
sendMessage()
、
enqueueMessage()
、
Looper.prepareMainLooper()
。
所有事情的起源要從
Looper.prepareMainLooper()
開始講起。這個函數處于
ActivityThread
中,沒有了解過這個類的讀者們需要知道,java程式設計一定是有一個主入口的,但是我們在整個Android程式設計中,從來沒有涉及過
main()
這個函數,是因為它已經包含在了
ActivityThread
這個類中,而它已經經過了複雜的封裝。
接下來看下這個
Looper.prepareMainLooper ()
函數。
public static void prepareMainLooper() {
prepare(false); // 1
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper(); // 2
}
}
// 上述注釋1對應的函數
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
// 上述注釋2對應的函數
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
兩小段代碼,裡面用到了一個變量
sThreadLocal
,這個變量是使用
static final
修飾的,意味這全局唯一。從他主動抛出的異常我們也可以看出
Looper
這個對象也是一個唯一的變量。這是我們需要掌握的第一個知識點。
接下來是關于
sendMessage()
函數 這個函數其實是一個泛稱,他并不單單指
sendMessage()
,他還可以是
sendMessageAtTime()
、
sendMessageDelayed()
,他們都幹了一件事情——傳遞消息。通過一直向下探索,你就能知道他們最後調用的都是
enqueueMessage()
這個函數,也就是把消息放進了消息隊列中。
當然要注意到一個事情,這裡我們使用了synchronized的關鍵詞,其實就是為了保障我們的資料同步。
沒有很多的操作,就是我們熟悉的連結清單操作。這裡沒有做展示,有興趣的朋友進到源碼往下翻一點,馬上就能看到了。
就這樣很簡單,并且很成功的讓我們的消息進入了消息隊列。
處理消息
接收完消息,我們要幹嘛?我們為什麼要發消息,因為我們要處理啊。
這裡我們要遇到的函數有:
Looper.loop()
、
dispatchMessage()
、
handleMessage()
。用過Handler的讀者們都應該知道我們是需要重寫
handleMessage()
這個函數的,用于對不同的消息作出響應,是以就不再多介紹。是以第一個講的就是
Looper.loop()
這個函數。
一共兩段代碼,也是至關重要的一部分。在這個代碼中兩個至關重要的點:
(1)首先是問題是這麼一個死循環的函數,怎麼就沒引發ANR呢????
(2)通過
dispatchMessage()
如何分發這個消息的?target是什麼?
先是第一個問題的解答。網上的解答多種多樣。但是最關鍵的點其實是這樣的,
ANR
是圍繞
loop()
這個函數展開的,而
ANR
的出現也就是
loop()
的消息沒有得到及時的消費。
第二個問題。先說
target
這個爆紅的變量是什麼。
msg.target
也就是說這是
Message
的一個元素,搜尋
Message
就能找到如下圖示。
原來
target
就是一個
Handler
,而這個
Handler
就是我們對應的主動建立
Handler
。然後就是
dispatchMessage()
函數了。
/**
* Handle system messages here.
*/
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
想來這就很清楚了,他也就是将事件分發給了
handleMessage()
處理,而
handleMessage()
又是我們自己來專門寫的。
msg.callback
是一個
Runnable
對象。
害,原來就是這樣啊。。
MessageQueue中消息如果為空,該咋辦???
其實他在
MessageQueue
的
next()
方法中已經有了對應的解決方案了。
Message next() {
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1;
int nextPollTimeoutMillis = 0;
// 重點!重點!重點!!!
// 一個死循環
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
// 。。。。。。
// 判斷隊列内部是否還有消息
if (msg != null) {
// 。。。。。
} else {
// No more messages.
// 不存在更多的資訊資料了
nextPollTimeoutMillis = -1;
}
// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose();
return null;
}
// 兩種情況調用到IdleHandler
// 1.消息隊列為空;2.下一條消息在将來才執行
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
// 如果idleHandler都不存在了,那隻能讓Looper阻塞
// 繼續循環執行
if (pendingIdleHandlerCount <= 0) {
// 出現了一個用于阻塞的判斷
mBlocked = true;
continue;
}
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// 執行這些IdleHandler,那IdleHandler是幹嘛的?
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // release the reference to the handler
boolean keep = false;
try {
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
pendingIdleHandlerCount = 0;
nextPollTimeoutMillis = 0;
}
}
上面我們講述了MessageQueue中的next()想必已經為我們指點迷津了,這裡再進行一個梳理。
- MessageQueue中存在資料時,就是正常的找到對應的Handler進行處理。
- 如果MessageQueue沒有資料了,這時候分為兩種情況:
- 尋找mIdleHandlers,也就是一些類似與系統服務了,進行處理。
- 連mIdleHandlers都沒有了,就進入阻塞狀态,等待新的任務進入将其喚醒。
既然知道了
IdleHandler
的存在,就看看他的具體運作方式是如何的,而最主要的就是他的接口方法
queueIdle()
。
// 在ActivityThread中存在
// 使用了aidl調用了底層服務
// 也就是說在你沒事幹的時候,他也會自己給自己安排事情做
private class Idler implements MessageQueue.IdleHandler {
@Override
public final boolean queueIdle() {
ActivityClientRecord a = mNewActivities;
boolean stopProfiling = false;
if (mBoundApplication != null && mProfiler.profileFd != null
&& mProfiler.autoStopProfiler) {
stopProfiling = true;
}
if (a != null) {
mNewActivities = null;
IActivityTaskManager am = ActivityTaskManager.getService();
ActivityClientRecord prev;
do {
if (localLOGV) Slog.v(
TAG, "Reporting idle of " + a +
" finished=" +
(a.activity != null && a.activity.mFinished));
if (a.activity != null && !a.activity.mFinished) {
try {
am.activityIdle(a.token, a.createdConfig, stopProfiling);
a.createdConfig = null;
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
prev = a;
a = a.nextIdle;
prev.nextIdle = null;
} while (a != null);
}
if (stopProfiling) {
mProfiler.stopProfiling();
}
applyPendingProcessState();
return false;
}
}
final class GcIdler implements MessageQueue.IdleHandler {
@Override
public final boolean queueIdle() {
doGcIfNeeded();//做 GC 操作,VMRuntime.getRuntime().requestConcurrentGC();
purgePendingResources();//清除懸而未決的資源
return false;
}
}
final class PurgeIdler implements MessageQueue.IdleHandler {
@Override
public boolean queueIdle() {
purgePendingResources();//清除懸而未決的資源
return false;
}
}
其他講實話,可能就是處理一系列沒有優先級是那種并需要立刻處理的事情。
HandlerThread
作為一個Android 已封裝好的輕量級異步類,其他他已經給我們做出給了突破,幫我們做到了系統的那一套消息傳遞機制,也幫我們省去自己造一個Thread+Handler的模式。
廢了這麼多話,其實講實話啊,他就是我上面已經說過的Thread+Handler的模式,但是有一個事情要突破啊,Looper咋辦?先放一下,讓我們先看看他的使用方法吧。
使用方法
// 建立Handler執行個體
HandlerThread handlerThread = new HandlerThread("MainActivity");
// 線程啟動
handlerThread.start();
// 基于HandlerThread建構的Looper
Handler handler = new Handler(handlerThread.getLooper()){
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
Log.e(TAG, msg.what+"");
}
};
// 使用handler發送消息
handler.sendEmptyMessage(1);
// 退出Lopper的死循環
handlerThread.quit();
思考
咦,哪裡來的Looper呢???
那隻好先讓我們先看看HandlerThread的家庭裡有哪些成員了。
// 一個繼承自Thread的類
public class HandlerThread extends Thread {
int mPriority; // 優先級
int mTid = -1;
Looper mLooper; // Looper
private @Nullable Handler mHandler; // Handler
}
在上文中我們已經講到過了,
Looper
指的是在
ActivityThread
中定義的,也就是一個全局型的
Looper
,并且他的初始化是在main()這個主入口進入時就已經初始化完畢的。
那我們這兒的Looper是在哪裡進行初始化的呢?
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare(); // 1 --》
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
在HandlerThread中我們能夠看到這樣的一段代碼,從注釋1我們慢慢深入,其實最後回到了我們文章最開始的一段代碼。
// 上述注釋1對應的函數
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed)); // 2 -》
}
// 對目前的線程和MessageQueue進行了存儲
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
溫習一下,就是根據目前的Thread,來建立對應的Looper,并把他放到了ThreadLocal中進行存儲。
總結
Q1:Handler的記憶體洩漏執行個體。
Handler handler;
@Overrideprotected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main);
handler = new Handler(new Handler.Callback() { @Override public boolean handleMessage(@NonNull Message msg) { startActivity(new Intent(MainActivity.this, HandlerTestActivity.class)); return false; } });
new Thread(new Runnable() { @Override public void run() { // 中斷3秒 SystemClock.sleep(3000); handler.sendEmptyMessage(0); } }).start(); }
@Overrideprotected void onDestroy() { super.onDestroy(); Log.e("onDestroy", "已銷毀"); handler.removeCallbacksAndMessages(0); // 2 handler = null; // 1 }
如果不加注釋1和注釋2,這就是一段會記憶體洩露的代碼,看了代碼,應該也清楚邏輯十分簡單,就是一個跳轉。推出程式後,你仍然會看到跳轉,這就是
Handler
的記憶體洩漏。
Q2: 為什麼Handler不能在子線程建立?
這個問題其實有點問題,對于修改過底層的華為的作業系統并不存在這樣的問題,但是正常的
Android
原生系統就不行了,當然這針對的是無參構造函數,如果你通過傳入一個
Looper
來解決,像這樣
handler = Handler(Looper.getMainLooper(), Callback())
,也是沒問題的。
代碼如下
Handler handler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
handler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(@NonNull Message msg) {
startActivity(new Intent(MainActivity.this, HandlerTestActivity.class));
return false;
}
});
new Thread(new Runnable() {
@Override
public void run() {
// 中斷3秒
SystemClock.sleep(3000);
handler.sendEmptyMessage(0);
}
}).start();
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.e("onDestroy", "已銷毀");
handler.removeCallbacksAndMessages(0); // 2
handler = null; // 1
}
通過對報錯溯源,我們就能發現這樣一個問題。
他拿不到
Looper
,因為他不是UI線程。其實這就是問題所在,我們上文講過
sThreadLocal
這個變量,他通過一個
get()
函數擷取了
Looper
。但是這裡存在一個問題,這個
get()
,他擷取的是什麼。是以我們也就進去看看好了。
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
原來,他的取法就是從一個
Map
中進行擷取的,而
key
,就是目前線程,是以當我們在子線程中建立
Handler
的時候,我們是拿不到
Looper
的,因為
key
并不對應。這個時候我們同樣明白了
Looper
也是一個唯一的,因為他不會為我們建立出來的一個子線程再添加一個
Looper
,而是共用。
當然你要知道使用ThreadLocalMap是一個通過消耗空間來換取時間效率的方案。
Q3:為什麼Handler構造方法裡面的Looper不是new出來的?
這個問題的性質和問題2有點類似了,唯一性。
Looper
作為一個事件處理的重要組成部分,想來我們已經看到了,就像多道程式設計技術一樣,這是一個不受控制的過程,我們需要瘋狂的思考安全性,同步性等問題。這也是唯一性的好處,是以事件統一處理,處理起來也就有序。至少在我們的平時使用中已經證明了這是一個可取的方法。