在android開發中,經常會在子線程中進行一些操作,當操作完畢後會通過handler發送一些數據給主線程,通知主線程做相應的操作。
探索其背後的原理:子線程 handler 主線程 其實構成了線程模型中的經典問題 生產者消費者模型。
生產者消費者模型:生產者和消費者在同一時間段內共用同一個存儲空間,生產者往存儲空間中添加數據,消費者從存儲空間中取走數據
好處:
- 保證數據生產消費的順序(通過MessageQueue,先進先出)
- 不管是生產者(子線程)還是消費者(主線程)都只依賴緩衝區(handler),生產者消費者之間不會相互持有,使他們之間沒有任何耦合
源碼分析:
- Handler
- Handler機制的相關類
- 創建Looper
- 創建MessageQueue以及Looper與當前線程的綁定
- Looper.loop()
- 創建Handler
- 創建Message
- Message和Handler的綁定
- Handler發送消息
- Handler處理消息
Handler機制的相關類
Hanlder:發送和接收消息
Looper:用於輪詢消息隊列,一個線程只能有一個Looper
Message:
MessageQueue:
創建Looper
創建Looper的方法是調用Looper.prepare() 方法
在ActivityThread中的main方法中爲我們prepare了
public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
//其他代碼省略...
Looper.prepareMainLooper(); //初始化Looper以及MessageQueue
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop(); //開始輪循操作
throw new RuntimeException("Main thread loop unexpectedly exited");
}
Looper.prepareMainLooper();
public static void prepareMainLooper() {
prepare(false);//消息隊列不可以quit
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
prepare有兩個重載的方法,主要看 prepare(boolean quitAllowed) quitAllowed的作用是在創建MessageQueue時標識消息隊列是否可以銷燬, 主線程不可被銷燬 下面有介紹
public static void prepare() {
prepare(true);//消息隊列可以quit
}
//quitAllowed 主要
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {//不爲空表示當前線程已經創建了Looper
throw new RuntimeException("Only one Looper may be created per thread");
//每個線程只能創建一個Looper
}
sThreadLocal.set(new Looper(quitAllowed));//創建Looper並設置給sThreadLocal,這樣get的時候就不會爲null了
}
創建MessageQueue以及Looper與當前線程的綁定
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);//創建了MessageQueue
mThread = Thread.currentThread(); //當前線程的綁定
}
MessageQueue的構造方法
MessageQueue(boolean quitAllowed) {
//mQuitAllowed決定隊列是否可以銷燬 主線程的隊列不可以被銷燬需要傳入false, 在MessageQueue的quit()方法就不貼源碼了
mQuitAllowed = quitAllowed;
mPtr = nativeInit();
}
Looper.loop()
同時是在main方法中 Looper.prepareMainLooper() 後Looper.loop(); 開始輪詢
public static void loop() {
final Looper me = myLooper();//裏面調用了sThreadLocal.get()獲得剛纔創建的Looper對象
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}//如果Looper爲空則會拋出異常
final MessageQueue queue = me.mQueue;
// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
for (;;) {
//這是一個死循環,從消息隊列不斷的取消息
Message msg = queue.next(); // might block
if (msg == null) {
//由於剛創建MessageQueue就開始輪詢,隊列裏是沒有消息的,等到Handler sendMessage enqueueMessage後
//隊列裏纔有消息
// No message indicates that the message queue is quitting.
return;
}
// This must be in a local variable, in case a UI event sets the logger
Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
msg.target.dispatchMessage(msg);//msg.target就是綁定的Handler,詳見後面Message的部分,Handler開始
//後面代碼省略.....
msg.recycleUnchecked();
}
}
創建Handler
最常見的創建handler
Handler handler=new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
在內部調用 this(null, false);
public Handler(Callback callback, boolean async) {
//前面省略
mLooper = Looper.myLooper();//獲取Looper,**注意不是創建Looper**!
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;//創建消息隊列MessageQueue
mCallback = callback; //初始化了回調接口
mAsynchronous = async;
}
Looper.myLooper();
//這是Handler中定義的ThreadLocal ThreadLocal主要解多線程併發的問題
// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
sThreadLocal.get() will return null unless you’ve called prepare(). 這句話告訴我們get可能返回null 除非先調用prepare()方法創建Looper。在前面已經介紹了
創建Message
可以直接new Message 但是有更好的方式 Message.obtain。因爲可以檢查是否有可以複用的Message,用過複用避免過多的創建、銷燬Message對象達到優化內存和性能的目地
public static Message obtain(Handler h) {
Message m = obtain();//調用重載的obtain方法
m.target = h;//並綁定的創建Message對象的handler
return m;
}
public static Message obtain() {
synchronized (sPoolSync) {//sPoolSync是一個Object對象,用來同步保證線程安全
if (sPool != null) {//sPool是就是handler dispatchMessage 後 通過recycleUnchecked 回收用以複用的Message
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}
Message和Handler的綁定
創建Message的時候可以通過 Message.obtain(Handler h) 這個構造方法綁定。當然可以在 在Handler 中的 enqueueMessage()也綁定了,所有發送Message的方法都會調用此方法入隊,所以在創建Message的時候是可以不綁定的
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
Handler發送消息
Handler發送消息的重載方法很多,但是主要只有2種。
sendMessage(Message)
sendMessage方法通過一系列重載方法的調用,sendMessage調用sendMessageDelayed,繼續調用sendMessageAtTime,繼續調用enqueueMessage,繼續調用messageQueue的enqueueMessage方法,將消息保存在了消息隊列中,而最終由Looper取出,交給Handler的dispatchMessage進行處理
我們可以看到在dispatchMessage方法中,message中callback是一個Runnable對象,如果callback不爲空,則直接調用callback的run方法,否則判斷mCallback是否爲空,mCallback在Handler構造方法中初始化,在主線程通直接通過無參的構造方法new出來的爲null,所以會直接執行後面的handleMessage()方法。
public void dispatchMessage(Message msg) {
if (msg.callback != null) {//callback在message的構造方法中初始化或者使用handler.post(Runnable)時候纔不爲空
handleCallback(msg);
} else {
if (mCallback != null) {//mCallback是一個Callback對象,通過無參的構造方法創建出來的handler,該屬性爲null,此段不執行
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);//最終執行handleMessage方法
}
}
private static void handleCallback(Message message) {
message.callback.run();
}
Handler處理消息
在handleMessage(Message)方法中,我們可以拿到message對象,根據不同的需求進行處理,整個Handler機制的流程就結束了。