編輯:關於Android編程
消息隊列,但是內部存儲結構並不是真正的隊列,而是采用單鏈表的數據結構來存儲消息列表。
由於MessageQueue只是一個消息的存儲單元,它不能去處理消息,而Looper就填補了這個功能,Looper會以無限循環的形式去查找是否有新消息,如果有的話就處理消息,否則就一直等待著。
主要作用是將一個任務切換到某個指定的線程中去執行。
系統之所以提供Handler,主要原因就是為了解決在子線程中無法訪問UI的矛盾。
系統為什麼不允許在子線程中訪問UI呢?
因為Android的UI控件不是線程安全的,如果在多線程中並發訪問可能會導致UI控 件處於不可預期的狀態。那竟然不安全,為什麼系統不對UI控件的訪問加上鎖機制呢?
加鎖後會有2個缺點:首先加上鎖機制會讓UI訪問的邏輯變得復雜;其次鎖機制會降低UI訪問的效率,因為鎖機制會阻塞某些線程的執行。第一步:Handler發消息msg(在子線程中發送message,在主線程中創建Handler)
創建Handler完畢後,
通過Handler的post方法將一個Runnable投遞到Handler內部的Looper中去處理,
也可以通過Handler的send方法發送一個消息,這個消息同樣會在Looper中去處理。
其實post方法最終也是通過send方法來完成。
步驟流程: sendMessage(msg)-->sendMessageDelayed(Message msg, long delayMillis)-->sendMessageAtTime(Message msg, long uptimeMillis)-->enqueueMessage(Message msg, long when)
這裡的enqueueMessage方法是調用了MessageQueue的enqueueMessage方法
//1.發消息
handler.sendMessage(msg);//發消息
public final boolean sendMessage(Message msg)
{
//2 sendMessageDelayed
return sendMessageDelayed(msg, 0);
}
//2 sendMessageDelayed
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
//3 sendMessageAtTime
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
//3 sendMessageAtTime
public boolean sendMessageAtTime(Message msg, long uptimeMillis)
{
boolean sent = false;
//this 就是 handler mQueue 就是從Looper中取出來的消息隊列
MessageQueue queue = mQueue;
if (queue != null) {
//handler發消息 把當前的handler對象綁定到Message對象中
msg.target = this;
// 把消息放到消息隊列裡,排序 ,如果你調用sendMessage(msg),uptimeMillis = 0
//4 enqueueMessage
sent = queue.enqueueMessage(msg, uptimeMillis);
}
。。。
return sent;
}
//4 enqueueMessage
final boolean enqueueMessage(Message msg, long when) {
final boolean needWake;
synchronized (this) {
if (mQuiting) {
RuntimeException e = new RuntimeException(
msg.target + " sending message to a Handler on a dead thread");
Log.w("MessageQueue", e.getMessage(), e);
return false;
} else if (msg.target == null) {
mQuiting = true;
}
msg.when = when;
//Log.d("MessageQueue", "Enqueing: " + msg);
//mMessages 是MessageQueue對象裡的屬性
Message p = mMessages;
if (p == null || when == 0 || when < p.when) {
//msg 發的消息
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);//jni
}
return true;
}
第二步:當Handler的send方法被調用時,它最後會調用MessageQueue的enqueueMessage方法將這個消息放入消息隊列中
消息隊列在Android中指的是MessageQueue, MessageQueue主要包含兩個操作插入和讀取
插入 對應的方法為enqueueMessage
enqueueMessage的作用是往消息隊列中插入一條消息
讀取 對應的方法為next
next的作用是從消息隊列中取出一條消息並將其從消息隊列中移除
從enqueueMessage的實現來看,它的主要操作其實就是單鏈表的插入操作。
打插入消息的動作並不復雜,無非是在消息鏈表中找到合適的位置,插入Message節點而已。因為消息鏈表是按時間進行排序的,所以主要是在比對Message攜帶的when信息。消息鏈表的首個節點對應著最先將被處理的消息,如果Message被插到鏈表的頭部了,就意味著隊列的最近喚醒時間也應該被調整了,因此needWake會被設為true,以便代碼下方可以走進nativeWake()。
從next的實現來看,它是一個無限循環的方法,如果消息隊列中沒有信息,那麼next方法會一直阻塞在這裡。當有新消息到來時,next方法會返回這條消息並將其從單鏈表中移除。
第三步:當Handler發送過來的消息message打入到消息隊列後,Looper發現有新消息到來時,就會處理這個消息,最終消息中的Runnable或者Handler的handleMessage方法就會被調用。
Looper(輪詢器),會不停地從MessageQueue中查看是否有新消息,如果有新消息就會立刻處理,否則就一直阻塞在那裡。
我們知道,Handler的工作需要Looper,沒有Looper的線程就會報錯,那麼如何為一個線程創建Looper呢?其實很簡單,通過Looper.prepare()即可以為當前線程創建一個Looper,接著通過Looper.loop()來開啟消息循環
當一個線程運行到某處,准備運作一個Looper時,它必須先調用Looper類的靜態函數prepare(),做一些准備工作。說穿了就是創建一個Looper對象,並把它設置進線程的本地存儲區(TLS)裡。然後線程才能繼續調用Looper類的另一個靜態函數loop(),從而建立起消息處理循環。示意圖如下:
* ActivityThread類中初始化,應用程序運行之前執行,main方法如下:
//主線程
public static final void main(String[] args) {
。。。
//輪詢器的初始化
Looper.prepareMainLooper();
。。。
//輪詢器開始取消息
Looper.loop();
}
* loop方法
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;
}
if (me.mLogging!= null) me.mLogging.println(
">>>>> Dispatching to " + msg.target + " "
+ msg.callback + ": " + msg.what
);
msg.target.dispatchMessage(msg);
if (me.mLogging!= null) me.mLogging.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("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);
}
msg.recycle();
}
}
}
Looper往消息隊列中取消息,其中loop方法是一個死循環,唯一跳出循環的方式是MessageQueue的next方法返回了null
public static final void loop() {
Looper me = myLooper();
MessageQueue queue = me.mQueue;
while (true) {
//沒有消息阻塞
//從消息隊列裡取消息頭
Message msg = queue.next(); // might block
if (msg != null) {
...
打印日志
。。。
處理消息
msg.target.dispatchMessage(msg);
。。
回收消息
msg.recycle();
}
}
}
處理消息
msg.target.dispatchMessage(msg);
public void dispatchMessage(Message msg) {
if (msg.callback != null) {//runOu......
//處理runOnUithread方法
//1
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
//調用handleMessage 主線程
handleMessage(msg);
}
}
//1.
handleCallback(msg);
//2.
private final void handleCallback(Message message) {
message.callback.run();//runnable 跟線程沒關系
}
處理消息過程:
如果MessageQueue的next方法返回了新消息,Looper就會處理這條消息:msg.target.dispatchMessage(msg);,這裡的msg.target是發送這條消息的Handler對象,這樣Handler發送的消息最終又交給它的dispatchMessage方法處理了。但是這裡不同的是,Handler的dispatchMessage方法是在創建Handler時所使用的Looper中執行的,這樣就成功地將代碼邏輯切換到指定的線程中去執行了,最後還是由對應的線程Handler調用handlerMessage方法。
從源碼中我們可以發現,Handler發送消息的過程僅僅是向消息隊列中插入了一條消息,MessageQueue的next方法就會返回這條消息給Looper,Looper收到消息後就開始處理了,最終消息由Looper交由Handler處理。即Handler的dispatchMessage方法會被調用,這時Handler就進入了處理消息的階段。
轉載請注明本文出自大苞米的博客(http://blog.csdn.net/a396901990),謝謝支持! 開篇廢話: 這個項目准備分四部
一般來說,Android應用程序中的Button響應事件有兩種書寫方式 Button button=null; button.setOnClickListener(n
標准VideoView播放示例 在Android SDK中的ApiDemos內, 提供標准播放視頻的代碼,使用VideoView
Android資源文件分類:Android資源文件大致可以分為兩種:第一種是res目錄下存放的可編譯的資源文件:這種資源文件系統會在R.java裡面自動生成該資源文件的I