Android消息處理機(jī)制(Handler、Looper、MessageQueue與Message)

Android是消息驅(qū)動(dòng)的,實(shí)現(xiàn)消息驅(qū)動(dòng)有幾個(gè)要素:

1、Message:消息,其中包含了消息ID,消息處理對(duì)象以及處理的數(shù)據(jù)等,由MessageQueue統(tǒng)一列隊(duì),終由Handler處理

2、處理者,負(fù)責(zé)Message的發(fā)送及處理。使用Handler時(shí),需要實(shí)現(xiàn)handleMessage(Message

msg)方法來對(duì)特定的Message進(jìn)行處理,例如更新UI等。

3、MessageQueue:消息隊(duì)列,用來存放Handler發(fā)送過來的消息,并按照FIFO規(guī)則執(zhí)行。當(dāng)然,存放Message并非實(shí)際意義的保存,而是將Message以鏈表的方式串聯(lián)起來的,等待Looper的抽取。

4、Looper:消息泵,不斷地從MessageQueue中抽取Message執(zhí)行。因此,一個(gè)MessageQueue需要一個(gè)Looper。

5、Thread:線程,負(fù)責(zé)調(diào)度整個(gè)消息循環(huán),即消息循環(huán)的執(zhí)行場所。獲取loop

Looper.loop();

Public staticvoidloop(){

finalLooperme=myLooper();

}

//返回和線程相關(guān)的looper

Public staticLoopermyLooper(){

returnsThreadLocal.get();

}

Android系統(tǒng)的消息隊(duì)列和消息循環(huán)都是針對(duì)具體線程的,一個(gè)線程可以存在(當(dāng)然也可以不存在)一個(gè)消息隊(duì)列和一個(gè)消息循環(huán)(Looper),特定線程的消息只能分發(fā)給本線程,不能進(jìn)行跨線程,跨進(jìn)程通訊。但是創(chuàng)建的工作線程默認(rèn)是沒有消息循環(huán)和消息隊(duì)列的,如果想讓該

線程具有消息隊(duì)列和消息循環(huán),需要在線程中首先調(diào)用Looper.prepare()來創(chuàng)建消息隊(duì)列,然后調(diào)用Looper.loop()進(jìn)入消息循環(huán)。 如下例所示:

class LooperThread

extends Thread {

public Handler mHandler;

public void run() {

Looper.prepare();

mHandler = new Handler() {

public void handleMessage(Messagemsg) {

// process incoming messageshere

}

};

Looper.loop();

}

}

這樣你的線程就具有了消息處理機(jī)制了,在Handler中進(jìn)行消息處理。

Activity是一個(gè)UI線程,運(yùn)行于主線程中,Android系統(tǒng)在啟動(dòng)的時(shí)候會(huì)為Activity創(chuàng)建一個(gè)消息隊(duì)列和消息循環(huán)(Looper)。詳細(xì)實(shí)現(xiàn)請(qǐng)參考ActivityThread.java文件

Android應(yīng)用程序進(jìn)程在啟動(dòng)的時(shí)候,會(huì)在進(jìn)程中加載ActivityThread類,并且執(zhí)行這個(gè)類的main函數(shù),應(yīng)用程序的消息循環(huán)過程就是在這個(gè)main函數(shù)里面實(shí)現(xiàn)的

public final class

ActivityThread {

......

public

static final void main(String[] args) {

......

Looper.prepareMainLooper();

......

ActivityThread

thread = new ActivityThread();

thread.attach(false);

......

Looper.loop();

......

thread.detach();

......

}

}

這個(gè)函數(shù)做了兩件事情,一是在主線程中創(chuàng)建了一個(gè)ActivityThread實(shí)例,二是通過Looper類使主線程進(jìn)入消息循環(huán)中

初始化消息隊(duì)列

class LooperThread

extends Thread {

public Handler mHandler;

public void run() {

Looper.prepare();

mHandler = new Handler() {

public void handleMessage(Messagemsg) {

// process incoming messageshere

}

};

Looper.loop();

}

}

主要是紅色標(biāo)明的兩句,首先調(diào)用prepare初始化MessageQueue與Looper,然后調(diào)用loop進(jìn)入消息循環(huán)。先看一下Looper.prepare。

public static void

prepare() {

prepare(true);

}

private static void

prepare(boolean quitAllowed) {

if (sThreadLocal.get() != null) {

throw new RuntimeException("Onlyone Looper may be created per thread");

}

sThreadLocal.set(new Looper(quitAllowed));

}

重載函數(shù),quitAllowed默認(rèn)為true,從名字可以看出來就是消息循環(huán)是否可以退出,默認(rèn)是可退出的,Main線程(UI線程)初始化消息循環(huán)時(shí)會(huì)調(diào)用prepareMainLooper,傳進(jìn)去的是false。使用了ThreadLocal,每個(gè)線程可以初始化一個(gè)Looper。

private Looper(boolean quitAllowed) {

mQueue = new MessageQueue(quitAllowed);

mThread = Thread.currentThread();

}

MessageQueue(boolean quitAllowed) {

mQuitAllowed = quitAllowed;

mPtr = nativeInit();//mPtr記錄native消息隊(duì)列的信息

}

在Looper初始化時(shí),新建了一個(gè)MessageQueue的對(duì)象保存了在成員mQueue中。MessageQueue的構(gòu)造函數(shù)是包可見性,所以我們是無法直接使用的,在MessageQueue初始化的時(shí)候調(diào)用了nativeInit,這是一個(gè)Native方法:

android_os_MessageQueue_nativeInit();

static jlong

android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) {

NativeMessageQueue*nativeMessageQueue = new NativeMessageQueue();//初始化native消息隊(duì)列

if (!nativeMessageQueue) {

jniThrowRuntimeException(env,"Unable to allocate native queue");

return 0;

}

nativeMessageQueue->incStrong(env);

returnreinterpret_cast(nativeMessageQueue);

}

在nativeInit中,new了一個(gè)Native層的MessageQueue的對(duì)象,并將其地址保存在了Java層MessageQueue的成員mPtr中,Android中有好多這樣的實(shí)現(xiàn),一個(gè)類在Java層與Native層都有實(shí)現(xiàn),通過JNI的GetFieldID與SetIntField把Native層的類的實(shí)例地址保存到Java層類的實(shí)例的mPtr成員中,比如Parcel。

再看NativeMessageQueue的實(shí)現(xiàn):

new

NativeMessageQueue();

NativeMessageQueue::NativeMessageQueue()

: mPollEnv(NULL), mPollObj(NULL), mExceptionObj(NULL) {

mLooper =Looper::getForThread(); //獲取TLS中的Looper對(duì)象

if (mLooper == NULL) {

mLooper = newLooper(false); //創(chuàng)建native層的Looper

Looper::setForThread(mLooper); //保存native層的Looper到TLS中

}

}

Looper::getForThread(),功能類比于Java層的Looper.myLooper();

Looper::setForThread(mLooper),功能類比于Java層的ThreadLocal.set();

NativeMessageQueue::NativeMessageQueue()

: mInCallback(false), mExceptionObj(NULL) {

mLooper = Looper::getForThread();

if (mLooper == NULL) {

mLooper = new Looper(false);

Looper::setForThread(mLooper);

}

}

在NativeMessageQueue的構(gòu)造函數(shù)中獲得了一個(gè)Native層的Looper對(duì)象,Native層的Looper也使用了線程本地存儲(chǔ)

發(fā)送消息

通過Looper.prepare初始化好消息隊(duì)列后就可以調(diào)用Looper.loop進(jìn)入消息循環(huán)了,然后我們就可以向消息隊(duì)列發(fā)送消息,消息循環(huán)就會(huì)取出消息進(jìn)行處理,在看消息處理之前,先看一下消息是怎么被添加到消息隊(duì)列的。

