編輯:關於Android編程
先後順序按照拼音排序,無關技術本身。
先簡單地總結一下Looper,MessageQueue,Message和Handler四者之間的關系:
Looper和MessageQueueLooper.prepare()
)。 Handler(非抽象類)hanldeMessage()
處理消息。Hanlder在構造時和一個Looper對象關聯在一起。Handler和Looper是多對一的關系,多個Handler對象可以和同一個Looper對象建立關系,反之則不行。 Message
public final class Looper {
// 成員變量
static final ThreadLocal sThreadLocal = new ThreadLocal();
private static Looper sMainLooper;
final MessageQueue mQueue;
final Thread mThread;
// 成員方法
public static void prepare() {...}
private static void prepare(boolean quitAllowed) {...}
public static void prepareMainLooper() {...}
public static Looper getMainLooper() {...}
public static void loop() {...}
public static Looper myLooper() {...}
public static MessageQueue myQueue() {...}
}
public final class MessageQueue {
Message next() {...}
boolean enqueueMessage(Message msg, long when){...}
}
public class Handler {
//內部接口
public interface Callback {
public boolean handleMessage(Message msg);
}
//處理消息相關方法
public void handleMessage(Message msg) {}
public void dispatchMessage(Message msg) {...}
private static void handleCallback(Message message) {...}
// 構造器相關方法
public Handler() {...}
public Handler(Callback callback) {...}
public Handler(Looper looper) {...}
public Handler(Looper looper, Callback callback) {...}
public Handler(boolean async) {...}
public Handler(Callback callback, boolean async) {...}
public Handler(Looper looper, Callback callback, boolean async) {...}
// 獲取Message相關方法
public final Message obtainMessage(){...}
public final Message obtainMessage(int what){...}
public final Message obtainMessage(int what, Object obj){...}
public final Message obtainMessage(int what, int arg1, int arg2){...}
public final Message obtainMessage(int what, int arg1, int arg2, Object obj){...}
// post相關方法
public final boolean post(Runnable r){...}
public final boolean postAtTime(Runnable r, long uptimeMillis){...}
public final boolean postAtTime(Runnable r, Object token, long uptimeMillis){...}
public final boolean postDelayed(Runnable r, long delayMillis){...}
public final boolean postAtFrontOfQueue(Runnable r){...}
// send相關方法
public final boolean sendMessage(Message msg){...}
public final boolean sendEmptyMessage(int what){...}
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {...}
public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {...}
public final boolean sendMessageDelayed(Message msg, long delayMillis){...}
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {...}
public final boolean sendMessageAtFrontOfQueue(Message msg) {...}
// 進出消息隊列
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {...}
}
public final class Message implements Parcelable {
// 主要成員變量
public int what;
public int arg1;
public int arg2;
public Object obj;
public Messenger replyTo;
private static final Object sPoolSync = new Object();
private static Message sPool;
private static int sPoolSize = 0;
private static final int MAX_POOL_SIZE = 50;
// 主要成員方法
public Message() {} // 不建議使用
// 獲取Message相關方法
public static Message obtain() {...}
public static Message obtain(Message orig) {...}
public static Message obtain(Handler h) {...}
public static Message obtain(Handler h, Runnable callback) {...}
public static Message obtain(Handler h, int what) {...}
public static Message obtain(Handler h, int what, Object obj) {...}
public static Message obtain(Handler h, int what, int arg1, int arg2) {...}
public static Message obtain(Handler h, int what, int arg1, int arg2, Object obj) {...}
public void setTarget(Handler target) {...}
public void sendToTarget() {...}
public Handler getTarget() {...}
}
下面將以問答的方式解析異步消息處理機制。
解析:ThreadLocal是Thread Local Variable即線程本地變量的意思,通過把數據放在ThreadLocal中就可以讓每個線程創建一個該變量的副本,從而避免並發訪問的線程安全問題。這裡保存Looper類的實例對象。
Looper.prepare()
,而在子線程中則需要Looper.prepare()
?解析:這是因為在ActivityThread類中的main()
方法調用了Looper.prepareMainLooper()
,簡單代碼如下:
public static void main(String[] args) {
...// 前面的省略了
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, ActivityThread));
}
Looper.loop();
throw new RuntimeException(Main thread loop unexpectedly exited);
}
可以看到main()
方法中調用了Looper.prepareMainLooper()
,而Looper.prepareMainLooper()
源碼為:
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException(The main Looper has already been prepared.);
}
// 若已經存在Looper對象直接從ThreadLocal對象sThreadLocal中獲取
sMainLooper = myLooper();
}
}
而prepare(boolean)
代碼如下:
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException(Only one Looper may be created per thread);
}
sThreadLocal.set(new Looper(quitAllowed));
}
到這裡我們可以看到main()
方法中調用了Looper.prepareMainLooper()
,而Looper.prepareMainLooper()
又調用了prepare(boolean)
。prepare(boolean)
方法就會創建一個Looper對象並保存在靜態變量sThreadLocal(ThreadLocal類實例)。我們來看一下Looper的構造器:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
該構造器為私有的,且創建了一個消息隊列實例,並將當前線程保存下來。
同時在prepare(boolean)
可以看到若sThreadLocal已經有一個Looper對象後再創建就會出現異常。這說明了兩點:1. 一個線程中只能有一個Looper對象;2. 一個線程中只能最多調用一次prepare()
等prepare相關的方法(prepare()
,prepare(boolean)
,prepareMainLooper()
(UI線程中調用))。
而我們在子線程中調用的是Looper.prepare()
,來看一下這個方法的源碼:
public static void prepare() {
prepare(true);
}
可以看到同樣也是調用了prepare(boolean)
。
解析:該問題主要涉及到Handler中以下幾個方法:
public Handler() {this(null, false);}
public Handler(Callback callback) {this(callback, false);}
public Handler(Looper looper) {this(looper, null, false);}
public Handler(Looper looper, Callback callback) {this(looper, callback, false);}
public Handler(boolean async) {this(null, async);}
public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class 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;
}
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
在上述構造器中可能傳遞的參數為:
Looper對象,用於指定Handler將消息發送到的Looper對象。因此在實例化Handler對象之前需要調用Looper.prepare()
方法。 Callback接口對象,用於處理Handler發送的消息。 boolean值,是否是異步操作。針對Handler來說這裡都是傳遞的false。
首先,在Handler(Callback callback, boolean async)
中我們看到調用了Looper.myLooper()
返回Looper對象,若沒有則會拋出異常。
根據以上構造器,我們能夠有多種方式實例化一個handler對象,前五個構造器最終都會調用下面兩個構造器。對於Handler(Callback callback, boolean async)
這個構造器沒有指定Handler的Looper對象,則使用當前線程的Looper對象。同時我們看到傳遞了Callback接口對象,將其值賦值給了mCallback,我們來看一下相關的源碼:
public interface Callback {public boolean handleMessage(Message msg);}
final Callback mCallback;
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
通過上述代碼我們可以看到,若傳遞Callback接口對象的話,在dispatchMessage(Message msg)
中將不會執行handleMessage(msg)
(當然這之前先判斷msg.callback是否為空,這個問題之後再講),而是調用mCallback.handleMessage(msg)
,而該方法就是我們傳遞的Callback接口對象實現的方法。因此,我們也可以通過這種方式處理消息。若不傳遞Callback接口對象的話,那麼就會執行handleMessage(msg)
,我們可以通過繼承Handler實現該方法處理消息。
Looper.loop()
?為了解決這個問題我們先看一下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) {
// msg等於null,就會退出
return;
}
.....
msg.target.dispatchMessage(msg);
.....
msg.recycleUnchecked(); // 保證消息循環利用。
}
}
在loop()
中首先獲取Looper對象me,然後根據該對象獲取對應的消息隊列queue,下面就進入一個for(;;)
死循環不斷地從queue取消息,若沒有則next()
(該方法比較復雜,我沒怎麼看懂,,,)方法就會阻塞。
若消息為空的話就會退出loop()
;下面有一句:msg.target.dispatchMessage(msg);
,這個消息msg的target屬性就是Handler對象,那麼這個消息就是由該Handler來處理的。我們可以通過Message的重載方法obtainXXX()
和setTarget()
設置Handler對象。
通過loop()
,就能實現不斷處理發送過來的消息。
我們再來看一下Message的sendToTarget()
:
public void sendToTarget() {
target.sendMessage(this);
}
可以看到通過這個方法發送消息和通過Handler發送消息一樣。
sendMessage()
是如何將消息發送到消息隊列中的,而消息隊列又是如何管理消息的?首先,看一下,send
相關方法:
// send相關方法
public final boolean sendMessage(Message msg){...} // 發送的消息希望及時處理,但不插隊
public final boolean sendEmptyMessage(int what){...} // 只發送帶有what屬性的消息,希望消息及時處理即可
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {...} // 希望延遲處理
public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {...} // 希望消息在指定的時間處理
public final boolean sendMessageDelayed(Message msg, long delayMillis){...} // 希望延遲處理
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {...} // 希望消息在指定的時間處理
public final boolean sendMessageAtFrontOfQueue(Message msg) {...} // 消息插隊,希望馬上處理
帶有Empty
的方法甚至發送的不是消息,只是what
!!注意一下。
上面的send
方法,除了sendMessageAtFrontOfQueue(Message)
,都會調用sendMessageAtTime(Message,long)
。而這兩個方法中則會返回消息隊列的enqueueMessage(Message,long)
的方法,該方法就是將消息按照時間順序插入到消息隊列中。其實,消息隊列是用鏈表實現的,排序方式是按照時間進行排列的。enqueueMessage(Message,long)
源碼如下:
boolean enqueueMessage(Message msg, long when) {
....
synchronized (this) {
....
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
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;
}
....
}
return true;
}
可以看到首先通過if
判斷插入的消息的時間是否最小,若是的話直接插入到鏈表頭部,否則進入for (;;)
依次遍歷鏈表直到時間小於鏈表值,然後再插入。消息隊列以mMessages為表頭進行保存。
以上方法就通過Handler的send
方法將消息插入到消息隊列中。此時還是在同一個線程中。
loop()
方法循環檢測是否有消息進入隊列,若有則取出並處理; 在B線程中,通過Handler對象的sendMessage()
方法發送消息,發送過來的消息通過一系列的處理進入到消息隊列,這樣消息就進入了A線程中。
通過上述兩步實現了A線程和B線程之間的通信。
在哪個線程中,又是如何取消息的,怎麼阻塞的?
創建消息的方式有很多種,我們來對比一下:
Handler的obtainMessage()
系列; Message的靜態方法obtain()
系列; 使用構造器創建。
Handler中的方法實際上都是調用Message的靜態方法obtain()
系列,而obtain()
系列最終都會調用obtain()
方法。我們來看一下它的源碼:
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}
可以看到當前存在多余的消息時,則我們可以直接使用,而不是再創建一個新的消息,循環使用消息減少資源浪費。能夠循環使用消息的原因是:每當處理完一個消息時都會調用消息的recycleUnchecked()
該方法就會將使用過的消息進行保存。源碼如下:
void recycleUnchecked() {
... // 清空消息內容
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
sPool是靜態成員變量,保存鏈表的頭,當處理過一個消息時,(消息池大小小於給定值50時)該消息就會采用頭插法插入鏈表中。
post
方法是干什麼用的?我們首先回過頭來看一下dispatchMessage()
方法,其中有一個判斷:
if (msg.callback != null) {
handleCallback(msg);
}
只有當msg.callback
為空時才會執行下面的。消息的callback屬性是Runnable變量,那麼該屬性是在哪裡賦值的呢?我們發現在post
系列中都會調用一個方法getPostMessage()
:
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
可以看到該方法可以對callback屬性進行賦值,也就是說,通過post
方法發送消息的話和send
一樣可以完成處理消息(實際上在post
中也是調用send
方法),只不過不在handleMessage()
中處理消息,而是在handleCallback()
中處理消息。該方法的源碼會讓我們大吃一驚的:
private static void handleCallback(Message message) {
message.callback.run();
}
直接調用了Runnable對象的run()
方法,這樣一來就不是創建新線程,而是直接調用。
當然也可以在消息的obtain()
中設置callback屬性。
post
方法和send
方法的區別post
方法在內部會調用send
方法,並且post
方法發送的是帶有處理方法的消息,而send
方法發送的是不帶有處理方法的消息。前者消息在自己攜帶的方法中處理,後者則只能通過handleMessage()
處理
進程間通信方式在Android開發中我們可以通過Intent、ContentProviders來實現進程間通信,如果不限於Android特有的話,我們還可以使用File、
上拉刷新,即當ListView滾動到底部的時候,再繼續拉取的時候,將出現一個提示告訴你正在加載數據,稍後提示消失,新的數據出現。在這裡,我提供一個想法:ListView自
這個錯誤翻譯的意思是:不能在沒有Looper.prepare的線程裡面創建handler。起初我很疑惑,我根本沒有用到工作線程,也沒有創建handler。報錯的代碼如下:
本文實例講述了Android編程實現長按Button按鈕連續響應功能。分享給大家供大家參考,具體如下:在電商類app的購物車頁面,經常會有這樣的需求:長按“+”按鈕或者“