引子
Android應(yīng)用程序是通過消息來驅(qū)動的铺遂,即在應(yīng)用程序的主線程(UI線程)中有一個消息循環(huán),負責(zé)處理消息隊列中的消息疚鲤。Android應(yīng)用程序是支持多線程的啸如,即可以創(chuàng)建子線程來執(zhí)行一些計算型的任務(wù)。
- 這些子線程能不能像應(yīng)用程序的主線程一樣具有消息循環(huán)呢成翩?
- 這些子線程又能不能往應(yīng)用程序的主線程中發(fā)送消息呢觅捆?
一、原理
在開發(fā)Android應(yīng)用程序中麻敌,有時候我們需要在應(yīng)用程序中創(chuàng)建一些常駐的子線程來不定期地執(zhí)行一些不需要與應(yīng)用程序界面交互的計算型的任務(wù)栅炒。如果這些子線程具有消息循環(huán),那么它們就能夠常駐在應(yīng)用程序中不定期的執(zhí)行一些計算型任務(wù)了:當(dāng)我們需要用這些子線程來執(zhí)行任務(wù)時,就往這個子線程的消息隊列中發(fā)送一個消息赢赊,然后就可以在子線程的消息循環(huán)中執(zhí)行我們的計算型任務(wù)了乙漓。
但是,Android應(yīng)用程序的子線程是不可以操作主線程的UI的域携,那么簇秒,這個子線程應(yīng)該如何在應(yīng)用程序界面上顯示信息呢?如果我們能夠在子線程中往主線程的消息隊列中發(fā)送消息秀鞭,那么問題就迎刃而解了趋观,因為發(fā)往主線程消息隊列的消息最終是由主線程來處理的,在處理這個消息的時候锋边,我們就可以在應(yīng)用程序界面上顯示信息了皱坛。
二、消息循環(huán)模型
2.1 應(yīng)用程序主線程消息循環(huán)模型
當(dāng)運行在Android應(yīng)用程序框架層中的ActivityManagerService決定要為當(dāng)前啟動的應(yīng)用程序創(chuàng)建一個主線程的時候豆巨,它會在ActivityManagerService中的startProcessLocked成員函數(shù)調(diào)用Process類的靜態(tài)成員函數(shù)start為當(dāng)前應(yīng)用程序創(chuàng)建一個主線程:
public final class ActivityManagerService extends ActivityManagerNative
implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
......
private final void startProcessLocked(ProcessRecord app,
String hostingType, String hostingNameStr) {
......
try {
int uid = app.info.uid;
int[] gids = null;
try {
gids = mContext.getPackageManager().getPackageGids(
app.info.packageName);
} catch (PackageManager.NameNotFoundException e) {
......
}
......
int debugFlags = 0;
......
int pid = Process.start("android.app.ActivityThread",
mSimpleProcessManagement ? app.processName : null, uid, uid,
gids, debugFlags, null);
......
} catch (RuntimeException e) {
......
}
}
......
}
Process.start函數(shù)的第一個參數(shù)“android.app.ActivityThread”剩辟,它表示要在當(dāng)前新建的線程中加載android.app.ActivityThread類,并且調(diào)用這個類的靜態(tài)成員函數(shù)main作為應(yīng)用程序的入口點往扔。ActivityThread類定義在frameworks/base/core/java/android/app/ActivityThread.java文件中:
public final class ActivityThread {
......
public static final void main(String[] args) {
......
Looper.prepareMainLooper();
......
ActivityThread thread = new ActivityThread();
thread.attach(false);
......
Looper.loop();
......
thread.detach();
......
}
......
}
在這個main函數(shù)里面贩猎,除了創(chuàng)建一個ActivityThread實例外,就是在進行消息循環(huán)了萍膛。
在進行消息循環(huán)之前吭服,首先會通過Looper類的靜態(tài)成員函數(shù)prepareMainLooper為當(dāng)前線程準(zhǔn)備一個消息循環(huán)對象。Looper類定義在frameworks/base/core/java/android/os/Looper.java文件中:
public class Looper {
......
private static final ThreadLocal sThreadLocal = new ThreadLocal();
final MessageQueue mQueue;
......
/** Initialize the current thread as a looper.
* This gives you a chance to create handlers that then reference
* this looper, before actually starting the loop. Be sure to call
* {@link #loop()} after calling this method, and end it by calling
* {@link #quit()}.
*/
public static final void prepare() {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper());
}
/** Initialize the current thread as a looper, marking it as an application's main
* looper. The main looper for your application is created by the Android environment,
* so you should never need to call this function yourself.
* {@link #prepare()}
*/
public static final void prepareMainLooper() {
prepare();
setMainLooper(myLooper());
if (Process.supportsProcesses()) {
myLooper().mQueue.mQuitAllowed = false;
}
}
private synchronized static void setMainLooper(Looper looper) {
mMainLooper = looper;
}
/**
* Return the Looper object associated with the current thread. Returns
* null if the calling thread is not associated with a Looper.
*/
public static final Looper myLooper() {
return (Looper)sThreadLocal.get();
}
private Looper() {
mQueue = new MessageQueue();
mRun = true;
mThread = Thread.currentThread();
}
......
}
Looper類的靜態(tài)成員函數(shù)prepareMainLooper是專門應(yīng)用程序的主線程調(diào)用的蝗罗,應(yīng)用程序的其它子線程都不應(yīng)該調(diào)用這個函數(shù)來在本線程中創(chuàng)建消息循環(huán)對象艇棕,而應(yīng)該調(diào)用prepare函數(shù)來在本線程中創(chuàng)建消息循環(huán)對象。
函數(shù)prepareMainLooper做的事情其實就是在線程中創(chuàng)建一個Looper對象串塑,這個Looper對象是存放在sThreadLocal成員變量里面的沼琉,成員變量sThreadLocal的類型為ThreadLocal,表示這是一個線程局部變量桩匪,即保證每一個調(diào)用了prepareMainLooper函數(shù)的線程里面都有一個獨立的Looper對象打瘪。在線程是創(chuàng)建Looper對象的工作是由prepare函數(shù)來完成的,而在創(chuàng)建Looper對象的時候吸祟,會同時創(chuàng)建一個消息隊列MessageQueue瑟慈,保存在Looper的成員變量mQueue中,后續(xù)消息就是存放在這個隊列中去屋匕。MessageQueue的具體定義和實現(xiàn)是通過JNI方法完成的济锄。
為應(yīng)用程序的主線程專門準(zhǔn)備一個創(chuàng)建消息循環(huán)對象的函數(shù)捻浦,是為了讓其它地方能夠方便地通過Looper類的getMainLooper函數(shù)來獲得應(yīng)用程序主線程中的消息循環(huán)對象。獲得應(yīng)用程序主線程中的消息循環(huán)對象是為了能夠向應(yīng)用程序主線程發(fā)送消息了。
消息循環(huán)對象創(chuàng)建好之后,回到ActivityThread類的main函數(shù)中,接下來,就是要進入消息循環(huán)了:
Looper.loop();
2.2 應(yīng)用程序子線程消息循環(huán)模型
在Java框架中,如果我們想在當(dāng)前應(yīng)用程序中創(chuàng)建一個子線程绞惦,一般就是通過自己實現(xiàn)一個類,這個類繼承于Thread類洋措,然后重載Thread類的run函數(shù)济蝉,把我們想要在這個子線程執(zhí)行的任務(wù)都放在這個run函數(shù)里面實現(xiàn)。最后實例這個自定義的類菠发,并且調(diào)用它的start函數(shù)王滤,這樣一個子線程就創(chuàng)建好了,并且會調(diào)用這個自定義類的run函數(shù)滓鸠。但是當(dāng)這個run函數(shù)執(zhí)行完成后雁乡,子線程也就結(jié)束了,它沒有消息循環(huán)的概念糜俗。
不過有時候我們需要在應(yīng)用程序中創(chuàng)建一些常駐的子線程來不定期地執(zhí)行一些計算型任務(wù)踱稍,這時候就可以考慮使用Android系統(tǒng)提供的HandlerThread類了,它具有創(chuàng)建具有消息循環(huán)功能的子線程的作用悠抹。
HandlerThread類實現(xiàn)在frameworks/base/core/java/android/os/HandlerThread.java文件中珠月。
public class HandlerThread extends Thread {
......
private Looper mLooper;
public HandlerThread(String name) {
super(name);
......
}
......
public void run() {
......
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
......
}
......
Looper.loop();
......
}
public Looper getLooper() {
......
return mLooper;
}
......
}
HandlerThread類繼承了Thread類,因此楔敌,通過它可以在應(yīng)用程序中創(chuàng)建一個子線程桥温。在它的run函數(shù)中,會進入一個消息循環(huán)中梁丘,因此,這個子線程可以常駐在應(yīng)用程序中旺韭,直到它接收收到一個退出消息為止氛谜。
在run函數(shù)中,首先是調(diào)用Looper類的靜態(tài)成員函數(shù)prepare來準(zhǔn)備一個消息循環(huán)對象:
Looper.prepare();
然后通過Looper類的myLooper成員函數(shù)將這個子線程中的消息循環(huán)對象保存在HandlerThread類中的成員變量mLooper中:
mLooper = Looper.myLooper();
這樣区端,其它地方就可以方便地通過它的getLooper函數(shù)來獲得這個消息循環(huán)對象了值漫,有了這個消息循環(huán)對象后,就可以往這個子線程的消息隊列中發(fā)送消息织盼,通知這個子線程執(zhí)行特定的任務(wù)了杨何。
最在這個run函數(shù)通過Looper類的loop函數(shù)進入消息循環(huán)中:
Looper.loop();
這樣,一個具有消息循環(huán)的應(yīng)用程序子線程就準(zhǔn)備就緒了沥邻。
三危虱、應(yīng)用程序消息處理機制(Looper、Handler)
Android應(yīng)用程序通過消息來驅(qū)動唐全,系統(tǒng)為每一個應(yīng)用程序維護一個消息隊例埃跷,應(yīng)用程序的主線程不斷地從這個消息隊例中獲取消息(Looper)蕊玷,然后對這些消息進行處理(Handler),這樣就實現(xiàn)了通過消息來驅(qū)動應(yīng)用程序的執(zhí)行弥雹。
3.1 消息循環(huán)
在消息處理機制中垃帅,消息都是存放在一個消息隊列中去,而應(yīng)用程序的主線程就是圍繞這個消息隊列進入一個無限循環(huán)的剪勿,直到應(yīng)用程序退出贸诚。如果隊列中有消息,應(yīng)用程序的主線程就會把它取出來厕吉,并分發(fā)給相應(yīng)的Handler進行處理酱固;如果隊列中沒有消息,應(yīng)用程序的主線程就會進入空閑等待狀態(tài)赴涵,等待下一個消息的到來媒怯。在Android應(yīng)用程序中,這個消息循環(huán)過程是由Looper類來實現(xiàn)的髓窜,它定義在frameworks/base/core/java/android/os/Looper.java文件中扇苞,上文已經(jīng)介紹過。
在上面這些工作都準(zhǔn)備好之后寄纵,就調(diào)用Looper類的loop函數(shù)進入到消息循環(huán)中去了:
public class Looper {
......
public static final void loop() {
Looper me = myLooper();
MessageQueue queue = me.mQueue;
......
while (true) {
Message msg = queue.next(); // might block
......
if (msg != null) {
if (msg.target == null) {
// No target is a magic identifier for the quit message.
return;
}
......
msg.target.dispatchMessage(msg);
......
msg.recycle();
}
}
}
......
}
這里就是進入到消息循環(huán)中去了鳖敷,它不斷地從消息隊列mQueue中去獲取下一個要處理的消息msg,如果消息的target成員變量為null程拭,就表示要退出消息循環(huán)了定踱,否則的話就要調(diào)用這個target對象的dispatchMessage成員函數(shù)來處理這個消息,這個target對象的類型為Handler恃鞋,下面我們分析消息的發(fā)送時會看到這個消息對象msg是如設(shè)置的崖媚。
這個函數(shù)最關(guān)鍵的地方便是從消息隊列中獲取下一個要處理的消息了,即MessageQueue.next函數(shù)恤浪,它實現(xiàn)在frameworks/base/core/java/android/os/MessageQueue.java文件中:
public class MessageQueue {
......
final Message next() {
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(mPtr, nextPollTimeoutMillis);
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
final Message msg = mMessages;
if (msg != null) {
final long when = msg.when;
if (now >= when) {
mBlocked = false;
mMessages = msg.next;
msg.next = null;
if (Config.LOGV) Log.v("MessageQueue", "Returning message: " + msg);
return msg;
} else {
nextPollTimeoutMillis = (int) Math.min(when - now, Integer.MAX_VALUE);
}
} else {
nextPollTimeoutMillis = -1;
}
// If first time, then get the number of idlers to run.
if (pendingIdleHandlerCount < 0) {
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;
}
}
......
}
調(diào)用這個函數(shù)的時候畅哑,有可能會讓線程進入等待狀態(tài)。什么情況下水由,線程會進入等待狀態(tài)呢荠呐?兩種情況:
- 一是當(dāng)消息隊列中沒有消息時,它會使線程進入等待狀態(tài)砂客;
- 二是消息隊列中有消息泥张,但是消息指定了執(zhí)行的時間,而現(xiàn)在還沒有到這個時間鞠值,線程也會進入等待狀態(tài)媚创。
消息隊列中的消息是按時間先后來排序的,后面我們在分析消息的發(fā)送時會看到彤恶。
這里的等待筝野,是空閑等待晌姚,而不是忙等待,在進入空閑等待狀態(tài)前歇竟,如果應(yīng)用程序注冊了IdleHandler接口來處理一些事情挥唠,那么就會先執(zhí)行這里IdleHandler,然后再進入等待狀態(tài)焕议。IdlerHandler是定義在MessageQueue的一個內(nèi)部類:
public class MessageQueue {
......
/**
* Callback interface for discovering when a thread is going to block
* waiting for more messages.
*/
public static interface IdleHandler {
/**
* Called when the message queue has run out of messages and will now
* wait for more. Return true to keep your idle handler active, false
* to have it removed. This may be called if there are still messages
* pending in the queue, but they are all scheduled to be dispatched
* after the current time.
*/
boolean queueIdle();
}
......
}
它只有一個成員函數(shù)queueIdle宝磨,執(zhí)行這個函數(shù)時,如果返回值為false盅安,那么就會從應(yīng)用程序中移除這個IdleHandler唤锉,否則的話就會在應(yīng)用程序中繼續(xù)維護著這個IdleHandler,下次空閑時仍會再執(zhí)會這個IdleHandler别瞭。MessageQueue提供了addIdleHandler和removeIdleHandler兩注冊和刪除IdleHandler窿祥。
3.2 消息的發(fā)送
應(yīng)用程序的主線程準(zhǔn)備好消息隊列并且進入到消息循環(huán)后,其它地方就可以往這個消息隊列中發(fā)送消息了蝙寨。
代碼中需要將消息封裝成Message對象msg晒衩,然后通過Handler類的基類實現(xiàn)的sendMessage函數(shù)把這個消息對象msg加入到應(yīng)用程序的消息隊列中去。自定義的基類繼承于Handler類墙歪,安卓源碼中的樣例如下:
public final class ActivityThread {
......
private final class H extends Handler {
......
public void handleMessage(Message msg) {
......
switch (msg.what) {
......
}
......
}
......
}
這個H類就是通過其成員函數(shù)handleMessage函數(shù)來處理消息的听系。H類繼承于Handler類,當(dāng)創(chuàng)建這個H對象時虹菲,會調(diào)用Handler類的構(gòu)造函數(shù)靠胜,這個函數(shù)定義在frameworks/base/core/java/android/os/Handler.java文件中:
public class Handler {
......
public Handler() {
......
mLooper = Looper.myLooper();
......
mQueue = mLooper.mQueue;
......
}
final MessageQueue mQueue;
final Looper mLooper;
......
}
在Hanlder類的構(gòu)造函數(shù)中,主要就是初始成員變量mLooper和mQueue了毕源。這里的myLooper是Looper類的靜態(tài)成員函數(shù)浪漠,通過它來獲得一個Looper對象,這個Looper對象就是前面我們在分析消息循環(huán)時霎褐,在ActivityThread類的main函數(shù)中通過Looper.prepareMainLooper函數(shù)創(chuàng)建的郑藏。Looper.myLooper函數(shù)實現(xiàn)在frameworks/base/core/java/android/os/Looper.java文件中:
public class Looper {
......
public static final Looper myLooper() {
return (Looper)sThreadLocal.get();
}
......
}
有了這個Looper對象后,就可以通過Looper.mQueue來訪問應(yīng)用程序的消息隊列了瘩欺。
有了這個Handler對象H后,就可以通過它來往應(yīng)用程序的消息隊列中加入新的消息了拌牲。就開始調(diào)用H.sendMessage函數(shù)來發(fā)送消息了俱饿,這個函數(shù)定義在frameworks/base/core/java/android/os/Handler.java文件中:
public class Handler {
......
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
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)
{
boolean sent = false;
MessageQueue queue = mQueue;
if (queue != null) {
msg.target = this;
sent = queue.enqueueMessage(msg, uptimeMillis);
}
else {
......
}
return sent;
}
......
}
在發(fā)送消息時,是可以指定消息的處理時間的塌忽,但是通過sendMessage函數(shù)發(fā)送的消息的處理時間默認就為當(dāng)前時間拍埠,即表示要馬上處理,因此土居,從sendMessage函數(shù)中調(diào)用sendMessageDelayed函數(shù)枣购,傳入的時間參數(shù)為0嬉探,表示這個消息不要延時處理棉圈,而在sendMessageDelayed函數(shù)中涩堤,則會先獲得當(dāng)前時間,然后加上消息要延時處理的時間分瘾,即得到這個處理這個消息的絕對時間胎围,然后調(diào)用sendMessageAtTime函數(shù)來把消息加入到應(yīng)用程序的消息隊列中去。
在sendMessageAtTime函數(shù)德召,首先得到應(yīng)用程序的消息隊列mQueue白魂,這是在Handler對象構(gòu)造時初始化好的,前面已經(jīng)分析過了上岗,接著設(shè)置這個消息的目標(biāo)對象target福荸,即這個消息最終是由誰來處理的:
msg.target = this;
這里將它賦值為this,即表示這個消息最終由這個Handler對象來處理肴掷,設(shè)置為別的對象敬锐,則會控制其他進程實現(xiàn)。
函數(shù)最后調(diào)用queue.enqueueMessage來把這個消息加入到應(yīng)用程序的消息隊列中去捆等,這個函數(shù)實現(xiàn)在frameworks/base/core/java/android/os/MessageQueue.java文件中:
public class MessageQueue {
......
final boolean enqueueMessage(Message msg, long when) {
......
final boolean needWake;
synchronized (this) {
......
msg.when = when;
//Log.d("MessageQueue", "Enqueing: " + msg);
Message p = mMessages;
if (p == null || when == 0 || when < p.when) {
msg.next = p;
mMessages = msg;
needWake = mBlocked; // new head, might need to wake up
} else {
Message prev = null;
while (p != null && p.when <= when) {
prev = p;
p = p.next;
}
msg.next = prev.next;
prev.next = msg;
needWake = false; // still waiting on head, no need to wake up
}
}
if (needWake) {
nativeWake(mPtr);
}
return true;
}
......
}
把消息加入到消息隊列時滞造,分兩種情況,一種當(dāng)前消息隊列為空時栋烤,這時候應(yīng)用程序的主線程一般就是處于空閑等待狀態(tài)了谒养,這時候就要喚醒它,另一種情況是應(yīng)用程序的消息隊列不為空明郭,這時候就不需要喚醒應(yīng)用程序的主線程了买窟,因為這時候它一定是在忙著處于消息隊列中的消息,因此不會處于空閑等待的狀態(tài)薯定。
第一種情況比較簡單始绍,只要把消息放在消息隊列頭就可以了:
msg.next = p;
mMessages = msg;
needWake = mBlocked; // new head, might need to wake up
第二種情況相對就比較復(fù)雜一些了,前面我們說過话侄,當(dāng)往消息隊列中發(fā)送消息時亏推,是可以指定消息的處理時間的,而消息隊列中的消息年堆,就是按照這個時間從小到大來排序的吞杭,因此,當(dāng)把新的消息加入到消息隊列時变丧,就要根據(jù)它的處理時間來找到合適的位置芽狗,然后再放進消息隊列中去:
Message prev = null;
while (p != null && p.when <= when) {
prev = p;
p = p.next;
}
msg.next = prev.next;
prev.next = msg;
needWake = false; // still waiting on head, no need to wake up
把消息加入到消息隊列去后,如果應(yīng)用程序的主線程正處于空閑等待狀態(tài)痒蓬,就需要調(diào)用natvieWake函數(shù)來喚醒它了童擎,這是一個JNI方法滴劲。
3.3 消息的處理
在Looper類的loop成員函數(shù)中進行消息循環(huán)過程, 它從消息隊列中獲得消息對象msg后顾复,就會調(diào)用它的target成員變量的dispatchMessage函數(shù)來處理這個消息班挖。
在前面分析消息的發(fā)送時說過,這個消息對象msg的成員變量target是在發(fā)送消息的時候設(shè)置好的捕透,一般就通過哪個Handler來發(fā)送消息聪姿,就通過哪個Handler來處理消息。
dispatchMessage函數(shù)定義在frameworks/base/core/java/android/os/ Handler.java文件中:
public class Handler {
......
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
......
}
這里的消息對象msg的callback成員變量和Handler類的mCallBack成員變量一般都為null乙嘀,于是末购,就會調(diào)用Handler類的handleMessage函數(shù)來處理這個消息,由于子類在繼承Handler類時虎谢,一般會重寫handleMessage函數(shù)盟榴,因此,這里調(diào)用的實際上是子類的handleMessage函數(shù)婴噩。例:
public final class ActivityThread {
......
private final class H extends Handler {
......
public void handleMessage(Message msg) {
......
switch (msg.what) {
case LAUNCH_ACTIVITY: {
ActivityClientRecord r = (ActivityClientRecord)msg.obj;
r.packageInfo = getPackageInfoNoCheck(
r.activityInfo.applicationInfo);
handleLaunchActivity(r, null);
} break;
......
}
......
}
......
}
四擎场、總結(jié)
整個Handler的處理模型可以用下圖來說明:
而安卓中的多線程消息模型如下表示:
線程模型的序列圖如下:
參考文章:
老羅博客