在平常開發中,咱們勢必會使用到子線程和UI線程的通訊,而起着橋樑做用的就是咱們經常使用的Handler
。可是他的內部是怎麼運做的?運做的過程當中存在什麼問題?須要咱們注意,本文將會詳細講解。java
從圖中咱們就能夠知道了,整個Handler
工做組成的包括了Handler
、Looper
、MessageQueue
、Message
這四個部分。git
MessageQueue和Message分別只是一個隊列和消息實體類,天然再也不多說。 而Handler和Looper的具體是怎樣的呢?github
在個人模擬Handler項目中,已經比較清晰的闡述了整個框架的工做流程,接下里就是結合SDK代碼的一份解析了。編程
整個Handler往簡單了來講其實就幹了兩件事情:安全
涉及到的三個函數sendMessage()
、enqueueMessage()
、Looper.prepareMainLooper()
。框架
全部事情的起源要從Looper.prepareMainLooper()
開始講起。 這個函數處於ActivityThread
中,沒有了解過這個類的讀者們須要知道,java編程必定是有一個主入口的,可是咱們在整個Android編程中,歷來沒有涉及過main()
這個函數,是由於它已經包含在了ActivityThread
這個類中,而它已經通過了複雜的封裝。ide
接下來看下這個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
這個對象也是一個惟一的變量。這是咱們須要掌握的第一個知識點。oop
接下來是關於sendMessage()
函數 這個函數實際上是一個泛稱,他並不僅僅指sendMessage()
,他還能夠是sendMessageAtTime()
、sendMessageDelayed()
,他們都幹了一件事情——傳遞消息。經過一直向下探索,你就能知道他們最後調用的都是enqueueMessage()
這個函數,也就是把消息放進了消息隊列中。post
沒有不少的操做,就是咱們熟悉的鏈表操做。這裏沒有作展現,有興趣的朋友進到源碼往下翻一點,立刻就能看到了。
就這樣很簡單,而且很成功的讓咱們的消息進入了消息隊列。
接收完消息,咱們要幹嗎?咱們爲何要發消息,由於咱們要處理啊。
這裏咱們要遇到的函數有: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
對象。
害,原來就是這樣啊。。
問題1:Handler的內存泄漏實例。
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
}
複製代碼
若是不加註釋1和註釋2,這就是一段會內存泄露的代碼,看了代碼,應該也清楚邏輯十分簡單,就是一個跳轉。推出程序後,你仍然會看到跳轉,這就是Handler
的內存泄漏。
問題2: 爲何Handler不能在子線程建立?
這個問題其實有點問題,對於修改過底層的華爲的操做系統並不存在這樣的問題,可是正常的Android
原生系統就不行了。 代碼以下
new Thread(new Runnable() {
@Override
public void run() {
handler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(@NonNull Message msg) {
return false;
}
});
}
}).start();
複製代碼
經過對報錯溯源,咱們就能發現這樣一個問題。
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
的,這個時候咱們一樣明白了Looper
也是一個惟一的,由於他不會爲咱們建立出來的一個子線程再添加一個Looper
,而是共用。
就這個問題,Google其實有給出解決方案,詳細請看 》》HandlerThread那些事兒
問題3:爲何Handler構造方法裏面的Looper不是new出來的?
這個問題的性質和問題2有點相似了,惟一性。Looper
做爲一個事件處理的重要組成部分,想來咱們已經看到了,就像多道程序設計技術同樣,這是一個不受控制的過程,咱們須要瘋狂的思考安全性,同步性等問題。這也是惟一性的好處,因此事件統一處理,處理起來也就有序。至少在咱們的平時使用中已經證實了這是一個可取的方法。
以上就是個人學習成果,若是有什麼我沒有思考到的地方或是文章內存在錯誤,歡迎與我分享。
相關文章推薦: