Android消息循環機制源碼
前言:
搞Android的不懂Handler消息循環機制,都不好意思說自己是Android工程師。面試的時候一般也都會問這個知識點,但是我相信大多數碼農肯定是沒有看過相關源碼的,頂多也就是網上搜搜,看看別人的文章介紹。學姐不想把那個萬能的關系圖拿出來討論。
近來找了一些關于android線程間通信的資料,整理學習了一下,并制作了一個簡單的例子。
andriod提供了 Handler 和 Looper 來滿足線程間的通信。例如一個子線程從網絡上下載了一副圖片,當它下載完成后會發送消息給主線程,這個消息是通過綁定在主線程的Handler來傳遞的。
在Android,這里的線程分為有消息循環的線程和沒有消息循環的線程,有消息循環的線程一般都會有一個Looper,這個事android的新 概念。我們的主線程(UI線程)就是一個消息循環的線程。針對這種消息循環的機制,我們引入一個新的機制Handle,我們有消息循環,就要往消息循環里 面發送相應的消息,自定義消息一般都會有自己對應的處理,消息的發送和清除,消息的的處理,把這些都封裝在Handle里面,注意Handle只是針對那 些有Looper的線程,不管是UI線程還是子線程,只要你有Looper,我就可以往你的消息隊列里面添加東西,并做相應的處理。
但是這里還有一點,就是只要是關于UI相關的東西,就不能放在子線程中,因為子線程是不能操作UI的,只能進行數據、系統等其他非UI的操作。
在Android,這里的線程分為有消息循環的線程和沒有消息循環的線程,有消息循環的線程一般都會有一個Looper,這個是android的新概念。我們的主線程(UI線程)就是一個消息循環的線程。針對這種消息循環的機制,我們引入一個新的機制Handler,我們有消息循環,就要往消息循環里面發送相應的消息,自定義消息一般都會有自己對應的處理,消息的發送和清除,把這些都封裝在Handler里面,注意Handler只是針對那 些有Looper的線程,不管是UI線程還是子線程,只要你有Looper,我就可以往你的消息隊列里面添加東西,并做相應的處理。
但是這里還有一點,就是只要是關于UI相關的東西,就不能放在子線程中,因為子線程是不能操作UI的,只能進行數據、系統等其他非UI的操作。
先從我們平時的使用方法引出這個機制,再結合源碼進行分析。
我們平時使用是這樣的:
//1. 主線程
Handler handler = new MyHandler();
//2. 非主線程
HandlerThread handlerThread = new HandlerThread("handlerThread");
handlerThread.start();
Handler handler = new Handler(handlerThread.getLooper());
//發送消息
handler.sendMessage(msg);
//接收消息
static class MyHandler extends Handler {
//對于非主線程處理消息需要傳Looper,主線程有默認的sMainLooper
public MyHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
}
那么為什么初始化的時候,我們執行了1或2,后面只需要sendMessage就可處理任務了呢?學姐這里以非主線程為例進行介紹,handlerThread.start()的時候,實際上創建了一個用于消息循環的Looper和消息隊列MessageQueue,同時啟動了消息循環,并將這個循環傳給Handler,這個循環會從MessageQueue中依次取任務出來執行。用戶若要執行某項任務,只需要調用handler.sendMessage即可,這里做的事情是將消息添加到MessaeQueue中。對于主線程也類似,只是主線程sMainThread和sMainLooper不需要我們主動去創建,程序啟動的時候Application就創建好了,我們只需要創建Handler即可。
我們這里提到了幾個概念:
對應關系是:一對多,即(一個)HandlerThread、Looper、MessageQueue -> (多個)Handler、Message
源碼解析
1. Looper
(1)創建消息循環
prepare()用于創建Looper消息循環對象。Looper對象通過一個成員變量ThreadLocal進行保存。
(2)獲取消息循環對象
myLooper()用于獲取當前消息循環對象。Looper對象從成員變量ThreadLocal中獲取。
(3)開始消息循環
loop()開始消息循環。循環過程如下:
每次從消息隊列MessageQueue中取出一個Message
使用Message對應的Handler處理Message
已處理的Message加到本地消息池,循環復用
循環以上步驟,若沒有消息表明消息隊列停止,退出循環
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));
}
public static Looper myLooper() {
return sThreadLocal.get();
}
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();
}
}
2. Handler
(1)發送消息
Handler支持2種消息類型,即Runnable和Message。因此發送消息提供了post(Runnable r)和sendMessage(Message msg)兩個方法。從下面源碼可以看出Runnable賦值給了Message的callback,最終也是封裝成Message對象對象。學姐個人認為外部調用不統一使用Message,應該是兼容Java的線程任務,學姐認為這種思想也可以借鑒到平常開發過程中。發送的消息都會入隊到MessageQueue隊列中。
(2)處理消息
Looper循環過程的時候,是通過dispatchMessage(Message msg)對消息進行處理。處理過程:先看是否是Runnable對象,如果是則調用handleCallback(msg)進行處理,最終調到Runnable.run()方法執行線程;如果不是Runnable對象,再看外部是否傳入了Callback處理機制,若有則使用外部Callback進行處理;若既不是Runnable對象也沒有外部Callback,則調用handleMessage(msg),這個也是我們開發過程中最常覆寫的方法了。
(3)移除消息
removeCallbacksAndMessages(),移除消息其實也是從MessageQueue中將Message對象移除掉。
public void handleMessage(Message msg) {
}
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
private static void handleCallback(Message message) {
message.callback.run();
}
public final Message obtainMessage()
{
return Message.obtain(this);
}
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
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);
}
public final void removeCallbacksAndMessages(Object token) {
mQueue.removeCallbacksAndMessages(this, token);
}
3. MessageQueue
(1)消息入隊
消息入隊方法enqueueMessage(Message msg, long when)。其處理過程如下:
待入隊的Message標記為InUse,when賦值
若消息鏈表mMessages為空為空,或待入隊Message執行時間小于mMessage鏈表頭,則待入隊Message添加到鏈表頭
若不符合以上條件,則輪詢鏈表,根據when從低到高的順序,插入鏈表合適位置
(2)消息輪詢
next()依次從MessageQueue中取出Message
(3)移除消息
removeMessages()可以移除消息,做的事情實際上就是將消息從鏈表移除,同時將移除的消息添加到消息池,提供循環復用。
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("MessageQueue", 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;
}
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 (false) Log.v("MessageQueue", "Returning message: " + msg);
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("MessageQueue", "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;
}
}
void removeMessages(Handler h, int what, Object object) {
if (h == null) {
return;
}
synchronized (this) {
Message p = mMessages;
// Remove all messages at front.
while (p != null && p.target == h && p.what == what
&& (object == null || p.obj == object)) {
Message n = p.next;
mMessages = n;
p.recycleUnchecked();
p = n;
}
// Remove all messages after front.
while (p != null) {
Message n = p.next;
if (n != null) {
if (n.target == h && n.what == what
&& (object == null || n.obj == object)) {
Message nn = n.next;
n.recycleUnchecked();
p.next = nn;
continue;
}
}
p = n;
}
}
}
4. Message
(1)消息創建
Message.obtain()創建消息。若消息池鏈表sPool不為空,則從sPool中獲取第一個,flags標記為UnInUse,同時從sPool中移除,sPoolSize減1;若消息池鏈表sPool為空,則new Message()
(2)消息釋放
recycle()將消息釋放,從內部實現recycleUnchecked()可知,將flags標記為InUse,其他各種狀態清零,同時將Message添加到sPool,且sPoolSize加1
/**
* Return a new Message instance from the global pool. Allows us to
* avoid allocating new objects in many cases.
*/
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}
/**
* Return a Message instance to the global pool.
* <p>
* You MUST NOT touch the Message after calling this function because it has
* effectively been freed. It is an error to recycle a message that is currently
* enqueued or that is in the process of being delivered to a Handler.
* </p>
*/
public void recycle() {
if (isInUse()) {
if (gCheckRecycle) {
throw new IllegalStateException("This message cannot be recycled because it "
+ "is still in use.");
}
return;
}
recycleUnchecked();
}
/**
* Recycles a Message that may be in-use.
* Used internally by the MessageQueue and Looper when disposing of queued Messages.
*/
void recycleUnchecked() {
// Mark the message as in use while it remains in the recycled object pool.
// Clear out all other details.
flags = FLAG_IN_USE;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
sendingUid = -1;
when = 0;
target = null;
callback = null;
data = null;
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
5. HandlerThread
由于Java中的Thread是沒有消息循環機制的,run()方法執行完,線程則結束。HandlerThread通過使用Looper實現了消息循環,只要不主動調用HandlerThread或Looper的quit()方法,循環就是一直走下去。
public class HandlerThread extends Thread {
int mPriority;
int mTid = -1;
Looper mLooper;
public HandlerThread(String name) {
super(name);
mPriority = Process.THREAD_PRIORITY_DEFAULT;
}
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
public Looper getLooper() {
if (!isAlive()) {
return null;
}
// If the thread has been started, wait until the looper has been created.
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
public boolean quit() {
Looper looper = getLooper();
if (looper != null) {
looper.quit();
return true;
}
return false;
}
}
總結
感謝閱讀,希望能幫助到大家,謝謝大家對本站的支持!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。