這篇文章主要介紹了Java中Handler源碼的示例分析,具有一定借鑒價值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。
站在用戶的角度思考問題,與客戶深入溝通,找到華容網(wǎng)站設(shè)計與華容網(wǎng)站推廣的解決方案,憑借多年的經(jīng)驗,讓設(shè)計與互聯(lián)網(wǎng)技術(shù)結(jié)合,創(chuàng)造個性化、用戶體驗好的作品,建站類型包括:成都做網(wǎng)站、網(wǎng)站設(shè)計、企業(yè)官網(wǎng)、英文網(wǎng)站、手機端網(wǎng)站、網(wǎng)站推廣、域名注冊、網(wǎng)站空間、企業(yè)郵箱。業(yè)務(wù)覆蓋華容地區(qū)。從很早開始就認識到 Handler 了,只不過那時修為尚淺,了解的不夠深刻,也沒有應(yīng)用自如。不過隨著工作時間的增長,對 Handler 又有了更深層次的認識,于是有了這篇博客,希望盡可能的總結(jié)出多的知識點。
Handler 在 Java 層源碼主要有 4 個類:Looper、MessageQueue、Message、Handler。我歸納了他們的幾個主要知識點:
Looper:sThreadLocal、Looper.loop();
Message:數(shù)據(jù)結(jié)構(gòu)、消息緩存池;
MessageQueue:enqueueMessage、next、管道等待、同步消息隔離、idleHandler;
Handler:send/post、dispatchMessage 消息處理優(yōu)先級。
static final ThreadLocal sThreadLocal = new ThreadLocal();
private static Looper sMainLooper; // guarded by Looper.class
final MessageQueue mQueue;
// sThreadLocal
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) { throw Exception ... }
sThreadLocal.set(new Looper(quitAllowed));
}
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
// sMainLooper
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) { throw Exception ...}
sMainLooper = myLooper();
}
}
public static Looper getMainLooper() {
synchronized (Looper.class) {
return sMainLooper;
}
}
// mQueue
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
public static @NonNull MessageQueue myQueue() {
return myLooper().mQueue;
}
sThreadLocal:靜態(tài)常量,保證一個線程只有一個 Looper;
sMainLooper:靜態(tài)變量,在 prepareMainLooper 中賦值當前線程 Looper;
mQueue:變量,Looper 構(gòu)造函數(shù)中初始化,因為一個線程只有一個 Looper,所以也同樣只有一個 mQueue。
通過以上分析,我們可以總結(jié)出一下特性:
Looper、MessageQueue 是線程唯一的;
一個進程只有一個 sMainLooper;
根據(jù) ThreadLocal 的特性,可通過 myLooper 方法獲取當前線程的 Looper。
public static void loop() {
final Looper me = myLooper();
final MessageQueue queue = me.mQueue;
for (;;) {
Message msg = queue.next();
...
msg.target.dispatchMessage(msg);
...
msg.recycleUnchecked();
}
}
Looper.loop() 方法雖然看起來很多,其實他主要就做了三件事:
從消息隊列中獲取下一個消息;
msg.target 就是 handler,通過 dispatchMessage 方法把消息分發(fā)下去,這個方法下面會有說到;
消息回收,放到消息緩存池里。這里需要注意的是 Message 對象并沒有釋放,會緩存起來。
public int what, arg1, arg2;
public Object obj;
public Messenger replyTo;
int flags;
long when; // 消息發(fā)送時間
Bundle data;
Handler target;
Runnable callback;
Message next;
private static final Object sPoolSync = new Object();
private static Message sPool;
private static int sPoolSize = ;
private static final int MAX_POOL_SIZE = 50;
看到 next 變量,我們會想到單鏈表,其實 Message 就相當于單鏈表的 node,MessageQueue 就是一個單鏈表了,會持有表頭的引用;
what、arg1、arg2、obj、data 就是我們發(fā)送的一些信息;
值得注意的是 target,他是 Handler 類型,就是本消息的 Handler,會在 Handler 發(fā)送消息的時候賦值;
后面的四個對象,都是和消息緩存池有關(guān)的。
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = ; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}
void recycleUnchecked() {
flags = FLAG_IN_USE;
what = ;
arg1 = ;
arg2 = ;
obj = null;
replyTo = null;
sendingUid = -1;
when = ;
target = null;
callback = null;
data = null;
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
事實上緩存池的數(shù)據(jù)結(jié)構(gòu)也是一個鏈表,sPool 為鏈表頭引用,大容量為 50;
回收消息時,會把消息里面所有參數(shù)重置,并把當前消息設(shè)為鏈表頭;
獲取消息時,返回當前鏈表頭,并把 next 置空。
boolean enqueueMessage(Message msg, long when) {
synchronized (this) {
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == || when < p.when) {
// 作為表頭,如果隊列是阻塞狀態(tài)則需要喚醒
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// 根據(jù)時間順序,插入鏈表中間
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; // 插入消息
prev.next = msg;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
主要作為插入隊列的方法,有下列幾個特性:
把消息加入消息隊列,如果當前表頭為空,則把消息作為表頭引用;如果不為空,則會根據(jù)時間的順序,插入到對應(yīng)的時間中;
nativeWake 是調(diào)用底層在管道中寫操作以喚醒,在隊列不是阻塞的狀態(tài)下是不需要喚醒的;
另外注意其中用了 synchronized 關(guān)鍵字,說明消息隊列的插入是線性安全的,刪除也是線性安全的,之后我們會說到。
for (;;) {
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
// 如果有同步消息隔離,則會優(yōu)先查找異步消息
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
// 計算距離下一個消息的時間
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Got a message.
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
// 沒有更多消息的時候,nextPollTimeoutMillis 會置為 1。
nextPollTimeoutMillis = -1;
}
...
}
// 如果目前沒有消息,已經(jīng)處在空閑狀態(tài),則執(zhí)行 idler.queueIdle
for (int i = ; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // release the reference to the handler
boolean keep = false;
try {
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
...
}
此方法會從消息隊列中讀取下一個消息返回,主要做了以下操作:
nativePollOnce 函數(shù)會調(diào)用底層管道操作函數(shù),nextPollTimeoutMillis 為 -1 時,會阻塞,為 0 時不會阻塞,大于 0 時,會阻塞相應(yīng)的時間;
如果有同步消息隔離,則會優(yōu)先查找異步消息;
獲取當前時間隊列的消息,并返回;
如果隊列沒有任何消息,則會執(zhí)行 idler.queueIdle,通知監(jiān)聽者當前隊列處于空閑狀態(tài)。
上面我們有提到了同步消息隔離,這里我們介紹一下。同步隔離,有時候也可以叫異步消息,說的是一個意思。在源碼中主要用于優(yōu)先更新 UI。
private IdleHandler[] mPendingIdleHandlers;
public int postSyncBarrier() {
return postSyncBarrier(SystemClock.uptimeMillis());
}
private int postSyncBarrier(long when) {
// 向消息隊列中加入一個 handler 為空的消息
synchronized (this) {
final int token = mNextBarrierToken++;
final Message msg = Message.obtain();
msg.markInUse();
msg.when = when;
msg.arg1 = token;
Message prev = null;
Message p = mMessages;
if (when != ) {
while (p != null && p.when <= when) {
prev = p;
p = p.next;
}
}
if (prev != null) { // invariant: p == prev.next
msg.next = p;
prev.next = msg;
} else {
msg.next = p;
mMessages = msg;
}
return token;
}
}
如上 postSyncBarrier 函數(shù)中會向消息隊列中加入一個 handler(即 Message 的 target) 為空的消息作為標識。在我們上面 MessageQueue.next() 的函數(shù)中,當 msg.target == null 時,會優(yōu)先獲取異步消息并返回。
因此想要使用異步消息有兩個條件:
消息為異步消息,即 msg.isAsynchronous() 返回 false;
需要獲取當前隊列并運行 postSyncBarrier() 函數(shù)。
Handler 還提供了消息隊列空閑狀態(tài)通知。
private final ArrayList mIdleHandlers = new ArrayList();
public void addIdleHandler(@NonNull IdleHandler handler) {
if (handler == null) {
throw new NullPointerException("Can't add a null IdleHandler");
}
synchronized (this) {
mIdleHandlers.add(handler);
}
}
public void removeIdleHandler(@NonNull IdleHandler handler) {
synchronized (this) {
mIdleHandlers.remove(handler);
}
}
IdleHandler 的源碼比較簡單,就是一個 ArrayList,然后進行增加刪除操作。注意,這個也是線性安全的。
public final boolean post(Runnable r){
return sendMessageDelayed(getPostMessage(r), );
}
public final boolean sendMessage(Message msg){
return sendMessageDelayed(msg, );
}
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
sendMessage 和 post 最本質(zhì)的區(qū)別是之后處理任務(wù)時的優(yōu)先級,post 會處理 Runnable 中的任務(wù),而 sendMessage 會回調(diào)給 handler 處理;
他們最終都會走 enqueueMessage 方法,并設(shè)置當前 Handler 為 msg.target。
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
任務(wù)執(zhí)行時就會運行這個函數(shù),主要是一個優(yōu)先級的問題:
callback 優(yōu)先級最高,也就是 post 發(fā)送的消息
mCallback.handleMessage(msg),優(yōu)先級第二
handleMessage(msg),優(yōu)先級第三
感謝你能夠認真閱讀完這篇文章,希望小編分享的“Java中Handler源碼的示例分析”這篇文章對大家有幫助,同時也希望大家多多支持創(chuàng)新互聯(lián),關(guān)注創(chuàng)新互聯(lián)-成都網(wǎng)站建設(shè)公司行業(yè)資訊頻道,更多相關(guān)知識等著你來學(xué)習(xí)!