Android Handler消息機制分析
Handler是什么?
Handler 是一個可以實現(xiàn)多線程間切換的類,通過 Handler 可以輕松地將一個任務切換到 Handler 所在的線程中去執(zhí)行。我們最常用的使用的場景就是更新 UI 了,比如我們在子線程中訪問網(wǎng)絡,拿到數(shù)據(jù)后我們 UI 要做一些改變,如果此時我們直接訪問 UI 控件,就會觸發(fā)異常了。這個時候我們往往會通過 Handler 將更新 UI 的操作切換到主線程中。
Handler 的基本使用
用法一:通過 send 方法
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private MyHandler mMyHandler = new MyHandler();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new Thread(new Runnable() {
@Override
public void run() {
Message message = Message.obtain(mMyHandler,0,"通過 send 方法");
mMyHandler.sendMessage(message);
}
}).start();
}
private static class MyHandler extends Handler{
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case 0:
Toast.makeText(MainActivity.this,msg.obj.toString(),Toast.LENGTH_SHORT).show();
break;
}
}
}
}
用法二:通過 post 方法
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private Handler mMyHandler = new Handler();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new Thread(new Runnable() {
@Override
public void run() {
mMyHandler.post(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this,"通過post方法",Toast.LENGTH_SHORT).show();
}
});
}
}).start();
}
}
其實,通過 post 方法最后通過 send 方法來完成的。這個我們稍后會分析。講到 Handler,我們不得不提起 MessageQueue 類 和 Looper 類。 Handler 通過 send 方法 發(fā)送一個消息,會調用 MessageQueue 的 enqueueMessage 方法 將這個消息插入到 MessageQueue 中,然后 Looper 發(fā)現(xiàn)有消息來臨時,通過一系列的方法調用后,Handler 如果是通過 post 方法就會執(zhí)行 post 方法里面的 Runnable ,如果是通過 send 方法就會執(zhí)行 Handler 的 handleMessage 。這么說感覺有點云里霧里的,讓我們仔細的來看下 Handler 類、MessageQueue 類和 Looper 類。
Handler 類
我們先來看下 Handler 類的結構

Handler 類結構.png
Handler 的工作主要包括消息的發(fā)送和接收過程。一般來說,消息的發(fā)送和消息的接收是位于不同的線程。我們首先來看 post 方法。
/**
* Causes the Runnable r to be added to the message queue.
* The runnable will be run on the thread to which this handler is
* attached.
*
* @param r The Runnable that will be executed.
*
* @return Returns true if the Runnable was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting.
*/
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
這里調用了 sendMessageDelayed 方法
/**
* Enqueue a message into the message queue after all pending messages
* before (current time + delayMillis). You will receive it in
* {@link #handleMessage}, in the thread attached to this handler.
*
* @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting. Note that a
* result of true does not mean the message will be processed -- if
* the looper is quit before the delivery time of the message
* occurs then the message will be dropped.
*/
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
而 sendMessageDelayed 又調用了 sendMessageAtTime() 方法
/**
* Enqueue a message into the message queue after all pending messages
* before the absolute time (in milliseconds) <var>uptimeMillis</var>.
* <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b>
* Time spent in deep sleep will add an additional delay to execution.
* You will receive it in {@link #handleMessage}, in the thread attached
* to this handler.
*
* @param uptimeMillis The absolute time at which the message should be
* delivered, using the
* {@link android.os.SystemClock#uptimeMillis} time-base.
*
* @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting. Note that a
* result of true does not mean the message will be processed -- if
* the looper is quit before the delivery time of the message
* occurs then the message will be dropped.
*/
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 這個方法我們終于看到了 MessageQueue 類,這里的邏輯主要向 MessageQueue 中插入了一條消息(Message)。咦?我們不是通過 post 方法傳進來的 Runnable 么?什么時候變成 Message 了?其實剛才我們忽略了一個方法。
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
沒錯,就是 getPostMessage 方法
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
從這里看到,系統(tǒng)通過調用 Message.obtain() 創(chuàng)建一個 Message,并把我們通過 post 方法傳進來的 Runnable 賦值給 Message 的 callback。這里的 callback 需要留意,這個在我們之后的分析會用到。接下里我們看 Handler 的 send 方法。
/**
* Pushes a message onto the end of the message queue after all pending messages
* before the current time. It will be received in {@link #handleMessage},
* in the thread attached to this handler.
*
* @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting.
*/
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
是不是很熟悉?post 方法也是調用這個 sendMessageDelayed 方法,這也是為什么我們之前說 post 方法 也是通過 send 方法來執(zhí)行的。到此為止,我們已經(jīng)弄懂 Handler 的消息發(fā)送過程??偨Y的來說,通過 post 方法系統(tǒng)會把 我們傳進來的 Runnable 轉變成 Message,然后就和 send 方法一樣,通過一系列的方法調用之后把 Message 插入到 MessageQueue 當中。至于 Handler 的消息接收過程,我們暫且放一下,先來看 MessageQueue 類。
MessageQueue 類
前面說到,Handler 發(fā)送消息的過程就是往 MessageQueue 中插入 一個 Message,即調用 MessageQueue 的 enqueueMessage 方法。首先,我們來看下 MessageQueue 的類結構

