Android-Handler機制詳解並自定義Handler
之前研究過Android的Handler機制,但是一直沒有機會自己實現一次。最近又看到這個Handler機制,於是決定自己實現以下這個Handler機制。
首先,簡單介紹以下Handler機制。
Handler機制在Android中通常用來更新UI:子執行緒執行任務,任務執行完畢後傳送訊息:Handler.sendMessage(),然後在UI執行緒Handler.handleMessage()就會呼叫,執行相應處理。
Handler機制有幾個非常重要的類:
Handler:用來發送訊息:sendMessage等多個方法,並實現handleMessage()方法處理回撥(還可以使用Message或Handler的Callback進行回撥處理,具體可以看看原始碼)。
Message:訊息實體,傳送的訊息即為Message型別。
MessageQueue:訊息佇列,用於儲存訊息。傳送訊息時,訊息入佇列,然後Looper會從這個MessageQueen取出訊息進行處理。
Looper:與執行緒繫結,不僅僅侷限於主執行緒,繫結的執行緒用來處理訊息。loop()方法是一個死迴圈,一直從MessageQueen裡取出訊息進行處理。
原理圖如下:
按照上面介紹的4個重要組成類,從內向外依次自定義相應的類,實現Handler機制。
MyMessage類:
public class MyMessage {
private int code;
private String msg;
MyHandler target;
public MyMessage(int code, String msg) {
this.code = code;
this.msg = msg;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
}
MessageQueue類:(由於該類可以有多種實現方式,所以在此定義一個介面)
public interface IMyMessageQueue {
MyMessage next() throws InterruptedException;
void enqueueMsg(MyMessage msg) throws InterruptedException;
}
從Android原始碼中瞭解到,這個MessageQueue是一個佇列,所以在此簡單的已一個BlockingQueue佇列來實現訊息的進隊出隊操作:
public class MyBlockingQueue implements IMyMessageQueue {
private BlockingQueue<MyMessage> mQueue;
public MyBlockingQueue(int init) {
this.mQueue = new LinkedBlockingDeque<>(init);
}
@Override
public MyMessage next() throws InterruptedException {
return mQueue.take();
}
@Override
public void enqueueMsg(MyMessage msg) throws InterruptedException {
mQueue.put(msg);
}
}
MyLooper類:持有一個MyMessageQueue例項,並使用ThreadLocal綁定當前執行緒,啟動一個死迴圈。
public class MyLooper {
private static final ThreadLocal<MyLooper> THREAD_LOCAL = new ThreadLocal<>();
IMyMessageQueue mMessageQueue;
private static MyLooper mMainLooper;
public MyLooper() {
mMessageQueue = new MyBlockingQueue(4);
}
public static void prepare() {
if (null != THREAD_LOCAL.get()) {
throw new RuntimeException("Only one looper can be created per thread.");
}
THREAD_LOCAL.set(new MyLooper());
}
public static void prepareMainLooper() {
prepare();
synchronized (MyLooper.class) {
if (null != mMainLooper) {
throw new RuntimeException("MainLooper has already been prepared.");
}
mMainLooper = myLooper();
}
}
public static void loop() {
final MyLooper looper = myLooper();
if (null == looper) {
throw new RuntimeException("No looper! MyLooper.prepare() wasn't called on this thread.");
}
for (; ; ) {
MyMessage msg = null;
try {
msg = looper.mMessageQueue.next();
} catch (InterruptedException e) {
e.printStackTrace();
}
if (null != msg) {
msg.target.handleMessage(msg);
}
}
}
public static MyLooper getMainLooper() {
return mMainLooper;
}
public static MyLooper myLooper() {
return THREAD_LOCAL.get();
}
}
MyHandler類:持久訊息佇列,並定義一個抽象方法,負責傳送和處理訊息。
public abstract class MyHandler {
private IMyMessageQueue mQueue;
public MyHandler(MyLooper looper) {
mQueue = looper.mMessageQueue;
}
public MyHandler() {
MyLooper.myLooper();
}
public void sendMessage(MyMessage msg) {
msg.target = this;
try {
mQueue.enqueueMsg(msg);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public abstract void handleMessage(MyMessage msg);
}
到此,自定義Handler功能程式碼實現,下面測試一下啟動MyMainLooper,並在子執行緒中使用MyHandler傳送和處理訊息。
TestMyHandler
public class TestMyHandler {
@Test
public void test() {
MainThread mainThread = new MainThread();
mainThread.start();
while (null == MyLooper.getMainLooper()) {
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
MyHandler handler = new MyHandler(MyLooper.getMainLooper()) {
@Override
public void handleMessage(MyMessage msg) {
switch (msg.getCode()) {
case 1:
System.out.print(msg.getMsg() + "在" + Thread.currentThread().getName() + "上執行!\r\n");
break;
case 2:
System.out.print(msg.getMsg() + "在" + Thread.currentThread().getName() + "上執行!\r\n");
break;
}
}
};
new Thread(() -> {
MyMessage myMessage = new MyMessage(1, "子執行緒訊息1");
handler.sendMessage(myMessage);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
MyMessage myMessage2 = new MyMessage(2, "子執行緒訊息2");
handler.sendMessage(myMessage2);
}).run();
}
/**
* 主執行緒
*/
public class MainThread extends Thread {
public MainThread() {
setName("MainThread");
}
@Override
public void run() {
MyLooper.prepareMainLooper();
System.out.print(getName() + "has been prepared.\r\n");
MyLooper.loop();
}
}
}
測試結果: