編輯:關於Android編程
Android的消息循環機制主要先關的類有:
Handler Looper Message MessageQueue ActivityThread實際上
應用程序啟動的時候,會創建一個UI線程,然後該線程關聯一個消息隊列,相關操作封裝一個個消息放入隊列中,主線程會不斷循環從隊列中取出消息進行分發處理。
大家都知道,Android規定【訪問UI只能在主線程中進行】,如果在子線程中訪問UI,程序會出現異常。
throw new CalledFromWrongThreadException("only the original thread that created a view hierarchy can touch its views.");
所以只能在主線程中訪問UI,但是Android又不建議在主線程中做耗時操作,比如IO操作、網絡請求等操作,否則容易引起程序無法響應(ANR)。所以想這些耗時操作,都會放到其他的線程中進行處理,但是非UI線程又無法操作UI,所以 Handler 就派上用場了。
Handler的作用就是將一個任務切換到另外一個線程中執行。而我們主要用它來 更新UI 。
先來看看Handler的基本使用。
它的使用方法分為兩套: “post”方法和“send”方法。
private Handler handler2 = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
return false;
}
});
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what){
case 1:
testview.setText("處理完畢");
break;
default:
break;
}
}
};
new Thread(){
@Override
public void run() {
// 一些耗時的操作
handler.sendEmptyMessage(1);
}
}.start();
handler.post(new Runnable() {
@Override
public void run() {
testview.setText("post");
}
});
handler.postDelayed(new Runnable() {
@Override
public void run() {
testview.setText("postDelayed");
}
}, 5000);
/**
* 1. 發送一個消息
*/
public final boolean sendMessage(Message msg){
return sendMessageDelayed(msg, 0);
}
/**
* 2. 發送一個空消息
*/
public final boolean sendEmptyMessage(int what){
return sendEmptyMessageDelayed(what, 0);
}
/**
* 3. 定時發送一個消息
*/
public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageAtTime(msg, uptimeMillis);
}
/**
* 4. 延遲發送一個空的消息
* 內部調用了 sendMessageDelayed() 方法
*/
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageDelayed(msg, delayMillis);
}
/**
* 5. 發送一個消息到消息隊列的頭部
*/
public final boolean sendMessageAtFrontOfQueue(Message msg) {
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, 0);
}
/**
* 6. 定時發送一個消息
*/
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);
}
/**
* 7. 延遲delayMillis時間發送一個消息
*/
public final boolean sendMessageDelayed(Message msg, long delayMillis){
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
通過以上代碼可以發現,重載方法相互調用,最終都是調用了enqueueMessage()方法。
/**
* 1.
*/
public final boolean post(Runnable r){
return sendMessageDelayed(getPostMessage(r), 0);
}
/**
* 2.
*/
public final boolean postDelayed(Runnable r, long delayMillis){
return sendMessageDelayed(getPostMessage(r), delayMillis);
}
/**
* 3.
*/
public final boolean postAtTime(Runnable r, long uptimeMillis){
return sendMessageAtTime(getPostMessage(r), uptimeMillis);
}
/**
* 4.
*/
public final boolean postAtFrontOfQueue(Runnable r){
return sendMessageAtFrontOfQueue(getPostMessage(r));
}
/**
* 5.
*/
public final boolean postAtTime(Runnable r, Object token, long uptimeMillis){
return sendMessageAtTime(getPostMessage(r, token), uptimeMillis);
}
從以上代碼中可以看出,post的這幾個方法都是調用的send的先關方法。只不過 通過getPostMessage()的幾個重載方法,將Runnable封裝成了Message 。
來看看getPostMessage的重載方法:
/**
* 設置消息對象的callback, 返回Message對象
* 當調用dispatchMessage()方法的時候,判斷Message的callback是否為空,不為空時,調用callback的run()方法。
*/
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
/**
* 將Runnable和Object封裝成一個Message對象返回。
*/
private static Message getPostMessage(Runnable r, Object token) {
Message m = Message.obtain();
m.obj = token;
m.callback = r;
return m;
}
一共有兩個,都是通過 Message.obtain() 方法獲取一個消息對象,然後重新對內部變量賦值,然後返回該Message對象。
我們先記下這個方法,稍後進行探索。
綜上,不管是post方法,還是send方法,最後都牽扯到 enqueueMessage 這樣一個方法。
/**
* 將message添加到消息隊列
*/
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this; // 這裡將message的target對象賦為handler
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
該方法內部,將message的target賦值為當前Handler對象,可見 target是Handler類型的對象 。
最後調用了queue.enqueueMessage()對象。
queue是MessageQueue類型的變量 ,表示一個消息隊列。我們先不管該變量什麼時候初始化的,先看看這個方法。
Message mMessages;
/**
* 消息入隊操作
*/
boolean enqueueMessage(Message msg, long when) {
// ...
synchronized (this) {
// ...
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
//當mMessage為空 或者 when是0 後者when小於對頭的when值時,將當前msg作為對頭。
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 {
// 當msg添加到隊列中間。
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;
}
從上面代碼可以看出,消息隊列實際上是通過 單鏈表 的結構來實現的。其內部邏輯也好懂,就是通過對when和當前對頭指針的一些判斷邏輯,進而將參數中的message添加到單單鏈表中。
說到這裡,進行一個小總結:
當我們使用Handler的時候,通過post或者send的一些列方法時,實際上是把一個Message(消息)添加到MessageQueue(消息隊列)中去。
Looper可以稱之為“消息循環”。我們將消息放到消息隊列之後,還需要通過Looper從隊列中取出消息進行處理。
主線程也就是ActivityThread,它被創建的時候,會調用 ActivityThread.main() 方法。
ActivityThread.java 的部分源碼:
static Handler sMainThreadHandler; // set once in main()
public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
//...
Process.setArgV0("");
//初始化當前線程為looper
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"));
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
//開始運行線程中的 消息隊列 -- message queue
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
主要代碼是這兩句:
Looper.prepareMainLooper();
Looper.loop();
這又牽涉到了Looper.java類。
接著往下看。
先把Looper類中的先關代碼貼出來:
// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal sThreadLocal = new ThreadLocal();
private static Looper sMainLooper; // guarded by Looper.class
final MessageQueue mQueue;
final Thread mThread;
//系統調用此方法,初始化當前線程為looper,作為一個應用程序的主looper。
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {//一個線程只允許存在一個looper對象。
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
//Looper構造函數 -- 私有化
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
//返回綁定到當前線程上的Looper對象
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
通過以上代碼,我們可以看出:
Looper類中維護一個消息隊列和一個ThreadLocal類型的對象。
Looper的構造函數是private類型,私有化的。 所以只能通過 prepare(boolean) 方法來進行初始化。
在Looper的構造函數中對MessageQueue進行了初始化。
一個線程只允許存在一個looper對象。否則會出現運行時異常。
當ActivityThread.main方法准備好Looper之後,此時隊列就和線程關聯了起來。,然後調用了Looper的loop()方法。
final MessageQueue mQueue;
final Looper mLooper;
final Callback mCallback;
public static void loop() {
final Looper me = myLooper();
if (me == null) {//如果me是null,則表示沒有調用prepare()方法。
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;// 1. 獲取消息隊列
// ...
for (;;) { // 2. 死循環--消息循環
Message msg = queue.next(); // 3. 獲取消息 (從消息隊列中阻塞式的取出Message)
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
// ...
// Message對象內部有一個Handler對象target。故,實際上調用的handler的dispatchMessage
// 故,實際上調用的handler的dispatchMessage(msg)方法進行分發。
msg.target.dispatchMessage(msg); // 4. 分發處理消息
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
// ...
msg.recycleUnchecked();// 5. 回收消息
}
loop方法實際上建立了一個死循環,一直從消息隊列中讀取消息,然後調用Message的target對象,實際上就是Handler對象的 dispatchMessage(msg) 方法進行處理。
(上面提到了,target實際上是Handler類型的對象)
接著來看Handler.dispatchMessage(msg)
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {// 當message對象中Runnable類型的callback不為空時
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg); // 調用重寫的方法
}
}
當我們使用Handler.postxx()之類的方法時,會傳入一個Runnable對象,這種情況下,“創建”的Message的callback變量不是null,dispatchMessage方法裡面調用 handleCallback(msg) ,然後就調用了run()方法;
// 調用runnable類型的run()方法
private static void handleCallback(Message message) {
message.callback.run();
}
如果不是采用Handler.postxxx()之類的方法,也即callback變量為null,dispatchMessage方法裡面調用handlerMessage(msg);
// 實現該接口時,重寫該方法。
public interface Callback {
public boolean handleMessage(Message msg);
}
/**
* 使用Handler的時候,會重寫該方法。
*/
public void handleMessage(Message msg) {
}
綜上:
通過dispatchMessage(msg)方法的分發處理,就可以將事件在主線程中處理。
上面提到,handler的7+5個方法都是調用的 MessageQueue.enqueueMessage() 方法把消息添加到消息隊列中去。
實際上消息隊列就是在Handler的構造函數中 獲取 的。
看看它的構造方法:
/**
* 無參構造函數
*/
public Handler() {
this(null, false);
}
/**
* Callback就是那個接口
*/
public Handler(Callback callback) {
this(callback, false);
}
/**
* 通過looper對象構造
*/
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);
}
/**
* 進行一些初始化操作
* 可以看出當使用無參的Handler的時候,mCallback對象是null。
*/
public Handler(Callback callback, boolean async) {
// ...
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;// 獲取Looper中保存的消息隊列
mCallback = callback;
mAsynchronous = async;
}
/**
* 指定Looper對象的構造函數很簡單
*/
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
我們最常用的就是無參的構造。
無參構造又調用了 public Handler(Callback callback, boolean async) 這個方法。
該方法內部調用了Looper類的 myLooper() 方法。
然後又從looper中取出messagequeue為Handler裡的mQueue 賦值。
綜上,當調用Handler的send或者post相關方法時,把消息添加進的消息隊列是從Looper對象中獲取的。而Looper的消息隊列是new出來的,是在ActivityThread.main()中調用Looper.prepareMainLooper(),然後調用Looper.prepare(boolean)方法,在這個方法裡面new了一個Looper對象,而Looper的私有構造函數中正好創建了一個消息隊列。
此時,【消息產生->添加到消息隊列->處理消息】 這個流程就走通了。
回過頭來看getPostMessage()的兩個重載方法,內部調用了 Message.obtain() 方法。
該方法用來獲取一個Message對象,而不是直接new一個Message。
// Message采用鏈式存儲結構,內部存儲指向下一個message的“指針”
Message next;
private static final Object sPoolSync = new Object(); //
private static Message sPool; //
private static int sPoolSize = 0; //消息池大小
/**
* 直接從message池中返回一個對象,在多數情況下,都不需要重新new一個對象,
* 從而節省了開銷。
*/
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();
}
看到這段代碼,就發現一個“池”的概念。原來android使用了類似於“線程緩沖池”之類的“消息池”,用來保存一些處理完畢不用的Message對象,以便於下次使用時可以直接從池中獲取,而不是直接創建一個新的message,從而節省了內存開銷。當池中沒有時,才回去new一個新的Message返回。
那既然有從池中獲取的方法,當然也要有將對象放入池中的方法。
在Message類中有 recycler() 和 recyclerUnchecked() 兩個方法。
/**
* 將message對象回收
*/
public void recycle() {
if (isInUse()) { //是否還在使用
if (gCheckRecycle) {
throw new IllegalStateException("This message cannot be recycled because it "
+ "is still in use.");
}
return;
}
recycleUnchecked();
}
/**
* 重置狀態,將消息放到消息池中
*/
void recycleUnchecked() {
// Mark the message as in use while it remains in the recycled object pool.
// Clear out all other details.
flags = FLAG_IN_USE;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
sendingUid = -1;
when = 0;
target = null;
callback = null;
data = null;
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
Message類內部維護一個鏈表來存儲被回收的Message對象,recycler()方法會把不使用的Message對象添加此鏈表中。當調用handler的post系列方法時,會去構建一個message,這個message會優先從消息池中獲取,如果有就復用,沒有就重新創建。
程序啟動的時候,主線程會創建一個Looper對象。Looper對象內部維護一個MessageQueue,然後調用loop()方法循環去讀取消息。
初始化Handler的時候,在Handler的構造函數內部,會獲取當前線程的Looper對象,進而獲取MessageQueue對象。由此可見,想要操作UI的Handler必須在主線程中創建。否則會提示你:【”Can’t create handler inside thread that has not called Looper.prepare()”】
調用Handler的先關方法時,會獲取Message對象,將消息對象的target指向當前handler對象,然後放到消息隊列中。
loop()工作中,會從消息隊列中獲取一個個的消息,調用handle的dispatchMessage(msg)分發處理。
Message內部維護一個消息池,用來回收緩存message對象。
Looper相當於一個發動機,MessageQueue相當於流水線,Message相當於一個個的物品,而Handler就相當於工人。
Java中的內存洩漏java內存洩漏大家都不陌生了,簡單粗俗的講,就是該被釋放的對象沒有釋放,一直被某個或某些實例所持有卻不再被使用導致 GC 不能回收。在Java中,內
Bamboy的自定義Toast,(以下稱作“BToast”) 特點在於使用簡單, 並且自帶兩種樣式: 1)普通的文字樣式; 2)帶圖標樣式。 其中圖標有√和×兩種圖標。B
概述:沒什麼好說的。Demo新建一個自定義Viewpublic class MyPathView extends View { private int width;
上篇博客已經實現了地圖的定位以及結合了方向傳感器用戶路癡定位方向,如果你還不清楚,請查看:Android 百度地圖 SDK v3.0.0 (二) 定位與結合