MessageQueue類結構.png
我們看到 MessageQueue 是比較簡單的。其實,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;
}
讀取方法:next
需要注意的是:讀取操作本身會伴隨著刪除操作
Message next() {
// Return here if the message loop has already quit and been disposed.
// This can happen if the application tries to restart a looper after quit
// which is not supported.
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
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 (false) Log.v("MessageQueue", "Returning message: " + msg);
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose();
return null;
}
// If first time idle, then get the number of idlers to run.
// Idle handles only run if the queue is empty or if the first message
// in the queue (possibly a barrier) is due to be handled in the future.
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
// No idle handlers to run. Loop and wait some more.
mBlocked = true;
continue;
}
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// Run the idle handlers.
// We only ever reach this code block during the first iteration.
for (int i = 0; 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("MessageQueue", "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
// Reset the idle handler count to 0 so we do not run them again.
pendingIdleHandlerCount = 0;
// While calling an idle handler, a new message could have been delivered
// so go back and look again for a pending message without waiting.
nextPollTimeoutMillis = 0;
}
}
Looper 類
首先,我們也來看下 Looper 的類結構

Looper類結構.png
關于 Looper ,我們首先要明確一點,Looper 是線程相關的,即每個線程的 Looper 是不一樣的,但是線程默認是沒有 Looper 的??赡軙悬c繞,要理清這里面的邏輯的關系,我們首先要了解 ThreadLocal,關于 ThreadLocal 網(wǎng)上的資料挺多的。簡單地來說,ThreadLocal 是一個線程內部的數(shù)據(jù)存儲類,比如有有一個 int 類型的 x,在線程 A 的值是 1,在線程 B 的值可以是 0,1,2,..,在線程 C 的值可以是 0,1,2... 我們來看下 Looper 相關的源碼
// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
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));
}
/**
* Return the Looper object associated with the current thread. Returns
* null if the calling thread is not associated with a Looper.
*/
public static Looper myLooper() {
return sThreadLocal.get();
}
我們?yōu)槭裁匆鞔_ Looper 是線程相關的呢?因為 Handler 創(chuàng)建的時候會采用當前線程的 Looper 來構造消息循環(huán)系統(tǒng)的。Handler 創(chuàng)建的時候要先創(chuàng)建 Looper,這時候疑問就來了?我們平常創(chuàng)建 Handler 的時候直接就創(chuàng)建了啊,沒有創(chuàng)建什么 Looper 啊。這是因為我們通常是在主線程 ActivityThread 中創(chuàng)建 Handler。我們看到 Loop 類中有個 prepareMainLooper 方法。
/**
* Initialize the current thread as a looper, marking it as an
* application's main looper. The main looper for your application
* is created by the Android environment, so you should never need
* to call this function yourself. See also: {@link #prepare()}
*/
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
主線程在創(chuàng)建時,就會調用這個方法創(chuàng)建 Looper。但是如果我們在子線程(如下代碼)直接創(chuàng)建 Handler 就會拋出異常
new Thread(new Runnable() {
@Override
public void run() {
//Looper.prepare();
Handler handler = new Handler();
// Looper.loop();
}
}).start();
這時只要我們把注釋去掉就不會報異常了。通過源碼我們知道 Looper.prepare() 主要是為當前線程一個 Looper 對象。
/** Initialize the current thread as a looper.
* This gives you a chance to create handlers that then reference
* this looper, before actually starting the loop. Be sure to call
* {@link #loop()} after calling this method, and end it by calling
* {@link #quit()}.
*/
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.loop()方法是干什么的呢?其實,Looper 最重要的一個方法就是 loop 方法了。只有調用 loop 后,消息系統(tǒng)才會真正地起作用。我們來看 loop 方法
/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the 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;
// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
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);
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
// Make sure that during the course of dispatching the
// identity of the thread wasn't corrupted.
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
Log.wtf(TAG, "Thread identity changed from 0x"
+ Long.toHexString(ident) + " to 0x"
+ Long.toHexString(newIdent) + " while dispatching to "
+ msg.target.getClass().getName() + " "
+ msg.callback + " what=" + msg.what);
}
msg.recycleUnchecked();
}
}
我們可以看到 loop 方法是一個死循環(huán),在這個死循環(huán)方法里面會調用 MessageQueue 的 next 方法來獲取新消息。但是如果 next 方法返回了 null,loop 就退出循環(huán)。這種情況發(fā)生在 Loop 的 quit 方法被調用時,Looper 會 調用 MessageQueue 的 quit 方法來通知消息隊列退出,當消息隊列被標記退出狀態(tài)時,它的 next 方法就會返回 null。由于 next 是一個阻塞方法,所以 loop 也會一直阻塞在那里,如果有消息到來, msg.target.dispatchMessage(msg)。這個 msg.target 就是發(fā)送這個消息的 Handler 對象啦。這樣 Handler 發(fā)送的消息最終又交給自己的 dispatchMessage 方法來處理了。因為 Handler 的 dispatchMessage 方法是創(chuàng)建 Handler 時使用的 Looper 中執(zhí)行的,這樣就成功地完成線程切換了。
Handler 的消息接收過程
經(jīng)過跋山涉水,通過 Handler 發(fā)送的消息最終又會回到自己的 diapatchMessage 中來,那就讓我們來看下 diapatchMessage 方法。
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
首先,檢查 Messgae 的 callback 是否為 null,不為 null 就調用 handleCallback 方法,這個 Message 的 callback 就是我們之前post的。其次,檢查 mCallback 是否為 null ,不為 null 就調用 mCallback 的 handleMessage 方法來處理消息。如果我們是通過繼承 Handler 來實現(xiàn)邏輯的話,此時的mCallback 是為空的,即會調用 handleMessage(msg),也就是我們重寫的 handleMessage 方法。至此,完成了完美的閉環(huán)。
有的同學可能會疑問 mCallback 是什么?什么時候會為空?
/**
* Callback interface you can use when instantiating a Handler to avoid
* having to implement your own subclass of Handler.
*
* @param msg A {@link android.os.Message Message} object
* @return True if no further handling is desired
*/
public interface Callback {
public boolean handleMessage(Message msg);
}
/**
* Constructor associates this handler with the {@link Looper} for the
* current thread and takes a callback interface in which you can handle
* messages.
*
* If this thread does not have a looper, this handler won't be able to receive messages
* so an exception is thrown.
*
* @param callback The callback interface in which to handle messages, or null.
*/
public Handler(Callback callback) {
this(callback, false);
}
通過源碼可以看出,我們也可以采用 Handler handler = new Handler(callback) 來創(chuàng)建 Handler,這時dispatchMessage 里面就會走 mCallback 不為空的邏輯。
到此這篇關于Android Handler消息機制分析的文章就介紹到這了,更多相關Android Handler消息機制內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Android開發(fā)實現(xiàn)在Wifi下獲取本地IP地址的方法
這篇文章主要介紹了Android開發(fā)實現(xiàn)在Wifi下獲取本地IP地址的方法,涉及Android編程Wifi的調用及IP地址的獲取與轉換相關操作技巧,需要的朋友可以參考下2017-09-09
uniapp打包Android的apk(原生APP-云打包)及發(fā)布測試全過程
uni-app本地打包apk需要提前做非常多的準備工作,而且可能會勸退一些開發(fā)者,下面這篇文章主要給大家介紹了關于uniapp打包Android的apk(原生APP-云打包)及發(fā)布測試的相關資料,需要的朋友可以參考下2023-02-02
Android ImageView的selector效果實例詳解
這篇文章主要介紹了Android ImageView的selector效果實例詳解的相關資料,需要的朋友可以參考下2017-07-07
android app判斷是否有系統(tǒng)簽名步驟詳解
這篇文章主要為大家介紹了android app判斷是否有系統(tǒng)簽名步驟詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-11-11

