編輯:關於Android編程
Android Handler消息機制,在Android中扮演了很重要的角色。通過Android Handler,能夠實現延遲處理、子線程更新主線程界面等功能。(Android更新UI,一定需要在主線程中,這個規定的原因是,多個線程同時更新UI會造成UI界面更新混亂,不利於管理,因此,程序在初始化時,主線程中就初始化了UIHandler)。
Handler機制主要涉及到幾個類:
- Handler
- Looper
- ThreadLocal
- MessageQueue
後面將從簡單到復雜分析他們各自的功能,以及之間的關系。
ThreadLocal是一個線程相關的存儲類。ThreadLocal的存儲,獲取數據:
public void set(T value) {
Thread currentThread = Thread.currentThread();
Values values = values(currentThread);
if (values == null) {
values = initializeValues(currentThread);
}
values.put(this, value);
}
public T get() {
// Optimized for the fast path.
Thread currentThread = Thread.currentThread();
Values values = values(currentThread);
if (values != null) {
Object[] table = values.table;
int index = hash & values.mask;
if (this.reference == table[index]) {
return (T) table[index + 1];
}
} else {
values = initializeValues(currentThread);
}
return (T) values.getAfterMiss(this);
}
ThreadLocal中常用的方法主要是這兩個:
- set(T value)方法,傳入一個泛型對象,首先獲取當前線程的對象,然後values(currentThread),這個方法其實獲取currentThread.loacalValues,也就是說每個Thread都有localValues對象。如果這個values為空,則初始化,否則直接put添加這個數據。我們查看Values這個類的put方法:
void put(ThreadLocal key, Object value) {
cleanUp();
// Keep track of first tombstone. That's where we want to go back
// and add an entry if necessary.
int firstTombstone = -1;
for (int index = key.hash & mask;; index = next(index)) {
Object k = table[index];
if (k == key.reference) {
// Replace existing entry.
table[index + 1] = value;
return;
}
if (k == null) {
if (firstTombstone == -1) {
// Fill in null slot.
table[index] = key.reference;
table[index + 1] = value;
size++;
return;
}
// Go back and replace first tombstone.
table[firstTombstone] = key.reference;
table[firstTombstone + 1] = value;
tombstones--;
size++;
return;
}
// Remember first tombstone.
if (firstTombstone == -1 && k == TOMBSTONE) {
firstTombstone = index;
}
}
}
首先cleanup,這個方法會調用rehashed,如果數組容納的空間不夠,會進行空間擴展。然後外層是循環,循環的第一個判斷,如果找到原來設置的key,則直接修改原來key對應的值(保存的時候,是index保存鍵,index+1保存值);循環的第二個判斷,如果沒有找到對應的key,並且firstTombstone=-1,則在最後面保存key和value;第三個判斷,如果k!=null並且firstTombstone=-1和k==TOMBSTONE,那麼firstTomebstone=index,然後在下次循環中,將數據賦值到index這個位置(因為這個位置的數據是被刪除的位置,被刪除的數據會被賦值為TOMBSTONE)。
總結一下ThreadLocal:
1. 每個Thread都有一個Values對象,這個對象用來存儲數據。
1. ThreadLocal操作對應線程的數據,其實是操作在線程的Values中的數據。
1. put方法,會先檢查數組容量,然後將數據保存在數據中,如果其中有廢棄的數據,則會這個位置的數據會被覆蓋。
1. get方法,在線程的values中,查找指定位置的值。這個位置通過ThreadLocal的hash和Values的mask值來計算。
2. ThreadLocal是線程級保存數據的。
它保存了由Looper分發的一系列事件Message,MessageQueue主要用來操作Message消息的,它核心的方法主要是:enqueueMessage,next,quite。
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(TAG, 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;
}
分析一下上面方法的流程:
1. 首先判斷這個Message的Handler是否為空以及消息是否已經在使用。
1. 然後同步方法中,先判斷當前消息隊列是否退出,如果退出了則拋出異常,並將這個消息回收掉。
2. 如果消息隊列沒有退出,則開始將消息添加到隊列中:先將消息設置為正在使用中狀態(makeInUse),如果當前頭結點為空,則將這個消息Message作為頭結點,頭結點的名稱依然還是mMessages,這時,重新將阻塞的線程激活
3. 如果頭結點不為空,則先執行for循環,將p指向到隊列的末尾結點的next結點,最後將Message添加末尾結點的next結點,而Message的下一個結點指向p這個空結點。這樣就把新消息添加到了隊列末尾。
示意圖(將消息插入末尾):

