Android消息循環(huán)機(jī)制源碼深入理解
Android消息循環(huán)機(jī)制源碼
前言:
搞Android的不懂Handler消息循環(huán)機(jī)制,都不好意思說(shuō)自己是Android工程師。面試的時(shí)候一般也都會(huì)問(wèn)這個(gè)知識(shí)點(diǎn),但是我相信大多數(shù)碼農(nóng)肯定是沒(méi)有看過(guò)相關(guān)源碼的,頂多也就是網(wǎng)上搜搜,看看別人的文章介紹。學(xué)姐不想把那個(gè)萬(wàn)能的關(guān)系圖拿出來(lái)討論。
近來(lái)找了一些關(guān)于android線(xiàn)程間通信的資料,整理學(xué)習(xí)了一下,并制作了一個(gè)簡(jiǎn)單的例子。
andriod提供了 Handler 和 Looper 來(lái)滿(mǎn)足線(xiàn)程間的通信。例如一個(gè)子線(xiàn)程從網(wǎng)絡(luò)上下載了一副圖片,當(dāng)它下載完成后會(huì)發(fā)送消息給主線(xiàn)程,這個(gè)消息是通過(guò)綁定在主線(xiàn)程的Handler來(lái)傳遞的。
在Android,這里的線(xiàn)程分為有消息循環(huán)的線(xiàn)程和沒(méi)有消息循環(huán)的線(xiàn)程,有消息循環(huán)的線(xiàn)程一般都會(huì)有一個(gè)Looper,這個(gè)事android的新 概念。我們的主線(xiàn)程(UI線(xiàn)程)就是一個(gè)消息循環(huán)的線(xiàn)程。針對(duì)這種消息循環(huán)的機(jī)制,我們引入一個(gè)新的機(jī)制Handle,我們有消息循環(huán),就要往消息循環(huán)里 面發(fā)送相應(yīng)的消息,自定義消息一般都會(huì)有自己對(duì)應(yīng)的處理,消息的發(fā)送和清除,消息的的處理,把這些都封裝在Handle里面,注意Handle只是針對(duì)那 些有Looper的線(xiàn)程,不管是UI線(xiàn)程還是子線(xiàn)程,只要你有Looper,我就可以往你的消息隊(duì)列里面添加?xùn)|西,并做相應(yīng)的處理。
但是這里還有一點(diǎn),就是只要是關(guān)于UI相關(guān)的東西,就不能放在子線(xiàn)程中,因?yàn)樽泳€(xiàn)程是不能操作UI的,只能進(jìn)行數(shù)據(jù)、系統(tǒng)等其他非UI的操作。
在Android,這里的線(xiàn)程分為有消息循環(huán)的線(xiàn)程和沒(méi)有消息循環(huán)的線(xiàn)程,有消息循環(huán)的線(xiàn)程一般都會(huì)有一個(gè)Looper,這個(gè)是android的新概念。我們的主線(xiàn)程(UI線(xiàn)程)就是一個(gè)消息循環(huán)的線(xiàn)程。針對(duì)這種消息循環(huán)的機(jī)制,我們引入一個(gè)新的機(jī)制Handler,我們有消息循環(huán),就要往消息循環(huán)里面發(fā)送相應(yīng)的消息,自定義消息一般都會(huì)有自己對(duì)應(yīng)的處理,消息的發(fā)送和清除,把這些都封裝在Handler里面,注意Handler只是針對(duì)那 些有Looper的線(xiàn)程,不管是UI線(xiàn)程還是子線(xiàn)程,只要你有Looper,我就可以往你的消息隊(duì)列里面添加?xùn)|西,并做相應(yīng)的處理。
但是這里還有一點(diǎn),就是只要是關(guān)于UI相關(guān)的東西,就不能放在子線(xiàn)程中,因?yàn)樽泳€(xiàn)程是不能操作UI的,只能進(jìn)行數(shù)據(jù)、系統(tǒng)等其他非UI的操作。
先從我們平時(shí)的使用方法引出這個(gè)機(jī)制,再結(jié)合源碼進(jìn)行分析。
我們平時(shí)使用是這樣的:
//1. 主線(xiàn)程
Handler handler = new MyHandler();
//2. 非主線(xiàn)程
HandlerThread handlerThread = new HandlerThread("handlerThread");
handlerThread.start();
Handler handler = new Handler(handlerThread.getLooper());
//發(fā)送消息
handler.sendMessage(msg);
//接收消息
static class MyHandler extends Handler {
//對(duì)于非主線(xiàn)程處理消息需要傳Looper,主線(xiàn)程有默認(rèn)的sMainLooper
public MyHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
}
那么為什么初始化的時(shí)候,我們執(zhí)行了1或2,后面只需要sendMessage就可處理任務(wù)了呢?學(xué)姐這里以非主線(xiàn)程為例進(jìn)行介紹,handlerThread.start()的時(shí)候,實(shí)際上創(chuàng)建了一個(gè)用于消息循環(huán)的Looper和消息隊(duì)列MessageQueue,同時(shí)啟動(dòng)了消息循環(huán),并將這個(gè)循環(huán)傳給Handler,這個(gè)循環(huán)會(huì)從MessageQueue中依次取任務(wù)出來(lái)執(zhí)行。用戶(hù)若要執(zhí)行某項(xiàng)任務(wù),只需要調(diào)用handler.sendMessage即可,這里做的事情是將消息添加到MessaeQueue中。對(duì)于主線(xiàn)程也類(lèi)似,只是主線(xiàn)程sMainThread和sMainLooper不需要我們主動(dòng)去創(chuàng)建,程序啟動(dòng)的時(shí)候Application就創(chuàng)建好了,我們只需要?jiǎng)?chuàng)建Handler即可。
我們這里提到了幾個(gè)概念:
- HandlerThread 支持消息循環(huán)的線(xiàn)程
- Handler 消息處理器
- Looper 消息循環(huán)對(duì)象
- MessageQueue 消息隊(duì)列
- Message 消息體
對(duì)應(yīng)關(guān)系是:一對(duì)多,即(一個(gè))HandlerThread、Looper、MessageQueue -> (多個(gè))Handler、Message
源碼解析
1. Looper
(1)創(chuàng)建消息循環(huán)
prepare()用于創(chuàng)建Looper消息循環(huán)對(duì)象。Looper對(duì)象通過(guò)一個(gè)成員變量ThreadLocal進(jìn)行保存。
(2)獲取消息循環(huán)對(duì)象
myLooper()用于獲取當(dāng)前消息循環(huán)對(duì)象。Looper對(duì)象從成員變量ThreadLocal中獲取。
(3)開(kāi)始消息循環(huán)
loop()開(kāi)始消息循環(huán)。循環(huán)過(guò)程如下:
每次從消息隊(duì)列MessageQueue中取出一個(gè)Message
使用Message對(duì)應(yīng)的Handler處理Message
已處理的Message加到本地消息池,循環(huán)復(fù)用
循環(huán)以上步驟,若沒(méi)有消息表明消息隊(duì)列停止,退出循環(huán)
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)發(fā)送消息
Handler支持2種消息類(lèi)型,即Runnable和Message。因此發(fā)送消息提供了post(Runnable r)和sendMessage(Message msg)兩個(gè)方法。從下面源碼可以看出Runnable賦值給了Message的callback,最終也是封裝成Message對(duì)象對(duì)象。學(xué)姐個(gè)人認(rèn)為外部調(diào)用不統(tǒng)一使用Message,應(yīng)該是兼容Java的線(xiàn)程任務(wù),學(xué)姐認(rèn)為這種思想也可以借鑒到平常開(kāi)發(fā)過(guò)程中。發(fā)送的消息都會(huì)入隊(duì)到MessageQueue隊(duì)列中。
(2)處理消息
Looper循環(huán)過(guò)程的時(shí)候,是通過(guò)dispatchMessage(Message msg)對(duì)消息進(jìn)行處理。處理過(guò)程:先看是否是Runnable對(duì)象,如果是則調(diào)用handleCallback(msg)進(jìn)行處理,最終調(diào)到Runnable.run()方法執(zhí)行線(xiàn)程;如果不是Runnable對(duì)象,再看外部是否傳入了Callback處理機(jī)制,若有則使用外部Callback進(jìn)行處理;若既不是Runnable對(duì)象也沒(méi)有外部Callback,則調(diào)用handleMessage(msg),這個(gè)也是我們開(kāi)發(fā)過(guò)程中最常覆寫(xiě)的方法了。
(3)移除消息
removeCallbacksAndMessages(),移除消息其實(shí)也是從MessageQueue中將Message對(duì)象移除掉。
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)消息入隊(duì)
消息入隊(duì)方法enqueueMessage(Message msg, long when)。其處理過(guò)程如下:
待入隊(duì)的Message標(biāo)記為InUse,when賦值
若消息鏈表mMessages為空為空,或待入隊(duì)Message執(zhí)行時(shí)間小于mMessage鏈表頭,則待入隊(duì)Message添加到鏈表頭
若不符合以上條件,則輪詢(xún)鏈表,根據(jù)when從低到高的順序,插入鏈表合適位置
(2)消息輪詢(xún)
next()依次從MessageQueue中取出Message
(3)移除消息
removeMessages()可以移除消息,做的事情實(shí)際上就是將消息從鏈表移除,同時(shí)將移除的消息添加到消息池,提供循環(huán)復(fù)用。
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)消息創(chuàng)建
Message.obtain()創(chuàng)建消息。若消息池鏈表sPool不為空,則從sPool中獲取第一個(gè),flags標(biāo)記為UnInUse,同時(shí)從sPool中移除,sPoolSize減1;若消息池鏈表sPool為空,則new Message()
(2)消息釋放
recycle()將消息釋放,從內(nèi)部實(shí)現(xiàn)recycleUnchecked()可知,將flags標(biāo)記為InUse,其他各種狀態(tài)清零,同時(shí)將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是沒(méi)有消息循環(huán)機(jī)制的,run()方法執(zhí)行完,線(xiàn)程則結(jié)束。HandlerThread通過(guò)使用Looper實(shí)現(xiàn)了消息循環(huán),只要不主動(dòng)調(diào)用HandlerThread或Looper的quit()方法,循環(huán)就是一直走下去。
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;
}
}
總結(jié)
- 關(guān)鍵類(lèi):HandlerThread、Handler、Looper、MessageQueue、Messaga
- MessageQueue數(shù)據(jù)結(jié)構(gòu),鏈表。
感謝閱讀,希望能幫助到大家,謝謝大家對(duì)本站的支持!
相關(guān)文章
Android LuBan與Compressor圖片壓縮方式
本篇文章主要介紹了Android LuBan與Compressor圖片壓縮方式,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-04-04
Android Studio控制臺(tái)出現(xiàn)中文亂碼(方框)問(wèn)題解決辦法
這篇文章主要介紹了Android Studio控制臺(tái)出現(xiàn)中文亂碼(方框)問(wèn)題解決辦法的相關(guān)資料,需要的朋友可以參考下2017-06-06
android監(jiān)控sim卡有沒(méi)有服務(wù)示例(sim卡管理)
android監(jiān)聽(tīng)SIM卡有沒(méi)有服務(wù),可以使用android.telephony.PhoneStateListener類(lèi)來(lái)實(shí)現(xiàn),下面是一個(gè)簡(jiǎn)單的小例子,大家參考使用吧2014-01-01
Android中LinearLayout布局的常用屬性總結(jié)
這篇文章主要介紹了Android中LinearLayout布局的常用屬性總結(jié),包括居中、重心、比例等線(xiàn)性布局中的基本設(shè)置,需要的朋友可以參考下2016-04-04
Android Studio導(dǎo)入Eclipse項(xiàng)目時(shí).so庫(kù)文件的解決方法
這篇文章主要介紹了Android Studio導(dǎo)入Eclipse項(xiàng)目時(shí)項(xiàng)目中".so"庫(kù)文件的解決方法,需要的朋友可以參考下2018-06-06
Android自定義控件實(shí)現(xiàn)萬(wàn)能的對(duì)話(huà)框
這篇文章主要為大家詳細(xì)介紹了Android自定義控件實(shí)現(xiàn)萬(wàn)能對(duì)話(huà)框的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-03-03
Android實(shí)現(xiàn)可播放GIF動(dòng)畫(huà)的ImageView
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)可播放GIF動(dòng)畫(huà)的ImageView,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-09-09

