android的消息處理機制——Looper,Handler,Message (原理圖、源碼)
轉自:http://my.oschina.net/u/1391648/blog/282892
在開始討論android的消息處理機制前,先來談談一些基本相關的術語。
通信的同步(Synchronous):指向客戶端發送請求后,必須要在服務端有回應后客戶端才繼續發送其它的請求,所以這時所有請求將會在服務端得到同步,直到服務端返回請求。
通信的異步(Asynchronous):指客戶端在發送請求后,不必等待服務端的回應就可以發送下一個請求。
所謂同步調用,就是在一個函數或方法調用時,沒有得到結果之前,該調用就不返回,直到返回結果。異步調用和同步是相對的,在一個異步調用發起后,被調用者立即返回給調用者,但是調用者不能立刻得到結果,被調用都在實際處理這個調用的請求完成后,通過狀態、通知或回調等方式來通知調用者請求處理的結果。
android的消息處理有三個核心類:Looper,Handler和Message。其實還有一Message Queue(消息隊列),但是MQ被封裝到Looper里面了,我們不會直接與MQ打交道,所以它不算是個核心類。
1. 消息類:Message類
android.os.Message的主要功能是進行消息的封裝,同時可以指定消息的操作形式,Message類定義的變量和常用方法如下:
(1)public int what:變量,用於定義此Message屬於何種操作
(2)public Object obj:變量,用於定義此Message傳遞的信息數據,通過它傳遞信息
(3)public int arg1:變量,傳遞一些整型數據時使用
(4)public int arg2:變量,傳遞一些整型數據時使用
(5)public Handler getTarget():普通方法,取得操作此消息的Handler對象。
在整個消息處理機制中,message又叫task,封裝了任務攜帶的信息和處理該任務的handler。message的用法比較簡單,但是有這么幾點需要注意:
(1)盡管Message有public的默認構造方法,但是你應該通過Message.obtain()來從消息池中獲得空消息對象,以節省資源。
(2)如果你的message只需要攜帶簡單的int信息,請優先使用Message.arg1和Message.arg2來傳遞信息,這比用Bundle更省內存
(3)擅用message.what來標識信息,以便用不同方式處理message。
(4)使用setData()存放Bundle對象。???
2. 消息通道:Looper
在使用Handler處理Message時,需要Looper(通道)來完成。在一個Activity中,系統會自動幫用戶啟動Looper對象,而在一個用戶自定義的類中,則需要用戶手工調用Looper類中的方法,然后才可以正常啟動Looper對象。Looper的字面意思是“循環者”,它被設計用來使一個普通線程變成Looper線程。所謂Looper線程就是循環工作的線程。在程序開發中(尤其是GUI開發中),我們經常會需要一個線程不斷循環,一旦有新任務則執行,執行完繼續等待下一個任務,這就是Looper線程。使用Looper類創建Looper線程很簡單:
1
2
3
4
5
6
7
8
9
10
11
12
|
public
class
LooperThread
extends
Thread {
@Override
public
void
run() {
// 將當前線程初始化為Looper線程
Looper.prepare();
// ...其他處理,如實例化handler
// 開始循環處理消息隊列
Looper.loop();
}
}
|
通過上面兩行核心代碼,你的線程就升級為Looper線程了!那么這兩行代碼都做了些什么呢?
1)Looper.prepare():創建Loop而對象。
通過上圖可以看到,現在你的線程中有一個Looper對象,它的內部維護了一個消息隊列MQ。注意,一個Thread只能有一個Looper對象,為什么呢?來看一下源碼
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
public
class
Looper {
// 每個線程中的Looper對象其實是一個ThreadLocal,即線程本地存儲(TLS)對象
private
static final
ThreadLocal sThreadLocal =
new
ThreadLocal();
// Looper內的消息隊列
final MessageQueue mQueue;
// 當前線程
Thread mThread;
//其他屬性
// 每個Looper對象中有它的消息隊列,和它所屬的線程
private
Looper() {
mQueue =
new
MessageQueue();
mRun =
true
;
mThread = Thread.currentThread();
}
// 我們調用該方法會在調用線程的TLS中創建Looper對象
public
static
final
void
prepare() {
if
(sThreadLocal.get() !=
null
) {
// 試圖在有Looper的線程中再次創建Looper將拋出異常
throw
new
RuntimeException(
"Only one Looper may be created per thread"
);
}
sThreadLocal.set(
new
Looper());
}
// 其他方法
}
|
prepare()背后的工作方式一目了然,其核心就是將looper對象定義為ThreadLocal。
2)Looper.loop():循環獲取MQ中的消息,並發送給相應Handler對象。
調用loop方法后,Looper線程就開始真正工作了,它不斷從自己的MQ中取出隊頭的消息(也叫任務)執行。其源碼分析如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
public
static
final
void
loop() {
Looper me = myLooper();
//得到當前線程Looper
MessageQueue queue = me.mQueue;
//得到當前looper的MQ
Binder.clearCallingIdentity();
final
long
ident = Binder.clearCallingIdentity();
// 開始循環
while
(
true
) {
Message msg = queue.next();
// 取出message
if
(msg !=
null
) {
if
(msg.target ==
null
) {
// message沒有target為結束信號,退出循環
return
;
}
// 日志
if
(me.mLogging!=
null
) me.mLogging.println(
">>>>> Dispatching to "
+ msg.target +
" "
+ msg.callback +
": "
+ msg.what
);
// 非常重要!將真正的處理工作交給message的target,即后面要講的handler
msg.target.dispatchMessage(msg);
// 日志
if
(me.mLogging!=
null
) me.mLogging.println(
"<<<<< Finished to "
+ msg.target +
" "
+ msg.callback);
final
long
newIdent = Binder.clearCallingIdentity();
if
(ident != newIdent) {
Log.wtf(
"Looper"
,
"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);
}
// 回收message資源
msg.recycle();
}
}
}
|
除了prepare()和loop()方法,Looper類還提供了一些有用的方法,比如Looper.myLooper()得到當前線程looper對象:
1
2
3
4
|
public
static
final
Looper myLooper() {
// 在任意線程調用Looper.myLooper()返回的都是那個線程的looper
return
(Looper)sThreadLocal.get();
}
|
getThread()得到looper對象所屬線程:
1
2
3
|
public
Thread getThread() {
return
mThread;
}
|
quit()方法結束looper循環:
1
2
3
4
5
6
|
public
void
quit() {
// 創建一個空的message,它的target為NULL,表示結束循環消息
Message msg = Message.obtain();
// 發出消息
mQueue.enqueueMessage(msg,
0
);
}
|
綜上,Looper有以下幾個要點:
1)每個線程有且只能有一個Looper對象,它是一個ThreadLocal
2)Looper內部有一個消息隊列,loop()方法調用后線程開始不斷從隊列中取出消息執行
3)Looper使一個線程變成Looper線程。
那么,我們如何操作Message Queue上的消息呢?這就是Handler的用處了
3. 消息操作類:Handler類
Message對象封裝了所有的消息,而這些消息的操作需要android.os.Handler類完成。什么是handler?handler起到了處理MQ上的消息的作用(只處理由自己發出的消息),即通知MQ它要執行一個任務(sendMessage),並在loop到自己的時候執行該任務(handleMessage),整個過程是異步的。handler創建時會關聯一個looper,默認的構造方法將關聯當前線程的looper,不過這也是可以set的。默認的構造方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
public
class
handler {
final
MessageQueue mQueue;
// 關聯的MQ
final
Looper mLooper;
// 關聯的looper
final
Callback mCallback;
// 其他屬性
public
Handler() {
if
(FIND_POTENTIAL_LEAKS) {
final
Class<?
extends
Handler> klass = getClass();
if
((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) ==
0
) {
Log.w(TAG,
"The following Handler class should be static or leaks might occur: "
+ klass.getCanonicalName());
}
}
// 默認將關聯當前線程的looper
mLooper = Looper.myLooper();
// looper不能為空,即該默認的構造方法只能在looper線程中使用
if
(mLooper ==
null
) {
throw
new
RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()"
);
}
// 重要!!!直接把關聯looper的MQ作為自己的MQ,因此它的消息將發送到關聯looper的MQ上
mQueue = mLooper.mQueue;
mCallback =
null
;
}
// 其他方法
}
|
下面我們就可以為之前的LooperThread類加入Handler:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
public
class
LooperThread
extends
Thread {
private
Handler handler1;
private
Handler handler2;
@Override
public
void
run() {
// 將當前線程初始化為Looper線程
Looper.prepare();
// 實例化兩個handler
handler1 =
new
Handler();
handler2 =
new
Handler();
// 開始循環處理消息隊列
Looper.loop();
}
}
|
加入handler后的效果如下圖:
可以看到,一個線程可以有多個Handler,但是只能有一個Looper!
Handler發送消息
有了handler之后,我們就可以使用
post(Runnable)
postAtTime(Runnable, long)
postDelayed(Runnable, long)
sendEmptyMessage(int)
sendMessage(Message)
sendMessageAtTime(Message, long)
sendMessageDelayed(Message, long)
這些方法向MQ上發送消息了。光看這些API你可能會覺得handler能發兩種消息,一種是Runnable對象,一種是message對象,這是直觀的理解,但其實post發出的Runnable對象最后都被封裝成message對象了,見源碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
// 此方法用於向關聯的MQ上發送Runnable對象,它的run方法將在handler關聯的looper線程中執行
public
final
boolean
post(Runnable r)
{
// 注意getPostMessage(r)將runnable封裝成message
return
sendMessageDelayed(getPostMessage(r),
0
);
}
private
final
Message getPostMessage(Runnable r) {
Message m = Message.obtain();
//得到空的message
m.callback = r;
//將runnable設為message的callback,
return
m;
}
public
boolean
sendMessageAtTime(Message msg,
long
uptimeMillis)
{
boolean
sent =
false
;
MessageQueue queue = mQueue;
if
(queue !=
null
) {
msg.target =
this
;
// message的target必須設為該handler!
sent = queue.enqueueMessage(msg, uptimeMillis);
}
else
{
RuntimeException e =
new
RuntimeException(
this
+
" sendMessageAtTime() called with no mQueue"
);
Log.w(
"Looper"
, e.getMessage(), e);
}
return
sent;
}
|
通過handler發出的message有如下特點:
1.message.target為該handler對象,這確保了looper執行到該message時能找到處理它的handler,即loop()方法中的關鍵代碼
msg.target.dispatchMessage(msg);
2.post發出的message,其callback為Runnable對象
Handler處理消息
說完了消息的發送,再來看下handler如何處理消息。消息的處理是通過核心方法dispatchMessage(Message msg)與鈎子方法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);
- }
- }
可以看到,除了handleMessage(Message msg)和Runnable對象的run方法由開發者實現外(實現具體邏輯),handler的內部工作機制對開發者是透明的。Handler擁有下面兩個重要的特點:
1)handler可以在任意線程發送消息,這些消息會被添加到關聯的MQ上
2)消息的處理是通過核心方法dispatchMessage(Message msg)與鈎子方法handleMessage(Message msg)完成的,handler是在它關聯的looper線程中處理消息的。
這就解決了android最經典的不能在其他非主線程中更新UI的問題。android的主線程也是一個looper線程(looper在android中運用很廣),我們在其中創建的handler默認將關聯主線程MQ。因此,利用handler的一個solution就是在activity中創建handler並將其引用傳遞給worker thread,worker thread執行完任務后使用handler發送消息通知activity更新UI。(過程如圖)
下面給出sample代碼,僅供參考:
TestDriverActivity Activity { TextView textview; @Override onCreate(Bundle savedInstanceState) { .onCreate(savedInstanceState); setContentView(R.layout.main); textview = (TextView) findViewById(R.id.textview); Thread workerThread = Thread( SampleTask( MyHandler())); workerThread.start(); } appendText(String msg) { textview.setText(textview.getText() + "\n" + msg); } MyHandler Handler { @Override handleMessage(Message msg) { String result = msg.getData().getString("message"); appendText(result); } } }
SampleTask Runnable { String TAG = SampleTask..getSimpleName(); Handler handler; SampleTask(Handler handler) { (); .handler = handler; } @Override run() { { Thread.sleep(5000); Message msg = prepareMessage("task completed!"); handler.sendMessage(msg); } (InterruptedException e) { Log.d(TAG, "interrupted!"); } } Message prepareMessage(String str) { Message result = handler.obtainMessage(); Bundle data = Bundle(); data.putString("message", str); result.setData(data); result; } }
android Handler 機制研究學習筆記 (原理圖)
轉自:http://www.cnblogs.com/youxilua/archive/2011/11/25/2263825.html
前言: 很早以前,學習android的時候就接觸過Handler ,知道Handler是一個用於線程間通信的類,最常用於做下載條,最近,看了Pro android 3 這本書,里面描述的Handler 說得非常的細致,與此,寫下Handler的學習筆記
Android 運行的進程
為了,更好的了解Handler的機制,我們應該首先,將Android系統整個運行進程都要爛熟於心,下面是android 進程運行圖:
從圖中我們可以看到,當我們從外部調用組件的時候,Service 和 ContentProvider 是從線程池那里獲取線程,而Activity 和BroadcastReceiver是直接在主線程運行,為了,追蹤線程,我們可以用debug 方法,或者使用一個工具類,這里,我們創建一個用於監視線程的工具類
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667/**
* @author Tom_achai
* @
date
2011-11-20
*
*/
public class Utils {
public static long getThreadId(){
Thread t = Thread.currentThread();
return
t.getId();
}
/**
* 獲取單獨線程信息
* @
return
*/
public static String getThreadSignature(){
Thread t = Thread.currentThread();
long l = t.getId();
String name = t.getName();
long p = t.getPriority();
String gname = t.getThreadGroup().getName();
return
(
"(Thread):"
+name+
":(id)"
+ l +
"(:priority)"
+ p +
":(group)"
+ gname );
}
/**
*獲取當前線程 信息
*/
public static void logThreadSignature(){
Log.d(
"ThreadUtils"
, getThreadSignature());
}
public static void logThreadSignature(String name ){
Log.d(
"ThreadUtils"
, name +
":"
+getThreadSignature());
}
public static void sleepForInSecs(int secs){
try{
Thread.
sleep
(secs * 1000);
}catch (Exception e) {
//
TODO: handle exception
e.printStackTrace();
}
}
/**
* 講String放進Bundle 中
* @param message
* @
return
*/
public static Bundle getStringAsBundle(String message){
Bundle b = new Bundle();
b.putString(
"message"
, message);
return
b;
}
/**
*
* 獲取Bundle的String
* @param b
* @
return
*/
public static String getStringFromABundle(Bundle b){
return
b.getString(
"message"
);
}
}
有了這樣一個類就可以方便我們觀察線程的運行
好了,現在准備好以后就進入正題Handler
Handlers
為什么要使用Handlers?
因為,我們當我們的主線程隊列,如果處理一個消息超過5秒,android 就會拋出一個 ANP(無響應)的消息,所以,我們需要把一些要處理比較長的消息,放在一個單獨線程里面處理,把處理以后的結果,返回給主線程運行,就需要用的Handler來進行線程建的通信,關系如下圖;
下面是Handler,Message,Message Queue 之間的關系圖
這個圖有4個地方關系到handlers
1, 主線程(Main thread)
2, 主線程隊列(Main thread queue)
3,Hanlder
4,Message
上面的四個地方,主線程,和主線程的隊列我們無需處理,所以,我們主要是處理Handler 和 Message 之間的關系.
我們每發出一個Message,Message就會落在主線程的隊列當中,然后,Handler就可以調用Message綁定的數據,對主線程的組件進行操作.
Message
作為handler接受的對象,我們有必要知道Message這個數據類型是個怎樣的數據類型
從官方文檔中我們可以知道message 關於數據的字段
public int what public int arg1 public int arg2 public Object obj 從上面的表格可以看出,message 提供了一個對象來存儲對象,而且,還提供了三個int字段用來存儲少量int類型
當然,除了以上三個Message 自有的字段外,我們還可以通過setData(Bundle b),來存儲一個Bundle對象,來存儲更豐富的數據類型,例如,圖片等等.
在初始化我們的message的時候就可以為我們的Message默認字段賦值,注意賦值順序!!!
123456789Message msg = obtainMessage();
//
設置我們what 字段的初值,注意順序!!!
Message msg = mHandler.obtainMessage(int what);
//
下面同理
Message msg = mHandler.obtainMessage(int what,Object object);
Message msg = mHandler.obtainMessage(int what,int arg1,int arg2);
Message msg = mHandler.obtainMessage(int what,int arg1,int arg2, Object obj
);
handler機制的原理 (原理圖)
轉自:http://blog.csdn.net/itachi85/article/details/8035333
andriod提供了Handler 和 Looper 來滿足線程間的通信。Handler先進先出原則。Looper類用來管理特定線程內對象之間的消息交換(MessageExchange)。
1)Looper: 一個線程可以產生一個Looper對象,由它來管理此線程里的MessageQueue(消息隊列)。
2)Handler: 你可以構造Handler對象來與Looper溝通,以便push新消息到MessageQueue里;或者接收Looper從Message Queue取出)所送來的消息。
3) Message Queue(消息隊列):用來存放線程放入的消息。4)線程:UIthread 通常就是main thread,而Android啟動程序時會替它建立一個MessageQueue。
1.Handler創建消息
每一個消息都需要被指定的Handler處理,通過Handler創建消息便可以完成此功能。Android消息機制中引入了消息池。Handler創建消息時首先查詢消息池中是否有消息存在,如果有直接從消息池中取得,如果沒有則重新初始化一個消息實例。使用消息池的好處是:消息不被使用時,並不作為垃圾回收,而是放入消息池,可供下次Handler創建消息時使用。消息池提高了消息對象的復用,減少系統垃圾回收的次數。消息的創建流程如圖所示。
2.Handler發送消息
UI主線程初始化第一個Handler時會通過ThreadLocal創建一個Looper,該Looper與UI主線程一一對應。使用ThreadLocal的目的是保證每一個線程只創建唯一一個Looper。之后其他Handler初始化的時候直接獲取第一個Handler創建的Looper。Looper初始化的時候會創建一個消息隊列MessageQueue。至此,主線程、消息循環、消息隊列之間的關系是1:1:1。
Handler、Looper、MessageQueue的初始化流程如圖所示:
Hander持有對UI主線程消息隊列MessageQueue和消息循環Looper的引用,子線程可以通過Handler將消息發送到UI線程的消息隊列MessageQueue中。
3.Handler處理消息
UI主線程通過Looper循環查詢消息隊列UI_MQ,當發現有消息存在時會將消息從消息隊列中取出。首先分析消息,通過消息的參數判斷該消息對應的Handler,然后將消息分發到指定的Handler進行處理。
子線程通過Handler、Looper與UI主線程通信的流程如圖所示。
Android 異步消息處理機制 讓你深入理解 Looper、Handler、Message三者關系 (源碼!!!)
轉載請標明出處:http://blog.csdn.net/lmj623565791/article/details/38377229 ,本文出自【張鴻洋的博客】
很多人面試肯定都被問到過,請問Android中的Looper , Handler , Message有什么關系?本篇博客目的首先為大家從源碼角度介紹3者關系,然后給出一個容易記憶的結論。
1、 概述
Handler 、 Looper 、Message 這三者都與Android異步消息處理線程相關的概念。那么什么叫異步消息處理線程呢?
異步消息處理線程啟動后會進入一個無限的循環體之中,每循環一次,從其內部的消息隊列中取出一個消息,然后回調相應的消息處理函數,執行完成一個消息后則繼續循環。若消息隊列為空,線程則會阻塞等待。說了這一堆,那么和Handler 、 Looper 、Message有啥關系?其實Looper負責的就是創建一個MessageQueue,然后進入一個無限循環體不斷從該MessageQueue中讀取消息,而消息的創建者就是一個或多個Handler 。
2、 源碼解析
1、Looper
對於Looper主要是prepare()和loop()兩個方法。
首先看prepare()方法
- public static final void prepare() {
- if (sThreadLocal.get() != null) {
- throw new RuntimeException("Only one Looper may be created per thread");
- }
- sThreadLocal.set(new Looper(true));
- }
sThreadLocal是一個ThreadLocal對象,可以在一個線程中存儲變量。可以看到,在第5行,將一個Looper的實例放入了ThreadLocal,並且2-4行判斷了sThreadLocal是否為null,否則拋出異常。這也就說明了Looper.prepare()方法不能被調用兩次,同時也保證了一個線程中只有一個Looper實例~相信有些哥們一定遇到這個錯誤。
下面看Looper的構造方法:
- private Looper(boolean quitAllowed) {
- mQueue = new MessageQueue(quitAllowed);
- mRun = true;
- mThread = Thread.currentThread();
- }
在構造方法中,創建了一個MessageQueue(消息隊列)。
然后我們看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.recycle();
- }
- }
第2行:
public static Looper myLooper() {
return sThreadLocal.get();
}
方法直接返回了sThreadLocal存儲的Looper實例,如果me為null則拋出異常,也就是說looper方法必須在prepare方法之后運行。
第6行:拿到該looper實例中的mQueue(消息隊列)
13到45行:就進入了我們所說的無限循環。
14行:取出一條消息,如果沒有消息則阻塞。
27行:使用調用 msg.target.dispatchMessage(msg);把消息交給msg的target的dispatchMessage方法去處理。Msg的target是什么呢?其實就是handler對象,下面會進行分析。
44行:釋放消息占據的資源。
Looper主要作用:
1、 與當前線程綁定,保證一個線程只會有一個Looper實例,同時一個Looper實例也只有一個MessageQueue。
2、 loop()方法,不斷從MessageQueue中去取消息,交給消息的target屬性的dispatchMessage去處理。
好了,我們的異步消息處理線程已經有了消息隊列(MessageQueue),也有了在無限循環體中取出消息的哥們,現在缺的就是發送消息的對象了,於是乎:Handler登場了。2、Handler
使用Handler之前,我們都是初始化一個實例,比如用於更新UI線程,我們會在聲明的時候直接初始化,或者在onCreate中初始化Handler實例。所以我們首先看Handler的構造方法,看其如何與MessageQueue聯系上的,它在子線程中發送的消息(一般發送消息都在非UI線程)怎么發送到MessageQueue中的。
- public Handler() {
- this(null, false);
- }
- public Handler(Callback callback, boolean async) {
- if (FIND_POTENTIAL_LEAKS) {
- final Class<? extends Handler> klass = getClass();
- if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
- (klass.getModifiers() & Modifier.STATIC) == 0) {
- Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
- klass.getCanonicalName());
- }
- }
- 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;
- }
14行:通過Looper.myLooper()獲取了當前線程保存的Looper實例,然后在19行又獲取了這個Looper實例中保存的MessageQueue(消息隊列),這樣就保證了handler的實例與我們Looper實例中MessageQueue關聯上了。
然后看我們最常用的sendMessage方法
- public final boolean sendMessage(Message msg)
- {
- return sendMessageDelayed(msg, 0);
- }
- public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
- Message msg = Message.obtain();
- msg.what = what;
- return sendMessageDelayed(msg, delayMillis);
- }
- 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);
- }
輾轉反則最后調用了sendMessageAtTime,在此方法內部有直接獲取MessageQueue然后調用了enqueueMessage方法,我們再來看看此方法:
- private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
- msg.target = this;
- if (mAsynchronous) {
- msg.setAsynchronous(true);
- }
- return queue.enqueueMessage(msg, uptimeMillis);
- }
enqueueMessage中首先為meg.target賦值為this,【如果大家還記得Looper的loop方法會取出每個msg然后交給msg,target.dispatchMessage(msg)去處理消息】,也就是把當前的handler作為msg的target屬性。最終會調用queue的enqueueMessage的方法,也就是說handler發出的消息,最終會保存到消息隊列中去。
現在已經很清楚了Looper會調用prepare()和loop()方法,在當前執行的線程中保存一個Looper實例,這個實例會保存一個MessageQueue對象,然后當前線程進入一個無限循環中去,不斷從MessageQueue中讀取Handler發來的消息。然后再回調創建這個消息的handler中的dispathMessage方法,下面我們趕快去看一看這個方法:
- public void dispatchMessage(Message msg) {
- if (msg.callback != null) {
- handleCallback(msg);
- } else {
- if (mCallback != null) {
- if (mCallback.handleMessage(msg)) {
- return;
- }
- }
- handleMessage(msg);
- }
- }
可以看到,第10行,調用了handleMessage方法,下面我們去看這個方法:
- /**
- * Subclasses must implement this to receive messages.
- */
- public void handleMessage(Message msg) {
- }
可以看到這是一個空方法,為什么呢,因為消息的最終回調是由我們控制的,我們在創建handler的時候都是復寫handleMessage方法,然后根據msg.what進行消息處理。
例如:
- private Handler mHandler = new Handler()
- {
- public void handleMessage(android.os.Message msg)
- {
- switch (msg.what)
- {
- case value:
- break;
- default:
- break;
- }
- };
- };
到此,這個流程已經解釋完畢,讓我們首先總結一下
1、首先Looper.prepare()在本線程中保存一個Looper實例,然后該實例中保存一個MessageQueue對象;因為Looper.prepare()在一個線程中只能調用一次,所以MessageQueue在一個線程中只會存在一個。
2、Looper.loop()會讓當前線程進入一個無限循環,不端從MessageQueue的實例中讀取消息,然后回調msg.target.dispatchMessage(msg)方法。
3、Handler的構造方法,會首先得到當前線程中保存的Looper實例,進而與Looper實例中的MessageQueue想關聯。
4、Handler的sendMessage方法,會給msg的target賦值為handler自身,然后加入MessageQueue中。
5、在構造Handler實例時,我們會重寫handleMessage方法,也就是msg.target.dispatchMessage(msg)最終調用的方法。
好了,總結完成,大家可能還會問,那么在Activity中,我們並沒有顯示的調用Looper.prepare()和Looper.loop()方法,為啥Handler可以成功創建呢,這是因為在Activity的啟動代碼中,已經在當前UI線程調用了Looper.prepare()和Looper.loop()方法。
3、Handler post
今天有人問我,你說Handler的post方法創建的線程和UI線程有什么關系?
其實這個問題也是出現這篇博客的原因之一;這里需要說明,有時候為了方便,我們會直接寫如下代碼:
- mHandler.post(new Runnable()
- {
- @Override
- public void run()
- {
- Log.e("TAG", Thread.currentThread().getName());
- mTxt.setText("yoxi");
- }
- });
然后run方法中可以寫更新UI的代碼,其實這個Runnable並沒有創建什么線程,而是發送了一條消息,下面看源碼:
- public final boolean post(Runnable r)
- {
- return sendMessageDelayed(getPostMessage(r), 0);
- }
- private static Message getPostMessage(Runnable r) {
- Message m = Message.obtain();
- m.callback = r;
- return m;
- }
可以看到,在getPostMessage中,得到了一個Message對象,然后將我們創建的Runable對象作為callback屬性,賦值給了此message.
注:產生一個Message對象,可以new ,也可以使用Message.obtain()方法;兩者都可以,但是更建議使用obtain方法,因為Message內部維護了一個Message池用於Message的復用,避免使用new 重新分配內存。
- 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);
- }
最終和handler.sendMessage一樣,調用了sendMessageAtTime,然后調用了enqueueMessage方法,給msg.target賦值為handler,最終加入MessagQueue.
可以看到,這里msg的callback和target都有值,那么會執行哪個呢?
其實上面已經貼過代碼,就是dispatchMessage方法:
- public void dispatchMessage(Message msg) {
- if (msg.callback != null) {
- handleCallback(msg);
- } else {
- if (mCallback != null) {
- if (mCallback.handleMessage(msg)) {
- return;
- }
- }
- handleMessage(msg);
- }
- }
第2行,如果不為null,則執行callback回調,也就是我們的Runnable對象。
好了,關於Looper , Handler , Message 這三者關系上面已經敘述的非常清楚了。
最后來張圖解:
希望圖片可以更好的幫助大家的記憶~~
4、后話
其實Handler不僅可以更新UI,你完全可以在一個子線程中去創建一個Handler,然后使用這個handler實例在任何其他線程中發送消息,最終處理消息的代碼都會在你創建Handler實例的線程中運行。
- new Thread()
- {
- private Handler handler;
- public void run()
- {
- Looper.prepare();
- handler = new Handler()
- {
- public void handleMessage(android.os.Message msg)
- {
- Log.e("TAG",Thread.currentThread().getName());
- };
- };<pre code_snippet_id="445431" snippet_file_name="blog_20140808_19_1943618" name="code" class="java"> Looper.loop(); } </pre>
Android不僅給我們提供了異步消息處理機制讓我們更好的完成UI的更新,其實也為我們提供了異步消息處理機制代碼的參考~~不僅能夠知道原理,最好還可以將此設計用到其他的非Android項目中去~~
最新補充:
關於后記,有兄弟聯系我說,到底可以在哪使用,見博客:Android Handler 異步消息處理機制的妙用 創建強大的圖片加載類
Android之Handler用法總結 (Handler與Thread\TimerTask的結合使用思路,不用細看代碼。)
轉自:http://www.cnblogs.com/devinzhang/archive/2011/12/30/2306980.html
方法一:(java習慣,在android平台開發時這樣是不行的,因為它違背了單線程模型)
剛剛開始接觸android線程編程的時候,習慣好像java一樣,試圖用下面的代碼解決問題
new Thread( new Runnable() {
public void run() {
myView.invalidate();
}
}).start();可以實現功能,刷新UI界面。但是這樣是不行的,因為它違背了單線程模型:Android UI操作並不是線程安全的並且這些操作必須在UI線程中執行。
方法二:(Thread+Handler)
查閱了文檔和apidemo后,發覺常用的方法是利用Handler來實現UI線程的更新的。
Handler來根據接收的消息,處理UI更新。Thread線程發出Handler消息,通知更新UI。
Handler myHandler = new Handler() {
public void handleMessage(Message msg) {
switch (msg.what) {
case TestHandler.GUIUPDATEIDENTIFIER:
myBounceView.invalidate();
break;
}
super.handleMessage(msg);
}
};
class myThread implements Runnable {
public void run() {
while (!Thread.currentThread().isInterrupted()) {
Message message = new Message();
message.what = TestHandler.GUIUPDATEIDENTIFIER;
TestHandler.this.myHandler.sendMessage(message);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
}以上方法demo看:http://rayleung.javaeye.com/blog/411860
方法三:(java習慣。Android平台中,這樣做是不行的,這跟Android的線程安全有關)
在Android平台中需要反復按周期執行方法可以使用Java上自帶的TimerTask類,TimerTask相對於Thread來說對於資源消耗的更低,除了使用Android自帶的AlarmManager使用Timer定時器是一種更好的解決方法。 我們需要引入import java.util.Timer; 和 import java.util.TimerTask;
public class JavaTimer extends Activity {
Timer timer = new Timer();
TimerTask task = new TimerTask(){
public void run() {
setTitle("hear me?");
}
};
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
timer.schedule(task, 10000);
}
}
方法四:(TimerTask + Handler)
通過配合Handler來實現timer功能的!
public class TestTimer extends Activity {
Timer timer = new Timer();
Handler handler = new Handler(){
public void handleMessage(Message msg) {
switch (msg.what) {
case 1:
setTitle("hear me?");
break;
}
super.handleMessage(msg);
}
};
TimerTask task = new TimerTask(){
public void run() {
Message message = new Message();
message.what = 1;
handler.sendMessage(message);
}
};
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
timer.schedule(task, 10000);
}
}
方法五:( Runnable + Handler.postDelayed(runnable,time) )
在Android里定時更新 UI,通常使用的是 java.util.Timer, java.util.TimerTask, android.os.Handler組合。實際上Handler 自身已經提供了定時的功能。
private Handler handler = new Handler();
private Runnable myRunnable= new Runnable() {
public void run() {
if (run) {
handler.postDelayed(this, 1000);
count++;
}
tvCounter.setText("Count: " + count);
}
};然后在其他地方調用
handler.post(myRunnable);
handler.post(myRunnable,time);
案例看:http://shaobin0604.javaeye.com/blog/515820
====================================================================
知識點總結補充:
很多初入Android或Java開發的新手對Thread、Looper、Handler和Message仍然比較迷惑,衍生的有HandlerThread、java.util.concurrent、Task、AsyncTask由於目前市面上的書籍等資料都沒有談到這些問題,今天就這一問題做更系統性的總結。我們創建的Service、Activity以及Broadcast均是一個主線程處理,這里我們可以理解為UI線程。但是在操作一些耗時操作時,比如I/O讀寫的大文件讀寫,數據庫操作以及網絡下載需要很長時間,為了不阻塞用戶界面,出現ANR的響應提示窗口,這個時候我們可以考慮使用Thread線程來解決。
對於從事過J2ME開發的程序員來說Thread比較簡單,直接匿名創建重寫run方法,調用start方法執行即可。或者從Runnable接口繼承,但對於Android平台來說UI控件都沒有設計成為線程安全類型,所以需要引入一些同步的機制來使其刷新,這點Google在設計Android時倒是參考了下Win32的消息處理機制。
1. 對於線程中的刷新一個View為基類的界面,可以使用postInvalidate()方法在線程中來處理,其中還提供了一些重寫方法比如postInvalidate(int left,int top,int right,int bottom) 來刷新一個矩形區域,以及延時執行,比如postInvalidateDelayed(long delayMilliseconds)或postInvalidateDelayed(long delayMilliseconds,int left,int top,int right,int bottom) 方法,其中第一個參數為毫秒
2. 當然推薦的方法是通過一個Handler來處理這些,可以在一個線程的run方法中調用handler對象的 postMessage或sendMessage方法來實現,Android程序內部維護着一個消息隊列,會輪訓處理這些,如果你是Win32程序員可以很好理解這些消息處理,不過相對於Android來說沒有提供 PreTranslateMessage這些干涉內部的方法。
3. Looper又是什么呢? ,其實Android中每一個Thread都跟着一個Looper,Looper可以幫助Thread維護一個消息隊列,但是Looper和Handler沒有什么關系,我們從開源的代碼可以看到Android還提供了一個Thread繼承類HanderThread可以幫助我們處理,在HandlerThread對象中可以通過getLooper方法獲取一個Looper對象控制句柄,我們可以將其這個Looper對象映射到一個Handler中去來實現一個線程同步機制,Looper對象的執行需要初始化Looper.prepare方法就是昨天我們看到的問題,同時推出時還要釋放資源,使用Looper.release方法。
4.Message 在Android是什么呢? 對於Android中Handler可以傳遞一些內容,通過Bundle對象可以封裝String、Integer以及Blob二進制對象,我們通過在線程中使用Handler對象的sendEmptyMessage或sendMessage方法來傳遞一個Bundle對象到Handler處理器。對於Handler類提供了重寫方法handleMessage(Message msg) 來判斷,通過msg.what來區分每條信息。將Bundle解包來實現Handler類更新UI線程中的內容實現控件的刷新操作。相關的Handler對象有關消息發送sendXXXX相關方法如下,同時還有postXXXX相關方法,這些和Win32中的道理基本一致,一個為發送后直接返回,一個為處理后才返回 .
5. java.util.concurrent對象分析,對於過去從事Java開發的程序員不會對Concurrent對象感到陌生吧,他是JDK 1.5以后新增的重要特性作為掌上設備,我們不提倡使用該類,考慮到Android為我們已經設計好的Task機制,這里不做過多的贅述,相關原因參考下面的介紹:
6. 在Android中還提供了一種有別於線程的處理方式,就是Task以及AsyncTask,從開源代碼中可以看到是針對Concurrent的封裝,開發人員可以方便的處理這些異步任務。
摘錄自:http://www.cnblogs.com/playing/archive/2011/03/24/1993583.html
Android應用程序消息處理機制(Looper、Handler)分析 (!!!消息的循環、發送、處理原理!深入!分析!)
轉自:http://blog.csdn.net/luoshengyang/article/details/6817933/
Android應用程序是通過消息來驅動的,系統為每一個應用程序維護一個消息隊例,應用程序的主線程不斷地從這個消息隊例中獲取消息(Looper),然后對這些消息進行處理(Handler),這樣就實現了通過消息來驅動應用程序的執行,本文將詳細分析Android應用程序的消息處理機制。
前面我們學習Android應用程序中的Activity啟動(Android應用程序啟動過程源代碼分析和Android應用程序內部啟動Activity過程(startActivity)的源代碼分析)、Service啟動(Android系統在新進程中啟動自定義服務過程(startService)的原理分析和Android應用程序綁定服務(bindService)的過程源代碼分析)以及廣播發送(Android應用程序發送廣播(sendBroadcast)的過程分析)時,它們都有一個共同的特點,當ActivityManagerService需要與應用程序進行並互時,如加載Activity和Service、處理廣播待,會通過Binder進程間通信機制來知會應用程序,應用程序接收到這個請求時,它不是馬上就處理這個請求,而是將這個請求封裝成一個消息,然后把這個消息放在應用程序的消息隊列中去,然后再通過消息循環來處理這個消息。這樣做的好處就是消息的發送方只要把消息發送到應用程序的消息隊列中去就行了,它可以馬上返回去處理別的事情,而不需要等待消息的接收方去處理完這個消息才返回,這樣就可以提高系統的並發性。實質上,這就是一種異步處理機制。
這樣說可能還是比較籠統,我們以Android應用程序啟動過程源代碼分析一文中所介紹的應用程序啟動過程的一個片斷來具體看看是如何這種消息處理機制的。在這篇文章中,要啟動的應用程序稱為Activity,它的默認Activity是MainActivity,它是由Launcher來負責啟動的,而Launcher又是通過ActivityManagerService來啟動的,當ActivityManagerService為這個即將要啟的應用程序准備好新的進程后,便通過一個Binder進程間通信過程來通知這個新的進程來加載MainActivity,如下圖所示:
它對應Android應用程序啟動過程中的Step 30到Step 35,有興趣的讀者可以回過頭去參考Android應用程序啟動過程源代碼分析一文。這里的Step 30中的scheduleLaunchActivity是ActivityManagerService通過Binder進程間通信機制發送過來的請求,它請求應用程序中的ActivityThread執行Step 34中的performLaunchActivity操作,即啟動MainActivity的操作。這里我們就可以看到,Step 30的這個請求並沒有等待Step 34這個操作完成就返回了,它只是把這個請求封裝成一個消息,然后通過Step 31中的queueOrSendMessage操作把這個消息放到應用程序的消息隊列中,然后就返回了。應用程序發現消息隊列中有消息時,就會通過Step 32中的handleMessage操作來處理這個消息,即調用Step 33中的handleLaunchActivity來執行實際的加載MainAcitivy類的操作。
了解Android應用程序的消息處理過程之后,我們就開始分樣它的實現原理了。與Windows應用程序的消息處理過程一樣,Android應用程序的消息處理機制也是由消息循環、消息發送和消息處理這三個部分組成的,接下來,我們就詳細描述這三個過程。
1. 消息循環
在消息處理機制中,消息都是存放在一個消息隊列中去,而應用程序的主線程就是圍繞這個消息隊列進入一個無限循環的,直到應用程序退出。如果隊列中有消息,應用程序的主線程就會把它取出來,並分發給相應的Handler進行處理;如果隊列中沒有消息,應用程序的主線程就會進入空閑等待狀態,等待下一個消息的到來。在Android應用程序中,這個消息循環過程是由Looper類來實現的,它定義在frameworks/base/core/Java/android/os/Looper.java文件中,在分析這個類之前,我們先看一下Android應用程序主線程是如何進入到這個消息循環中去的。
在Android應用程序進程啟動過程的源代碼分析一文中,我們分析了Android應用程序進程的啟動過程,Android應用程序進程在啟動的時候,會在進程中加載ActivityThread類,並且執行這個類的main函數,應用程序的消息循環過程就是在這個main函數里面實現的,我們來看看這個函數的實現,它定義在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();
- ......
- }
- }
這個函數做了兩件事情,一是在主線程中創建了一個ActivityThread實例,二是通過Looper類使主線程進入消息循環中,這里我們只關注后者。
首先看Looper.prepareMainLooper函數的實現,這是一個靜態成員函數,定義在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();
- }
- ......
- }
函數prepareMainLooper做的事情其實就是在線程中創建一個Looper對象,這個Looper對象是存放在sThreadLocal成員變量里面的,成員變量sThreadLocal的類型為ThreadLocal,表示這是一個線程局部變量,即保證每一個調用了prepareMainLooper函數的線程里面都有一個獨立的Looper對象。在線程是創建Looper對象的工作是由prepare函數來完成的,而在創建Looper對象的時候,會同時創建一個消息隊列MessageQueue,保存在Looper的成員變量mQueue中,后續消息就是存放在這個隊列中去。消息隊列在Android應用程序消息處理機制中最重要的組件,因此,我們看看它的創建過程,即它的構造函數的實現,實現frameworks/base/core/java/android/os/MessageQueue.java文件中:
- public class MessageQueue {
- ......
- private int mPtr; // used by native code
- private native void nativeInit();
- MessageQueue() {
- nativeInit();
- }
- ......
- }
它的初始化工作都交給JNI方法nativeInit來實現了,這個JNI方法定義在frameworks/base/core/jni/android_os_MessageQueue.cpp文件中:
- static void android_os_MessageQueue_nativeInit(JNIEnv* env, jobject obj) {
- NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();
- if (! nativeMessageQueue) {
- jniThrowRuntimeException(env, "Unable to allocate native queue");
- return;
- }
- android_os_MessageQueue_setNativeMessageQueue(env, obj, nativeMessageQueue);
- }
在JNI中,也相應地創建了一個消息隊列NativeMessageQueue,NativeMessageQueue類也是定義在frameworks/base/core/jni/android_os_MessageQueue.cpp文件中,它的創建過程如下所示:
- NativeMessageQueue::NativeMessageQueue() {
- mLooper = Looper::getForThread();
- if (mLooper == NULL) {
- mLooper = new Looper(false);
- Looper::setForThread(mLooper);
- }
- }
它主要就是在內部創建了一個Looper對象,注意,這個Looper對象是實現在JNI層的,它與上面Java層中的Looper是不一樣的,不過它們是對應的,下面我們進一步分析消息循環的過程的時候,讀者就會清楚地了解到它們之間的關系。
這個Looper的創建過程也很重要,不過我們暫時放一放,先分析完android_os_MessageQueue_nativeInit函數的執行,它創建了本地消息隊列NativeMessageQueue對象之后,接着調用android_os_MessageQueue_setNativeMessageQueue函數來把這個消息隊列對象保存在前面我們在Java層中創建的MessageQueue對象的mPtr成員變量里面:
- static void android_os_MessageQueue_setNativeMessageQueue(JNIEnv* env, jobject messageQueueObj,
- NativeMessageQueue* nativeMessageQueue) {
- env->SetIntField(messageQueueObj, gMessageQueueClassInfo.mPtr,
- reinterpret_cast<jint>(nativeMessageQueue));
- }
這里傳進來的參數messageQueueObj即為我們前面在Java層創建的消息隊列對象,而gMessageQueueClassInfo.mPtr即表示在Java類MessageQueue中,其成員變量mPtr的偏移量,通過這個偏移量,就可以把這個本地消息隊列對象natvieMessageQueue保存在Java層創建的消息隊列對象的mPtr成員變量中,這是為了后續我們調用Java層的消息隊列對象的其它成員函數進入到JNI層時,能夠方便地找回它在JNI層所對應的消息隊列對象。
我們再回到NativeMessageQueue的構造函數中,看看JNI層的Looper對象的創建過程,即看看它的構造函數是如何實現的,這個Looper類實現在frameworks/base/libs/utils/Looper.cpp文件中:
- Looper::Looper(bool allowNonCallbacks) :
- mAllowNonCallbacks(allowNonCallbacks),
- mResponseIndex(0) {
- int wakeFds[2];
- int result = pipe(wakeFds);
- ......
- mWakeReadPipeFd = wakeFds[0];
- mWakeWritePipeFd = wakeFds[1];
- ......
- #ifdef LOOPER_USES_EPOLL
- // Allocate the epoll instance and register the wake pipe.
- mEpollFd = epoll_create(EPOLL_SIZE_HINT);
- ......
- struct epoll_event eventItem;
- memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union
- eventItem.events = EPOLLIN;
- eventItem.data.fd = mWakeReadPipeFd;
- result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, & eventItem);
- ......
- #else
- ......
- #endif
- ......
- }
這個構造函數做的事情非常重要,它跟我們后面要介紹的應用程序主線程在消息隊列中沒有消息時要進入等待狀態以及當消息隊列有消息時要把應用程序主線程喚醒的這兩個知識點息息相關。它主要就是通過pipe系統調用來創建了一個管道了:
- int wakeFds[2];
- int result = pipe(wakeFds);
- ......
- mWakeReadPipeFd = wakeFds[0];
- mWakeWritePipeFd = wakeFds[1];
管道是Linux系統中的一種進程間通信機制,具體可以參考前面一篇文章Android學習啟動篇推薦的一本書《Linux內核源代碼情景分析》中的第6章--傳統的Uinx進程間通信。簡單來說,管道就是一個文件,在管道的兩端,分別是兩個打開文件文件描述符,這兩個打開文件描述符都是對應同一個文件,其中一個是用來讀的,別一個是用來寫的,一般的使用方式就是,一個線程通過讀文件描述符中來讀管道的內容,當管道沒有內容時,這個線程就會進入等待狀態,而另外一個線程通過寫文件描述符來向管道中寫入內容,寫入內容的時候,如果另一端正有線程正在等待管道中的內容,那么這個線程就會被喚醒。這個等待和喚醒的操作是如何進行的呢,這就要借助Linux系統中的epoll機制了。 Linux系統中的epoll機制為處理大批量句柄而作了改進的poll,是Linux下多路復用IO接口select/poll的增強版本,它能顯著減少程序在大量並發連接中只有少量活躍的情況下的系統CPU利用率。但是這里我們其實只需要監控的IO接口只有mWakeReadPipeFd一個,即前面我們所創建的管道的讀端,為什么還需要用到epoll呢?有點用牛刀來殺雞的味道。其實不然,這個Looper類是非常強大的,它除了監控內部所創建的管道接口之外,還提供了addFd接口供外界面調用,外界可以通過這個接口把自己想要監控的IO事件一並加入到這個Looper對象中去,當所有這些被監控的IO接口上面有事件發生時,就會喚醒相應的線程來處理,不過這里我們只關心剛才所創建的管道的IO事件的發生。
要使用Linux系統的epoll機制,首先要通過epoll_create來創建一個epoll專用的文件描述符:
- mEpollFd = epoll_create(EPOLL_SIZE_HINT);
傳入的參數EPOLL_SIZE_HINT是在這個mEpollFd上能監控的最大文件描述符數。
接着還要通過epoll_ctl函數來告訴epoll要監控相應的文件描述符的什么事件:
- struct epoll_event eventItem;
- memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union
- eventItem.events = EPOLLIN;
- eventItem.data.fd = mWakeReadPipeFd;
- result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, & eventItem);
這里就是告訴mEpollFd,它要監控mWakeReadPipeFd文件描述符的EPOLLIN事件,即當管道中有內容可讀時,就喚醒當前正在等待管道中的內容的線程。
C++層的這個Looper對象創建好了之后,就返回到JNI層的NativeMessageQueue的構造函數,最后就返回到Java層的消息隊列MessageQueue的創建過程,這樣,Java層的Looper對象就准備好了。有點復雜,我們先小結一下這一步都做了些什么事情:
A. 在Java層,創建了一個Looper對象,這個Looper對象是用來進入消息循環的,它的內部有一個消息隊列MessageQueue對象mQueue;
B. 在JNI層,創建了一個NativeMessageQueue對象,這個NativeMessageQueue對象保存在Java層的消息隊列對象mQueue的成員變量mPtr中;
C. 在C++層,創建了一個Looper對象,保存在JNI層的NativeMessageQueue對象的成員變量mLooper中,這個對象的作用是,當Java層的消息隊列中沒有消息時,就使Android應用程序主線程進入等待狀態,而當Java層的消息隊列中來了新的消息后,就喚醒Android應用程序的主線程來處理這個消息。
回到ActivityThread類的main函數中,在上面這些工作都准備好之后,就調用Looper類的loop函數進入到消息循環中去了:
- 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();
- }
- }
- }
- ......
- }
這里就是進入到消息循環中去了,它不斷地從消息隊列mQueue中去獲取下一個要處理的消息msg,如果消息的target成員變量為null,就表示要退出消息循環了,否則的話就要調用這個target對象的dispatchMessage成員函數來處理這個消息,這個target對象的類型為Handler,下面我們分析消息的發送時會看到這個消息對象msg是如設置的。
這個函數最關鍵的地方便是從消息隊列中獲取下一個要處理的消息了,即MessageQueue.next函數,它實現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;
- }
- }
- ......
- }
調用這個函數的時候,有可能會讓線程進入等待狀態。什么情況下,線程會進入等待狀態呢?兩種情況,一是當消息隊列中沒有消息時,它會使線程進入等待狀態;二是消息隊列中有消息,但是消息指定了執行的時間,而現在還沒有到這個時間,線程也會進入等待狀態。消息隊列中的消息是按時間先后來排序的,后面我們在分析消息的發送時會看到。
執行下面語句是看看當前消息隊列中有沒有消息:
- nativePollOnce(mPtr, nextPollTimeoutMillis);
這是一個JNI方法,我們等一下再分析,這里傳入的參數mPtr就是指向前面我們在JNI層創建的NativeMessageQueue對象了,而參數nextPollTimeoutMillis則表示如果當前消息隊列中沒有消息,它要等待的時候,for循環開始時,傳入的值為0,表示不等待。
當前nativePollOnce返回后,就去看看消息隊列中有沒有消息:
- 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;
- }
如果消息隊列中有消息,並且當前時候大於等於消息中的執行時間,那么就直接返回這個消息給Looper.loop消息處理,否則的話就要等待到消息的執行時間:
- nextPollTimeoutMillis = (int) Math.min(when - now, Integer.MAX_VALUE);
如果消息隊列中沒有消息,那就要進入無窮等待狀態直到有新消息了:
- nextPollTimeoutMillis = -1;
-1表示下次調用nativePollOnce時,如果消息中沒有消息,就進入無限等待狀態中去。
這里計算出來的等待時間都是在下次調用nativePollOnce時使用的。
這里說的等待,是空閑等待,而不是忙等待,因此,在進入空閑等待狀態前,如果應用程序注冊了IdleHandler接口來處理一些事情,那么就會先執行這里IdleHandler,然后再進入等待狀態。IdlerHandler是定義在MessageQueue的一個內部類:
- 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();
- }
- ......
- }
它只有一個成員函數queueIdle,執行這個函數時,如果返回值為false,那么就會從應用程序中移除這個IdleHandler,否則的話就會在應用程序中繼續維護着這個IdleHandler,下次空閑時仍會再執會這個IdleHandler。MessageQueue提供了addIdleHandler和removeIdleHandler兩注冊和刪除IdleHandler。
回到MessageQueue函數中,它接下來就是在進入等待狀態前,看看有沒有IdleHandler是需要執行的:
- // 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);
如果沒有,即pendingIdleHandlerCount等於0,那下面的邏輯就不執行了,通過continue語句直接進入下一次循環,否則就要把注冊在mIdleHandlers中的IdleHandler取出來,放在mPendingIdleHandlers數組中去。
接下來就是執行這些注冊了的IdleHanlder了:
- // 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);
- }
- }
- }
執行完這些IdleHandler之后,線程下次調用nativePollOnce函數時,就不設置超時時間了,因為,很有可能在執行IdleHandler的時候,已經有新的消息加入到消息隊列中去了,因此,要重置nextPollTimeoutMillis的值:
- // 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;
分析完MessageQueue的這個next函數之后,我們就要深入分析一下JNI方法nativePollOnce了,看看它是如何進入等待狀態的,這個函數定義在frameworks/base/core/jni/android_os_MessageQueue.cpp文件中:
- static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj,
- jint ptr, jint timeoutMillis) {
- NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
- nativeMessageQueue->pollOnce(timeoutMillis);
- }
這個函數首先是通過傳進入的參數ptr取回前面在Java層創建MessageQueue對象時在JNI層創建的NatvieMessageQueue對象,然后調用它的pollOnce函數:
- void NativeMessageQueue::pollOnce(int timeoutMillis) {
- mLooper->pollOnce(timeoutMillis);
- }
這里將操作轉發給mLooper對象的pollOnce函數處理,這里的mLooper對象是在C++層的對象,它也是在前面在JNI層創建的NatvieMessageQueue對象時創建的,它的pollOnce函數定義在frameworks/base/libs/utils/Looper.cpp文件中:
- int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {
- int result = 0;
- for (;;) {
- ......
- if (result != 0) {
- ......
- return result;
- }
- result = pollInner(timeoutMillis);
- }
- }
為了方便討論,我們把這個函數的無關部分都去掉,它主要就是調用pollInner函數來進一步操作,如果pollInner返回值不等於0,這個函數就可以返回了。
函數pollInner的定義如下:
- int Looper::pollInner(int timeoutMillis) {
- ......
- int result = ALOOPER_POLL_WAKE;
- ......
- #ifdef LOOPER_USES_EPOLL
- struct epoll_event eventItems[EPOLL_MAX_EVENTS];
- int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
- bool acquiredLock = false;
- #else
- ......
- #endif
- if (eventCount < 0) {
- if (errno == EINTR) {
- goto Done;
- }
- LOGW("Poll failed with an unexpected error, errno=%d", errno);
- result = ALOOPER_POLL_ERROR;
- goto Done;
- }
- if (eventCount == 0) {
- ......
- result = ALOOPER_POLL_TIMEOUT;
- goto Done;
- }
- ......
- #ifdef LOOPER_USES_EPOLL
- for (int i = 0; i < eventCount; i++) {
- int fd = eventItems[i].data.fd;
- uint32_t epollEvents = eventItems[i].events;
- if (fd == mWakeReadPipeFd) {
- if (epollEvents & EPOLLIN) {
- awoken();
- } else {
- LOGW("Ignoring unexpected epoll events 0x%x on wake read pipe.", epollEvents);
- }
- } else {
- ......
- }
- }
- if (acquiredLock) {
- mLock.unlock();
- }
- Done: ;
- #else
- ......
- #endif
- ......
- return result;
- }
這里,首先是調用epoll_wait函數來看看epoll專用文件描述符mEpollFd所監控的文件描述符是否有IO事件發生,它設置監控的超時時間為timeoutMillis:
- int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
回憶一下前面的Looper的構造函數,我們在里面設置了要監控mWakeReadPipeFd文件描述符的EPOLLIN事件。
當mEpollFd所監控的文件描述符發生了要監控的IO事件后或者監控時間超時后,線程就從epoll_wait返回了,否則線程就會在epoll_wait函數中進入睡眠狀態了。返回后如果eventCount等於0,就說明是超時了:
- if (eventCount == 0) {
- ......
- result = ALOOPER_POLL_TIMEOUT;
- goto Done;
- }
如果eventCount不等於0,就說明發生要監控的事件:
- for (int i = 0; i < eventCount; i++) {
- int fd = eventItems[i].data.fd;
- uint32_t epollEvents = eventItems[i].events;
- if (fd == mWakeReadPipeFd) {
- if (epollEvents & EPOLLIN) {
- awoken();
- } else {
- LOGW("Ignoring unexpected epoll events 0x%x on wake read pipe.", epollEvents);
- }
- } else {
- ......
- }
- }
這里我們只關注mWakeReadPipeFd文件描述符上的事件,如果在mWakeReadPipeFd文件描述符上發生了EPOLLIN就說明應用程序中的消息隊列里面有新的消息需要處理了,接下來它就會先調用awoken函數清空管道中的內容,以便下次再調用pollInner函數時,知道自從上次處理完消息隊列中的消息后,有沒有新的消息加進來。
函數awoken的實現很簡單,它只是把管道中的內容都讀取出來:
- void Looper::awoken() {
- ......
- char buffer[16];
- ssize_t nRead;
- do {
- nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer));
- } while ((nRead == -1 && errno == EINTR) || nRead == sizeof(buffer));
- }
因為當其它的線程向應用程序的消息隊列加入新的消息時,會向這個管道寫入新的內容來通知應用程序主線程有新的消息需要處理了,下面我們分析消息的發送的時候將會看到。
這樣,消息的循環過程就分析完了,這部分邏輯還是比較復雜的,它利用Linux系統中的管道(pipe)進程間通信機制來實現消息的等待和處理,不過,了解了這部分內容之后,下面我們分析消息的發送和處理就簡單多了。
2. 消息的發送
應用程序的主線程准備就好消息隊列並且進入到消息循環后,其它地方就可以往這個消息隊列中發送消息了。我們繼續以文章開始介紹的Android應用程序啟動過程源代碼分析一文中的應用程序啟動過為例,說明應用程序是如何把消息加入到應用程序的消息隊列中去的。在Android應用程序啟動過程源代碼分析這篇文章的Step 30中,ActivityManagerService通過調用ApplicationThread類的scheduleLaunchActivity函數通知應用程序,它可以加載應用程序的默認Activity了,這個函數定義在frameworks/base/core/java/android/app/ActivityThread.java文件中:
- public final class ActivityThread {
- ......
- private final class ApplicationThread extends ApplicationThreadNative {
- ......
- // we use token to identify this activity without having to send the
- // activity itself back to the activity manager. (matters more with ipc)
- public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
- ActivityInfo info, Bundle state, List<ResultInfo> pendingResults,
- List<Intent> pendingNewIntents, boolean notResumed, boolean isForward) {
- ActivityClientRecord r = new ActivityClientRecord();
- r.token = token;
- r.ident = ident;
- r.intent = intent;
- r.activityInfo = info;
- r.state = state;
- r.pendingResults = pendingResults;
- r.pendingIntents = pendingNewIntents;
- r.startsNotResumed = notResumed;
- r.isForward = isForward;
- queueOrSendMessage(H.LAUNCH_ACTIVITY, r);
- }
- ......
- }
- ......
- }
這里把相關的參數都封裝成一個ActivityClientRecord對象r,然后調用queueOrSendMessage函數來往應用程序的消息隊列中加入一個新的消息(H.LAUNCH_ACTIVITY),這個函數定義在frameworks/base/core/java/android/app/ActivityThread.java文件中:
- public final class ActivityThread {
- ......
- private final class ApplicationThread extends ApplicationThreadNative {
- ......
- // if the thread hasn't started yet, we don't have the handler, so just
- // save the messages until we're ready.
- private final void queueOrSendMessage(int what, Object obj) {
- queueOrSendMessage(what, obj, 0, 0);
- }
- ......
- private final void queueOrSendMessage(int what, Object obj, int arg1, int arg2) {
- synchronized (this) {
- ......
- Message msg = Message.obtain();
- msg.what = what;
- msg.obj = obj;
- msg.arg1 = arg1;
- msg.arg2 = arg2;
- mH.sendMessage(msg);
- }
- }
- ......
- }
- ......
- }
在queueOrSendMessage函數中,又進一步把上面傳進來的參數封裝成一個Message對象msg,然后通過mH.sendMessage函數把這個消息對象msg加入到應用程序的消息隊列中去。這里的mH是ActivityThread類的成員變量,它的類型為H,繼承於Handler類,它定義在frameworks/base/core/java/android/app/ActivityThread.java文件中:
- public final class ActivityThread {
- ......
- private final class H extends Handler {
- ......
- public void handleMessage(Message msg) {
- ......
- switch (msg.what) {
- ......
- }
- ......
- }
- ......
- }
這個H類就是通過其成員函數handleMessage函數來處理消息的了,后面我們分析消息的處理過程時會看到。
ActivityThread類的這個mH成員變量是什么時候創建的呢?我們前面在分析應用程序的消息循環時,說到當應用程序進程啟動之后,就會加載ActivityThread類的main函數里面,在這個main函數里面,在通過Looper類進入消息循環之前,會在當前進程中創建一個ActivityThread實例:
- public final class ActivityThread {
- ......
- public static final void main(String[] args) {
- ......
- ActivityThread thread = new ActivityThread();
- thread.attach(false);
- ......
- }
- }
在創建這個實例的時候,就會同時創建其成員變量mH了:
- public final class ActivityThread {
- ......
- final H mH = new H();
- ......
- }
前面說過,H類繼承於Handler類,因此,當創建這個H對象時,會調用Handler類的構造函數,這個函數定義在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類的構造函數中,主要就是初始成員變量mLooper和mQueue了。這里的myLooper是Looper類的靜態成員函數,通過它來獲得一個Looper對象,這個Looper對象就是前面我們在分析消息循環時,在ActivityThread類的main函數中通過Looper.prepareMainLooper函數創建的。Looper.myLooper函數實現在frameworks/base/core/java/android/os/Looper.java文件中:
- public class Looper {
- ......
- public static final Looper myLooper() {
- return (Looper)sThreadLocal.get();
- }
- ......
- }
有了這個Looper對象后,就可以通過Looper.mQueue來訪問應用程序的消息隊列了。
有了這個Handler對象mH后,就可以通過它來往應用程序的消息隊列中加入新的消息了。回到前面的queueOrSendMessage函數中,當它准備好了一個Message對象msg后,就開始調用mH.sendMessage函數來發送消息了,這個函數定義在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;
- }
- ......
- }
在發送消息時,是可以指定消息的處理時間的,但是通過sendMessage函數發送的消息的處理時間默認就為當前時間,即表示要馬上處理,因此,從sendMessage函數中調用sendMessageDelayed函數,傳入的時間參數為0,表示這個消息不要延時處理,而在sendMessageDelayed函數中,則會先獲得當前時間,然后加上消息要延時處理的時間,即得到這個處理這個消息的絕對時間,然后調用sendMessageAtTime函數來把消息加入到應用程序的消息隊列中去。
在sendMessageAtTime函數,首先得到應用程序的消息隊列mQueue,這是在Handler對象構造時初始化好的,前面已經分析過了,接着設置這個消息的目標對象target,即這個消息最終是由誰來處理的:
- msg.target = this;
這里將它賦值為this,即表示這個消息最終由這個Handler對象來處理,即由ActivityThread對象的mH成員變量來處理。
函數最后調用queue.enqueueMessage來把這個消息加入到應用程序的消息隊列中去,這個函數實現在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;
- }
- ......
- }
把消息加入到消息隊列時,分兩種情況,一種當前消息隊列為空時,這時候應用程序的主線程一般就是處於空閑等待狀態了,這時候就要喚醒它,另一種情況是應用程序的消息隊列不為空,這時候就不需要喚醒應用程序的主線程了,因為這時候它一定是在忙着處於消息隊列中的消息,因此不會處於空閑等待的狀態。
第一種情況比較簡單,只要把消息放在消息隊列頭就可以了:
- msg.next = p;
- mMessages = msg;
- needWake = mBlocked; // new head, might need to wake up
第二種情況相對就比較復雜一些了,前面我們說過,當往消息隊列中發送消息時,是可以指定消息的處理時間的,而消息隊列中的消息,就是按照這個時間從小到大來排序的,因此,當把新的消息加入到消息隊列時,就要根據它的處理時間來找到合適的位置,然后再放進消息隊列中去:
- 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
把消息加入到消息隊列去后,如果應用程序的主線程正處於空閑等待狀態,就需要調用natvieWake函數來喚醒它了,這是一個JNI方法,定義在frameworks/base/core/jni/android_os_MessageQueue.cpp文件中:
- static void android_os_MessageQueue_nativeWake(JNIEnv* env, jobject obj, jint ptr) {
- NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
- return nativeMessageQueue->wake();
- }
這個JNI層的NativeMessageQueue對象我們在前面分析消息循環的時候創建好的,保存在Java層的MessageQueue對象的mPtr成員變量中,這里把它取回來之后,就調用它的wake函數來喚醒應用程序的主線程,這個函數也是定義在frameworks/base/core/jni/android_os_MessageQueue.cpp文件中:
- void NativeMessageQueue::wake() {
- mLooper->wake();
- }
這里它又通過成員變量mLooper的wake函數來執行操作,這里的mLooper成員變量是一個C++層實現的Looper對象,它定義在frameworks/base/libs/utils/Looper.cpp文件中:
- void Looper::wake() {
- ......
- ssize_t nWrite;
- do {
- nWrite = write(mWakeWritePipeFd, "W", 1);
- } while (nWrite == -1 && errno == EINTR);
- .......
- }
這個wake函數很簡單,只是通過打開文件描述符mWakeWritePipeFd往管道的寫入一個"W"字符串。其實,往管道寫入什么內容並不重要,往管道寫入內容的目的是為了喚醒應用程序的主線程。前面我們在分析應用程序的消息循環時說到,當應用程序的消息隊列中沒有消息處理時,應用程序的主線程就會進入空閑等待狀態,而這個空閑等待狀態就是通過調用這個Looper類的pollInner函數來進入的,具體就是在pollInner函數中調用epoll_wait函數來等待管道中有內容可讀的。
這時候既然管道中有內容可讀了,應用程序的主線程就會從這里的Looper類的pollInner函數返回到JNI層的nativePollOnce函數,最后返回到Java層中的MessageQueue.next函數中去,這里它就會發現消息隊列中有新的消息需要處理了,於就會處理這個消息。
3. 消息的處理
前面在分析消息循環時,說到應用程序的主線程是在Looper類的loop成員函數中進行消息循環過程的,這個函數定義在frameworks/base/core/java/android/os/Looper.java文件中:
- 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();
- }
- }
- }
- ......
- }
它從消息隊列中獲得消息對象msg后,就會調用它的target成員變量的dispatchMessage函數來處理這個消息。在前面分析消息的發送時說過,這個消息對象msg的成員變量target是在發送消息的時候設置好的,一般就通過哪個Handler來發送消息,就通過哪個Handler來處理消息。
我們繼續以前面分析消息的發送時所舉的例子來分析消息的處理過程。前面說到,在Android應用程序啟動過程源代碼分析這篇文章的Step 30中,ActivityManagerService通過調用ApplicationThread類的scheduleLaunchActivity函數通知應用程序,它可以加載應用程序的默認Activity了,而ApplicationThread類的scheduleLaunchActivity函數最終把這個請求封裝成一個消息,然后通過ActivityThread類的成員變量mH來把這個消息加入到應用程序的消息隊列中去。現在要對這個消息進行處理了,於是就會調用H類的dispatchMessage函數進行處理。
H類沒有實現自己的dispatchMessage函數,但是它繼承了父類Handler的dispatchMessage函數,這個函數定義在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,於是,就會調用Handler類的handleMessage函數來處理這個消息,由於H類在繼承Handler類時,重寫了handleMessage函數,因此,這里調用的實際上是H類的handleMessage函數,這個函數定義在frameworks/base/core/java/android/app/ActivityThread.java文件中:
- 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;
- ......
- }
- ......
- }
- ......
- }
因為前面在分析消息的發送時所舉的例子中,發送的消息的類型為H.LAUNCH_ACTIVITY,因此,這里就會調用ActivityThread類的handleLaunchActivity函數來真正地處理這個消息了,后面的具體過程就可以參考Android應用程序啟動過程源代碼分析這篇文章了。
至此,我們就從消息循環、消息發送和消息處理三個部分分析完Android應用程序的消息處理機制了,為了更深理解,這里我們對其中的一些要點作一個總結:
A. Android應用程序的消息處理機制由消息循環、消息發送和消息處理三個部分組成的。
B. Android應用程序的主線程在進入消息循環過程前,會在內部創建一個Linux管道(Pipe),這個管道的作用是使得Android應用程序主線程在消息隊列為空時可以進入空閑等待狀態,並且使得當應用程序的消息隊列有消息需要處理時喚醒應用程序的主線程。
C. Android應用程序的主線程進入空閑等待狀態的方式實際上就是在管道的讀端等待管道中有新的內容可讀,具體來說就是是通過Linux系統的Epoll機制中的epoll_wait函數進行的。
D. 當往Android應用程序的消息隊列中加入新的消息時,會同時往管道中的寫端寫入內容,通過這種方式就可以喚醒正在等待消息到來的應用程序主線程。
E. 當應用程序主線程在進入空閑等待前,會認為當前線程處理空閑狀態,於是就會調用那些已經注冊了的IdleHandler接口,使得應用程序有機會在空閑的時候處理一些事情。
老羅的新浪微博:http://weibo.com/shengyangluo,歡迎關注!