void quit(boolean safe) {
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit.");
}
synchronized (this) {
if (mQuitting) {
return;
}
mQuitting = true;
if (safe) {
removeAllFutureMessagesLocked();
} else {
removeAllMessagesLocked();
}
// We can assume mPtr != 0 because mQuitting was previously false.
nativeWake(mPtr);
}
}
主要是兩個判斷
1. 是否運行退出循環,主線程中的Looper是不允許的。
2. 是否安全退出,非安全退出,則隊列中所有的消息將會因為調用removeAllMessagesLocked()移除;安全退出只會刪除時間大於當前的消息,已經在隊列中可執行的消息不會刪除。
Looper的主要作用是在當前線程中運行一個消息循環隊列。sMainLooper是一個靜態變量,在主線程調用prepareMainLooper時候就會被賦值。因此這個的獲取就很簡單,調用getMainLooper就ok了。
後面主要講講Looper的核心方法,按照Looper的使用方法順序,prepare(),loop(),quite()方法說明。
prepare
public static void prepare() {
prepare(true);
}
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));
}
Looper的初始化直接調用prepare靜態方法,會先new一個Looper對象,然後保存在當前線程中。
loop
public static void loop() {
...
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);
...
}
}
上面是loop方法的核心代碼,主要是通過死循環不斷的調用queue.next()獲取隊列中的消息,並調用dispatchMessage將消息發送給Handler。消息隊列的next()方法當消息隊列中沒有消息後,會阻塞;當調用了quit方法後,next()返回null,然後loop的循環也會退出。總體來說,流程比較簡單。
Handler是發送消息和處理消息的主要參與者。下面主要分析發送消息和處理消息的相關方法:
enqueueMessage
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
可以發現,Handler裡面的sendMessage或者是post相關方法,最終都會調用enqueueMessage方法,最後其實是MessageQueue的enqueueMessage方法,這就回到上面MessageQUeue分析的過程了。
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,如果設置了直接調用callback處理,否則調用調用Handler的Callback或者是調用handleMessage方法處理。
以上就是Android中Handler的源碼分析。接下來我們通過常用的使用方式結合源碼再梳理一遍。
Looper.preprare() Looper.looper Handler handler = new Hander(looper) hanlder.sendMessage handleMessage
上面是整個用法的簡單寫法,Looper保存在prepare方法調用的所在線程中,Handle可以在其他線程中創建,只要能拿到looper對象,Handler的方法也可以在其他線程中調用。如果不指定looper對象,Handler綁定的就是當前線程的Looper對象。Looper對象的loop方法決定了Handler的handleMessage方法處理操作所在的線程。

到此為止,Handler的機制基本就結束了。
MINA框架源碼分析(二)
上一篇我們通過實例學習了MINA框架的用法,發現用起來還是挺方便的,就那麼幾步就可以了,本著學東西必知其原理的觀念,決定看看MINA的源碼實現,好了,我們開始吧!MINA
Android bluetooth介紹(一):基本概念及硬件接口
關鍵詞:藍牙硬件接口 UART PCM blueZ 版本:基於android4.2之前版本 bluez內核:linux/linux3.08系統:android/and
Android 使用Vitamio打造自己的萬能播放器(4)——本地播放(快捷搜索、數據存儲)
前言 關鍵字:Vitamio、VPlayer、Android播放器、Android影音、Android開源播放器 本章節把Android萬能播放器本地播放的主要功能(
Android進階(二十四)Android UI---界面開發推薦顏色
在Android開發過程中,總要給app添加一些背景,個人認為使用純色調便可以達到優雅的視覺效果。補充一些常用的顏色值:colors.xml #ffffff #f