在Java層,Message類表示一個(gè)消息對(duì)象,要發(fā)送消息首先就要先獲得一個(gè)消息對(duì)象,Message類的構(gòu)造函數(shù)是public的,但是不建議直接new Message,Message內(nèi)部保存了一個(gè)緩存的消息池,我們可以用obtain從緩存池獲得一個(gè)消息,Message使用完后系統(tǒng)會(huì)調(diào)用recycle回收,如果自己new很多Message,每次使用完后系統(tǒng)放入緩存池,會(huì)占用很多內(nèi)存的,如下所示:

public static

Message obtain() {

synchronized (sPoolSync) {

if (sPool != null) {

Message m = sPool;

sPool = m.next;

m.next = null;

sPoolSize--;

return m;

}

}

return new Message();

}

public void recycle() {

clearForRecycle();

synchronized (sPoolSync) {

if (sPoolSize < MAX_POOL_SIZE) {

next = sPool;

sPool = this;

sPoolSize++;

}

}

}

Message內(nèi)部通過next成員實(shí)現(xiàn)了一個(gè)鏈表,這樣sPool就了為了一個(gè)Messages的緩存鏈表。

消息對(duì)象獲取到了怎么發(fā)送呢,大家都知道是通過Handler的post、sendMessage等方法,其實(shí)這些方法最終都是調(diào)用的同一個(gè)方法sendMessageAtTime:

public boolean

sendMessageAtTime(Message msg, long uptimeMillis) {

MessageQueue queue = mQueue;

if (queue == null) {

RuntimeException e = newRuntimeException(

this + "sendMessageAtTime() called with no mQueue");

Log.w("Looper",e.getMessage(), e);

return false;

}

return enqueueMessage(queue, msg,uptimeMillis);

}

sendMessageAtTime獲取到消息隊(duì)列然后調(diào)用enqueueMessage方法,消息隊(duì)列mQueue是從與Handler關(guān)聯(lián)的Looper獲得的。

private boolean

enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {

msg.target = this;

if (mAsynchronous) {

msg.setAsynchronous(true);

}

return queue.enqueueMessage(msg,uptimeMillis);

}

enqueueMessage

接下來看一下是怎么MessageQueue的enqueueMessage。

final boolean

enqueueMessage(Message msg, long when) {

if (msg.isInUse()) {

throw newAndroidRuntimeException(msg + " This message is already in use.");

}

if (msg.target == null) {

throw newAndroidRuntimeException("Message must have a target.");

}

boolean needWake;

synchronized (this) {

if (mQuiting) {

RuntimeException e = newRuntimeException(

msg.target + "sending message to a Handler on a dead thread");

Log.w("MessageQueue",e.getMessage(), e);

return false;

}

msg.when = when;

Message p = mMessages;

if (p == null || when == 0 || when< p.when) {

// New head, wake up the eventqueue if blocked.

msg.next = p;

mMessages = msg;

needWake = mBlocked;

} else {

// Inserted within the middleof the queue.Usually we don't have towake

// up the event queue unlessthere is a barrier at the head of the queue

// and the message is theearliest asynchronous message in the queue.

needWake = mBlocked &&p.target == null && msg.isAsynchronous();

Message prev;

for (;;) {

prev = p;

p = p.next;

if (p == null || when

break;

}

if (needWake &&p.isAsynchronous()) {

needWake = false;

}

}

msg.next = p; // invariant: p== prev.next

prev.next = msg;

}

}

if (needWake) {

nativeWake(mPtr);

}

return true;

}

在enqueueMessage中首先判斷,如果當(dāng)前的消息隊(duì)列為空,或者新添加的消息的執(zhí)行時(shí)間when是0,或者新添加的消息的執(zhí)行時(shí)間比消息隊(duì)列頭的消息的執(zhí)行時(shí)間還早,就把消息添加到消息隊(duì)列頭(消息隊(duì)列按時(shí)間排序),否則就要找到合適的位置將當(dāng)前消息添加到消息隊(duì)列。

消息循環(huán)

