編輯:關於Android編程
下圖表示了Handler、Looper、MessageQueue、Message這四個類之間的關系。
實際使用中通常在Activity中new Handler()創建一個Handler。之前說過,Handler必須與一個Looper關聯,這個構造方法怎麼沒有給成員變量mLooper賦值?分析以下源碼,可以看到通過調用Looper.myLooper()就可以獲取當前線程的Looper。若獲取不成功,則會拋出RuntimeException,創建Handler失敗。獲取成功後,就會給Handler的成員變量mLooper和mQueue賦值。
關於Looper.myLooper()的源碼分析請參見Looper源碼分析。
public Handler() {
this(null, false);
}
public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
// 這一塊代碼通過反射機制判斷該繼承與Handler的類是不是匿名內部類或成員類或局部類且不為static
// 若以上條件都滿足,則會提示要將該Handler設為static,否則會內存洩露
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();
if (mLooper == null) {
// 若該線程沒有Looper,則Handler無法創建
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
// Handler中的mQueue引用的就是Looper中的mQueue
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
上面兩個Handler構造方法有個不足,在哪個線程調用該構造方法,該Handler就必須與當前線程的Looper進行關聯,所以Handler還提供了另外一個構造方法,該構造方法不管在哪個線程調用,可以與特定的Looper進行關聯,源碼如下:
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
Handle有兩組方法向MessageQueue發送Message。
sendMessage postRunnable分析以下源碼可知,postRunnable先是創建一個Message,並將參數Runnable賦給Message的callback成員變量。兩組方法最終調用的都是sendMessageAtTime。
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;
}
public final boolean sendMessage(Message msg) {
return sendMessageDelayed(msg, 0);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
// 這裡將delay時間轉換為了以boot時間為基准的時間,這樣所有的Message有了同樣的時間基准,從而可以排序
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調用了方法enqueueMessage。方法先將該Handler的引用賦給msg的成員變量target,該target會在消息派發的時候起作用,它標識了該消息應該交給哪個Handler處理。最後調用MessageQueue.enqueueMessage方法。
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
在了解MessageQueue如何處理Message之前,先了解一下Message有哪些重要屬性。
MessageQueue.enqueueMessage源碼如下:
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的target是否為空,是否正在被使用,MessageQueue是否處於quiting狀態,若是則會拋出異常。
以下代碼片段為關鍵邏輯。
if (p == null || when == 0 || when < p.when) {
// 將消息插入鏈表頭。
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;
}
MessageQueue為一個消息鏈表,節點按消息派發時刻先後的順序排列,p為Message鏈表頭。
當MessageQueue為空時(p == null),或消息派發時刻為0(when == 0),或消息派發時刻早於消息鏈表頭消息的派發時刻時,就將消息插入消息鏈表頭。
反之,則遍歷鏈表,尋找插入位置,將該消息插入鏈表。
消息循環Looper.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;
...
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
...
msg.target.dispatchMessage(msg);
...
msg.recycleUnchecked();
}
}
queue.next()取出消息。
msg.target.dispatchMessage(msg)相當於handler.dispatchMessage(msg),派發消息,調用目標handler回調方法dispatchMessage。
消息處理回調方法源碼如下:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
判斷Message的callback是否不為空,當handler使用postRunnable時,參數Runnable就付給了msg.callback,這種情況就不為空,則會執行該callback。
判斷mCallback時候為null,mCallback是Handler的成員變量,是在構造方法裡賦值的,若不為空則執行mCallback.handleMessage(msg)。
當msg.callback和mCallback都為null時,執行handleMessage(msg)。
上述過程正是Android消息機制。
通常在這樣的情況下使用Handler,由於在非UI線程不能執行更新UI操作,所以在非UI線程使用Handler發送更新UI消息,Handler在UI處理消息並執行更新UI操作。這個過程其實是要將方法的調用與方法的執行解耦。
發送更新UI消息相當於方法的調用
執行更新UI操作相當於方法的執行
這正是Active Object模式要解決的問題。
Active Object的概念在《Java編程思想》第4版中有所提及
第21章 並發
21.10 活動對象之所以稱這些對象是“活動的”,是因為每個對象都維護著它自己的工作器線程和消息隊列,並且所有對這種對象的請求都將進入隊列排隊,任何時刻都只能運行其中的一個。
一般情況下,一個對象調用其某個方法,該方法將會在當前線程中執行,即在哪個線程調用就在哪個線程執行,對象完全被動,無法控制方法的執行線程,所以一般的對象稱為被動對象Passive Object。
而主動對象就不同了,它維護著自己的工作線程以及消息隊列,不管在哪個線程調用主動對象的方法,調用請求都會進入隊列排隊,它自己的工作線程從消息隊列中取出調用請求並執行,方法的調用與方法的執行解耦,並且方法的執行線程完全受自己控制,所以稱為主動對象。
以Handler為例,Handler維護了一個Looper和一個消息隊列MessageQueue,Looper決定了Handler在哪個線程處理消息,不管在哪個線程調用Handler.sendMessage(msg),消息處理程序都會在Looper所在線程中執行。
在Handler這個例子中,組成結構對應關系如下:
Proxy -> Handler.sendMessage()
Scheduler -> Looper
ActivationList -> MessageQueue
MethodRequest -> Message
ConcreteMethodRequest -> 不同的消息類型
Servant -> Handler的消息處理方法handleMessage
Android aapt自動打包工具概念在Android.mk中有LOCAL_AAPT_FLAGS配置項,在gradle中也有aaptOptions,那麼aapt到底是干
客戶端獲取後台支付API請求參數的設計參數樣例:{data: {method: 1,platform: 1,version:1.0,relate_orders:B2016
概述本篇博客是對developer.android.com/上的Training課程的簡單翻譯,若是覺得翻譯出來的理解有困難,請點擊下方鏈接查看原文!關於DrawerLa
直接上效果圖 功能特色: 1、可以設置刮開後顯示文字或圖片 2、可以統計已刮開區域所占百分比 下面是源碼:= MV) { // 二次貝塞爾,實現平滑曲線;cX,