一、概述
Android的消息機制,其實就是指Handler運行機制,而與之相關的就是MessageQueue和Looper;
Handler常見的用法:
用于更新UI,比如在子線程中通過UIHandler來更新UI;
在工作線程中處理耗時操作,如文件讀寫和網絡訪問操作;
Handler核心作用就是消息的分發和處理,以及多線程間的線程切換,Handler可以輕松的將任務切換到Handler所在的線程中執行:Handler將消息發送給MessageQueue,接收Looper返回的消息然后處理;
MessageQueue是消息隊列,它內部的存儲結構是單鏈表的數據結構,它只負責消息的存儲;
而Looper為消息循環,它負責管理MessageQueue,它工作的方式是無限循環的檢查消息隊列中是否有新消息,有的話就將消息交給Handler處理,否則就一直等待;
一個線程中只能包含一個Looper對象。
二、創建Handler的方法示例
1、UIHandler(主線程中的Handler)
方法一(推薦的方式):
Handler uiHandler=new Handler(Looper.getMainLooper());
方法二:
在主線程中,直接使用Handler的無參構造函數:
Handler uiHandler=new Handler();
2.workHandler的創建
方法一、利用HandlerThread來創建workHandler
HandlerThread handlerThread=new HandlerThread();
handlerThread.start();
Handler workHandler=new Handler(handlerThread.getLooper());
方法二、在子線程中直接創建workHandler,注意默認的開啟一個新的線程的時候,這個新線程是不包含Looper的,所以需要為新線程添加一個Looper
new Thread(){
@override
public void run(){
Looper.prepare();//創建Looper實例,并賦值給當前Thread,細節代碼下文會有解釋;
Looper.loop();
Handler workHandler=new Handler();
}
}.start();
三、Thread
Thread是一個并行執行單元,每個應用程序至少有一個Thread即主線程;Thread有自己的Call Stack,用于存放方法調用相關的信息;
當新建一個線程的時候,默認的是沒有Message Looper的,想要創建一個Looper需要在Thread的run()方法中去創建一個:
調用Looper.prepare(),創建一個Looper實例,將創建的實例存入當前線程的LocalValues中,然后調用Looper.loop()方法,開始處理消息;
四、Looper
1.prepare()方法
我們來看下Looper.prepare()方法:
public static void prepare() {
prepare(true);
}
內部調用下面的方法
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));
}
調用sThreadLocal.get(),從當前線程中去獲取Looper對象,如果獲取到的looper不為null,那么會拋出異常:
這里表明一個線程只能有一個Looper對象,如果線程中已經有了looper,再調用Looper.prepare()就會拋異常;
但是看到這里,會有一個疑惑,sThreadLocal是個是什么東西?
sThreadLocal.get()和sThreadLocal.set(new Looper(quitAllowed))兩個方法是什么意思?
首先,看下sThreadLocal這個變量是什么含義:
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
sThreadLocal是Looper的靜態成員變量;
2.ThreadLocal
ThreadLocal<T>,官方文檔:
Thread自己的存儲類,每個Thread都有自己的值;
所有的threads共享一個ThreadLocal對象,但是每個thread獲取的值是不一樣;
然后還說每一個thread所做的改變不會影響其他thread值的獲取。
前面的話好理解,后邊的兩句話是什么意思?
ThreadLocal是Google大神們很巧妙的設計,至于大神們是怎么想到這樣的設計,我們無從得知;
理解ThreadLocal<T>的工作原理,只需要弄懂put()和get()兩個方法;
2.1.ThreadLocal的set()
首先來看下ThreadLocal<T>的set()方法:
public void set(T value) {
//獲取調用set()方法的線程
Thread currentThread = Thread.currentThread();
//從當前線程中獲取Values,如果為空,則創建
Values values = values(currentThread);
if (values == null) {
values = initializeValues(currentThread);
}
//將方法參數直接存入當前線程的values中
values.put(this, value);
}
我們會看到set()會獲取當前所在的線程(set()方法所在的線程,也就是Looper.prepare()所在的線程),然后會調用values(currentThread):
Values values(Thread current) {
return current.localValues;
}
獲取到當前線程的localValues;也就是當前線程自己的Values對象,如果此時線程的localValues沒有初始化,會對其初始化:new Values()對象;
接著調用vaules中的put()方法:
void put(ThreadLocal<?> key, Object value) {
cleanUp();
// Keep track of first tombstone. That's where we want to go back
// and add an entry if necessary.
int firstTombstone = -1;
for (int index = key.hash & mask;; index = next(index)) {
Object k = table[index];
if (k == key.reference) {
// Replace existing entry.
table[index + 1] = value;
return;
}
if (k == null) {
if (firstTombstone == -1) {
// Fill in null slot.
table[index] = key.reference;
table[index + 1] = value;
size++;
return;
}
// Go back and replace first tombstone.
table[firstTombstone] = key.reference;
table[firstTombstone + 1] = value;
tombstones--;
size++;
return;
}
// Remember first tombstone.
if (firstTombstone == -1 && k == TOMBSTONE) {
firstTombstone = index;
}
}
}
put()方法的第一個參數是sThreadLocal對象,第二個參數是要保存的值即looper對象;
mask的值則是在新建values對象的時候賦的值:
private void initializeTable(int capacity) {
this.table = new Object[capacity * 2];
this.mask = table.length - 1;
this.clean = 0;
this.maximumLoad = capacity * 2 / 3; // 2/3
}
從上邊的代碼可以看出mask的值為table.length-1;
上邊方法中的for循環,index的初始化值為index= key.hash & mask,直接結果就是將index的值不會超出table數組下標的范圍;
第一次執行的時候,獲取到的k值為null,所以直接走:
if (k == null) {
if (firstTombstone == -1) {
// Fill in null slot.
table[index] = key.reference;
table[index + 1] = value;
size++;
return;
}
// Go back and replace first tombstone.
table[firstTombstone] = key.reference;
table[firstTombstone + 1] = value;
tombstones--;
size++;
return;
}
那么table[index]和table[index+1]分別被賦值為sThreadLocal對象的reference值(這個reference是一個WeakReference對象)和looper對象;
上面可以看出,將相關的一組數據保存到相鄰的數組值中;
2.2.ThreadLocal的get()
再來看下,get()方法:
public T get() {
// 獲取調用get()方法的線程
Thread currentThread = Thread.currentThread();
//獲取當前線程的字段values;
Values values = values(currentThread);
if (values != null) {
//獲取當前線程的values中的table數組;
Object[] table = values.table;
int index = hash & values.mask;
if (this.reference == table[index]) {
return (T) table[index + 1];
}
} else {
values = initializeValues(currentThread);
}
return (T) values.getAfterMiss(this);
}
首先還是獲取當前線程,接著獲取當前線程的localValues;
如果localValues不為null,獲取localValues中的table數組,根據sThreadLocal的hash和localValues的mask值來計算index值,判斷sThreadLocal的reference值是否與table[index]相等,相等的直接返回table[index+1];
我們在調用set(value)的時候,將reference和value存放到線程的values的table數組,且兩者位置相鄰,而我們在調用get()方法時在根據reference確定要去的值在線程的Values的table中的index,然后直接獲取到相應的值table[index+1];
這里可以看到ThreadLocal的set()和get()協同工作,完美配合;
讀到此時,仔細的讀者可能還會有個疑問:
所有的threads共享一個Looper的靜態變量ThreadLocal對象,不同的thread根據sThreadLocal.get()獲取的值卻不同,為什么?
這個問題進一步具體的闡述就是這樣一個疑問:
不同的線程來調用Looper.prepare()來獲取looper對象:
比如A線程的run()方法中調用了Looper.prepare(),然后就會調用sThreadLocal.set(new Looper(quitAllowed))方法;
B線程的run()方法中調用Looper.prepare(),也會調用sThreadLocal.set(new Looper(quitAllowed))方法;
A和B線程中通過index=hash & values.mask的算法計算出的index值其實是一樣的,并且A和B線程調用是同一個sThreadLocal對象的set()方法和get()方法,存入的值不會混淆嗎?
相信仔細的讀者可能會有這樣一個疑問,這個疑問的答案在上面對ThreadLocal的set(value)和get()方法的分析中已經給出了答案:
public void set(T value) {
Thread currentThread = Thread.currentThread();
Values values = values(currentThread);
if (values == null) {
values = initializeValues(currentThread);
}
values.put(this, value);
}
這里邊根據當前線程來獲取線程自己的values值,然后線程自己的 values.table中根據index的值來存入和獲取相關的數據;
同樣的,看下get()方法:
public T get() {
Thread currentThread = Thread.currentThread();
Values values = values(currentThread);
if (values != null) {
Object[] table = values.table;
int index = hash & values.mask;
if (this.reference == table[index]) {
return (T) table[index + 1];
}
} else {
values = initializeValues(currentThread);
}
return (T) values.getAfterMiss(this);
}
也是先拿到當前線程的localValues值,然后再獲取這個localValues中的相關數據;
也就是說不同的線程有自己的values對象,某一個線程是不能修改或者獲取其他線程的values對象的;這樣就保證了線程之間不會相互干擾。
接著分析thread關聯looper的流程,在Looper.prepare()方法中,會新建一個Looper實例:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
我們看到新建一個Looper實例時,會初始化mQueue參數,將新建一個MessageQueue對象,然后會將當前線程賦值給Looper的成員變量mThread,也就是說Looper中的mThread引用了該Looper所在的線程;
3.loop()方法:
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
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) {
// 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);
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
// Make sure that during the course of dispatching the
// identity of the thread wasn't corrupted.
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
Log.wtf(TAG, "Thread identity changed from 0x"
+ Long.toHexString(ident) + " to 0x"
+ Long.toHexString(newIdent) + " while dispatching to "
+ msg.target.getClass().getName() + " "
+ msg.callback + " what=" + msg.what);
}
msg.recycleUnchecked();
}
}
內部調用myLooper():
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
獲取當前線程的looper,接著獲取當前線程looper的mQueue消息隊列;
進入到for循環,無限循環中,調用MessageQueue的next()方法,獲取消息message,注意這里
Message msg = queue.next(); // might block
注釋,此方法可能會被阻塞,looper沒有stop的情況下,MessageQueue中沒有消息,則會被阻塞,不會返回null;
如果message不為null:
msg.target.dispatchMessage(msg);
會調用message對象中的handle對象的dispatchMessage(msg);
五、Handler
5.1
我們在新建一個Handler對象的時候,通常會使用Handler的構造方法:
new Handler(looper);
這個方法內部調用的是Handler的構造函數:
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
可以看到在構造方法中,會將looper賦值給Handler對象的成員變量mLooper,并且將looper中的looper.mQueue賦值給mQueue;
從上面的Looper.prepare()分析知道thread跟looper是一一對應的關系,而looper跟MessageQueue也是一一對應的關系
5.2
當直接調用new Handler()的時候,Handler中的成員變量mLooper給定一個默認的值,當前線程的looper對象:
public Handler() {
this(null, false);
}
內部調用的方法:
public Handler(Callback callback, boolean async) {
......
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
可以看到mLooper = Looper.myLooper();即如果在創建Handler的時候不指定Looper對象,那么默認使用當前線程的Looper對象,如果當前線程的Looper為空的話,就會拋出異常;
5.3.post()
handler的post(runable)方法:
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
內部調用:
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
這里就可以看出,Handler消息分發最終都會走sendMessage()方法;
內部調用:
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
內部調用:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
這里就會調用構造Handler對象時賦值給mQueue的MessageQueue對象的enqueueMessage()方法,將消息傳入到mQueue中存儲,后續Looper對象會調用loop()方法來提取存儲的消息;
注意這一行代碼:
msg.target = this;
這行代碼表明,消息隊列中的message會持有相關Handler對象,以便在后續的 message loop過程中將消息分發給對應的Handler對象來處理;
那么在界面中如果有匿名的Handler對象,就會造成內存泄漏(匿名類會隱性地持有外部類,然后message有持有Handler對象,導致外部類持有的資源不能釋放,如果外部類是個Activity界面,就會導致Activity占用的資源得不到釋放);
5.4.dispatchMessage()
dispatchMessage(msg):
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
判斷message對象是否有callback,有的話進入 handleCallback(msg):
private static void handleCallback(Message message) {
message.callback.run();
}
該方法中會執行message.callback中的run()方法;
message對象中沒有callback的話,判斷當前handler中的成員變量mCallback是否為null:
不為null,執行mCallback 的handleMessage(msg);
為null,執行Handler中的 方法handleMessage(msg):
public void handleMessage(Message msg) {}
也就是說,Handler中的handleMessage()的優先級最低;
六、MessageQueue
6.1.MessageQueue中的enqueueMessage
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
synchronized (this) {
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
在enqueueMessage中首先判斷,如果當前的消息隊列為空,或者新添加的消息的執行時間when是0,或者新添加的消息的執行時間比消息隊列頭的消息的執行時間還早,就把消息添加到消息隊列頭(消息隊列按時間排序),否則就要找到合適的位置將當前消息添加到消息隊列。
6.2.next()方法:
Message next() {
// Return here if the message loop has already quit and been disposed.
// This can happen if the application tries to restart a looper after quit
// which is not supported.
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Got a message.
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose();
return null;
}
// If first time idle, then get the number of idlers to run.
// Idle handles only run if the queue is empty or if the first message
// in the queue (possibly a barrier) is due to be handled in the future.
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
// No idle handlers to run. Loop and wait some more.
mBlocked = true;
continue;
}
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// Run the idle handlers.
// We only ever reach this code block during the first iteration.
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);
}
}
}
// Reset the idle handler count to 0 so we do not run them again.
pendingIdleHandlerCount = 0;
// While calling an idle handler, a new message could have been delivered
// so go back and look again for a pending message without waiting.
nextPollTimeoutMillis = 0;
}
}
獲取消息隊列中的消息,如果沒有消息,則阻塞;
有的話取出消息并返回message;
七、總結
梳理下上面的邏輯:
- 假設我們新建了一個線程A,并且調用了Looper.prepare(),這個方法將looper對象賦值給了Thread A中的localValues;
- 新建一個Handler(looper),構造函數中傳入線程A的looper對象,那么調用該Handler對象的post方法,會把消息傳入到線程A的looper對象中的MessageQueue對象中;
- 接著調用Looper.loop()方法,Looper.loop()方法是內部有一個無限循環,所以它會檢查消息隊列的消息是否更新,如果有新消息,那么就會提取;獲取之后會調用message中存儲的Handler對象的dispatchMessage(msg)來進行處理;
- 沒有新消息,該方法會被阻塞;只到有新消息,會繼續提取和分發消息;