消息隊(duì)列初始化好了,也知道怎么發(fā)消息了,下面就是怎么處理消息了,看Handler.loop函數(shù):

public static void

loop() {

final Looper me =myLooper();//從該線程中取出對(duì)應(yīng)的looper對(duì)象

if (me == null) {

throw new RuntimeException("NoLooper; Looper.prepare() wasn't called on this thread.");

}

final MessageQueue queue = me.mQueue;//取消息隊(duì)列對(duì)象...

// Make sure the identity of thisthread is that of the local process,

// and keep track of what that identitytoken actually is.

Binder.clearCallingIdentity();

final long ident =Binder.clearCallingIdentity();

for (;;) {

Message msg = queue.next();//?might?block取消息隊(duì)列中的一個(gè)待處理消息..

if (msg == null) {

// No message indicates thatthe message queue is quitting.

return;

}

// This must be in a localvariable, 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 courseof dispatching the

// identity of the thread wasn'tcorrupted.

final long newIdent =Binder.clearCallingIdentity();

if (ident != newIdent) {

Log.wtf(TAG, "Threadidentity changed from 0x"

+Long.toHexString(ident) + " to 0x"

+Long.toHexString(newIdent) + " while dispatching to "

+msg.target.getClass().getName() + " "

+ msg.callback + "what=" + msg.what);

}

msg.recycle();

}

}

loop每次從MessageQueue取出一個(gè)Message,調(diào)用msg.target.dispatchMessage(msg),target就是發(fā)送message時(shí)跟message關(guān)聯(lián)的handler,這樣就調(diào)用到了熟悉的dispatchMessage,Message被處理后會(huì)被recycle。當(dāng)queue.next返回null時(shí)會(huì)退出消息循環(huán),接下來就看一下MessageQueue.next是怎么取出消息的,又會(huì)在什么時(shí)候返回null。

final Message next()

{

int pendingIdleHandlerCount = -1; // -1only during first iteration

int nextPollTimeoutMillis = 0;

for (;;) {

if (nextPollTimeoutMillis != 0) {

Binder.flushPendingCommands();

}

nativePollOnce(mPtr,nextPollTimeoutMillis);

synchronized (this) {

if (mQuiting) {

return null;

}

// Try to retrieve the nextmessage.Return if found.

final long now =SystemClock.uptimeMillis();

Message prevMsg = null;

Message msg = mMessages;

if (msg != null &&msg.target == null) {

// Stalled by abarrier.Find the next asynchronousmessage in the queue.

do {

prevMsg = msg;

msg = msg.next;

} while (msg != null&& !msg.isAsynchronous());

}

if (msg != null) {

if (now < msg.when) {

// Next message is notready.Set a timeout to wake up when itis 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);

msg.markInUse();

return msg;

}

} else {

// No more messages.

nextPollTimeoutMillis = -1;

}

// If first time idle, then getthe number of idlers to run.

// Idle handles only run if thequeue is empty or if the first message

// in the queue (possibly abarrier) 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 torun.Loop and wait some more.

mBlocked = true;

continue;

}

if (mPendingIdleHandlers ==null) {

mPendingIdleHandlers = newIdleHandler[Math.max(pendingIdleHandlerCount, 4)];

}

mPendingIdleHandlers =mIdleHandlers.toArray(mPendingIdleHandlers);

}

// Run the idle handlers.

// We only ever reach this codeblock during the first iteration.

for (int i = 0; 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 threwexception", t);

}

if (!keep) {

synchronized (this) {

mIdleHandlers.remove(idler);

}

}

}

// Reset the idle handler count to0 so we do not run them again.

pendingIdleHandlerCount = 0;

// While calling an idle handler, anew message could have been delivered

// so go back and look again for apending message without waiting.

nextPollTimeoutMillis = 0;

}

}

MessageQueue.next首先會(huì)調(diào)用nativePollOnce,然后如果mQuiting為true就返回null,Looper就會(huì)退出消息循環(huán)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容