Handler是一個訊息的分發物件,進行訊息的傳送和處理。主要用於非同步訊息的通訊。
Handler配合Looper是Android在單執行緒模型實現多執行緒通訊的重要構件
單執行緒模型是指Android程式啟動時會建立一個主執行緒,只有在主執行緒下才能更新UI,其他執行緒執行耗時操作然後交由主執行緒重新整理頁面。
-
多執行緒通訊實現原理
應用啟動時,會自動初始化Lopper(如果是子執行緒必須自己建立Looper)
然後將Looper丟到ThreadLocal中
開啟lopper.loop()死迴圈讀取訊息
主執行緒中建立Handlerd物件的初始化函式從ThreadLocal拿到Looper中的messageQueue
其他執行緒通過Handler物件向MessageQueue新增訊息。
looper從MessageQueue中獲取訊息,通過訊息所屬的handler物件分發訊息
如果訊息有回撥Runable物件就直接呼叫執行,否則交由handleMessage()方法處理 -
多生產者-單消費者
這裡多執行緒中的一個典型場景就是多生產者對應一個消費者。那麼關鍵是通過什麼保證了執行緒安全
MessageQueue.java、Message.java
生產者:enqueueMessage()
synchronized (this-MessageQueue)
message單向連結串列 -先進後出
消費者:next()
從單連結串列中取出訊息,空就阻塞直到有訊息進入
取出message並更新訊息佇列單連結串列的節點,執行緒安全
當消費執行緒消費速度大於生產速度時,便阻塞在loop的queue.next()中的nativePollOnce()方法裡。此時主執行緒會釋放CPU資源進入休眠狀態,直到下個訊息到達或者有事務發生來喚醒主執行緒工作。
-
為什麼採用死迴圈
既然Android中採用了單執行緒模型,而我們知道執行緒執行完畢就會自動結束。而我們的應用需要一直響應使用者操作,不能開啟就自動關閉了。所以採用了死迴圈 -
系統服務AMS如何與應用的主執行緒進行互動
在進入loop之前開啟Binder執行緒用於接受系統AMS事件,然後Binder通過系統整合Handler的內部類向主執行緒傳送Message -
Activity的生命週期是怎麼實現在死迴圈體外能夠執行起來的?
H的handleMessage()接受不同的生命週期的message,執行不同的生命週期的邏輯
自己實現Handler多執行緒通訊
MyLooper
public class MyLooper {
/**
* 當前執行緒的MyLooper變數
*/
private static ThreadLocal<MyLooper> sThreadLocal = new ThreadLocal<>();
private MyMessageQueue mQueue = new MyMessageQueue();
/**
* 初始化looper物件,並將其存入執行緒中
*/
public static void prepare() {
sThreadLocal.set(new MyLooper());
}
/**
* 從當前執行緒獲取looper物件
* @return
*/
public static MyLooper getLooper() {
return sThreadLocal.get();
}
public MyMessageQueue getQueue() {
return mQueue;
}
/**
* 輪詢訊息佇列,分發訊息
*/
public void loop() {
for (; ; ) {
MyMessage message;
message = mQueue.next();//阻塞直到獲取訊息
message.target.dispatchMessage(message);//訊息分發
}
}
}
複製程式碼
MyHandler
public abstract class MyHandler {
private final MyMessageQueue myQueue;
public MyHandler() {
MyLooper looper = MyLooper.getLooper();
if (looper == null) {
throw new IllegalArgumentException("current thread no MyLooper!");
}
/**
* 從當前執行緒中的Looper物件中獲取訊息佇列
*/
myQueue = looper.getQueue();
}
/**
* 向訊息佇列傳送訊息
*
* @param message
*/
public void sendMessage(MyMessage message) {
message.target = this;//記錄訊息傳送者
myQueue.enqueueMessage(message);
}
/**
* 向訊息佇列傳送回撥函式
*
* @param callBack
*/
public void post(Runnable callBack) {
myQueue.enqueueMessage(getPostMessage(callBack));
}
private static MyMessage getPostMessage(Runnable callBack) {
MyMessage message = new MyMessage();
message.callback = callBack;
return message;
}
/**
* 分發訊息
*
* @param message
*/
public void dispatchMessage(MyMessage message) {
if (message.callback != null) {
handleCallback(message);
} else {
handleMessage(message);
}
}
/**
* 處理回撥函式
*
* @param message
*/
private static void handleCallback(MyMessage message) {
message.callback.run();
}
/**
* 處理訊息
*
* @param message
*/
public abstract void handleMessage(MyMessage message);
}
複製程式碼
MyMessageQueue
public class MyMessageQueue {
public volatile MyMessage mMessages;//保證執行緒可見性
/**
* 將訊息加入訊息佇列
*
* @param msg
* @return
*/
public boolean enqueueMessage(MyMessage msg) {
if (msg == null) {
throw new IllegalArgumentException("Message must not null.");
}
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
synchronized (this) {//執行緒安全
if (mMessages == null) {
mMessages = msg;
} else {
msg.next = mMessages;//先進後出的單連結串列,新訊息加入到訊息頭
mMessages = msg;
}
}
return true;
}
//從訊息列表中獲取訊息
public MyMessage next() {
MyMessage prevMsg = null;
MyMessage msg;
for (; ; ) {
/**
* 我們沒法呼叫Jni的nativeOnePoll自動喚醒執行緒,只能空旋執行緒了
* 每隔一秒檢查一次訊息佇列中是否有新訊息
*/
do {
msg = mMessages;
if (msg == null) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} while (msg == null);
synchronized (this) {//執行緒安全讀取訊息
if (msg != null) {
prevMsg = msg;
mMessages = mMessages.next;//單連結串列刪除訊息
return prevMsg;
}
}
}
}
}
複製程式碼
MyMessage
public class MyMessage {
public MyMessage next;//連結串列節點下一個訊息
public MyHandler target;//訊息傳送的物件
public Runnable callback;//回撥函式
public Object obj;
public int what;
}
複製程式碼
使用
MyHandler handler;
...
new Thread() {
@Override
public void run() {
MyLooper.prepare();//新增looper到執行緒中
handler = new MyHandler() {//初始化MyHandler
@Override
public void handleMessage(final MyMessage message) {
if (message.what == 1) {
runOnUiThread(new Runnable() {//在主執行緒提示訊息
@Override
public void run() {
Toast.makeText(MainActivity.this, message.obj.toString(), Toast.LENGTH_SHORT).show();
}
});
}
}
};
MyLooper.getLooper().loop();//開啟loop輪訓messageQueue
}
}.start();
...
MyMessage message = new MyMessage();
message.what = 1;
message.obj = "hello world:" + event.value + "";
handler.sendMessage(message);//傳送訊息
複製程式碼