原文連接配接,感謝~
前言
從我們學習android開始,幾乎每天都在和handler打交道.有了它,我們在子線程中處理好了耗時的操作,可以利用它來更新UI.它為我們線上程間的通信提供了很大的友善,而今天部落格就來詳細的介紹一下Handler的消息循環機制,一步一步的了解其中的奧妙,本文不介紹Handler的詳細使用,探究的是内部的原理.是以看這篇部落格的童鞋需要有handler的基本使用能力
本部落格基于Android 6.0源碼講解。
先抛出一個簡單的使用例子
public class DemoAct extends AppCompatActivity {
private Handler h = new Handler() {
@Override
public void handleMessage(Message msg) {
Toast.makeText(DemoAct.this, "收到啦", Toast.LENGTH_LONG).show();
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.act_demo);
}
/**
* 按鈕的點選事件
*
* @param view
*/
public void clickView(View view) {
new Thread(){
@Override
public void run() {
h.sendEmptyMessage();
}
}.start();
}
}
效果圖:
從圖中我們可以看到一個簡單的使用,那麼裡面的實作原理到底是怎麼樣的呢?
下面我們一起來探究!
上圖以在主線程中做了一個圖解,畫出了大緻的流程
圖檔畫的是不是很棒?以後請叫我神筆馬良~~~(臉皮厚模式)
MessageQueue和Looper的介紹
在Android中,一個線程可以對應一個Looper對象,不能有多個,為什麼說可以呢,Looper作為一個消息的循環器,在一個線程中可以使用它也可以不使用它,是以一個線程中可以有一個Looper對象不能有多個.
說到了消息的循環器,就必須掰扯掰扯所謂的消息隊列MessageQueue.每一個Looper對象裡面都會維護一個消息隊列MessageQueue,它用來存放消息(Message),在MessageQueue中,存放的消息按照FIFO(先進先出)原則執行。
Handler的介紹
Handler如何發送消息
Handler是我們發送消息和處理消息的一個類,那麼在上述的圖解中,到底是如何實作發送消息的呢?
public final boolean sendEmptyMessage(int what)
{
return sendEmptyMessageDelayed(what, );
}
上面是發送一空消息的源碼,可以看到調用了另一個方法,那麼點進去~~~
public final boolean sendEmptyMessageDelayed(int what,long delayMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageDelayed(msg, delayMillis);
}
在這裡我們可以明确的看到,在我們發送消息的時候,如何發送的不是一個Message對象,而是一個空消息,那麼它也會自動為我們建立一個Message對象,說到底其實最後發送出去的肯定是一個Message對象
繼續點進去
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < ) {
delayMillis = ;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
這個方法中對延時的時間毫秒值進行了判斷,因為時間永遠不會倒流的嘛~~~
繼續點
“`
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);
}
在這個方法中對一個成員變量進行了為空的判斷,但是并不抛出,而是将異常列印一下,那麼這個成員變量又是什麼呢?
其實他就是這個handler所線上程的Looper中的消息隊列!咦,你之前不是說消息隊列在Looper裡面麼?怎麼在Handler裡面也有一份啊?
瞧瞧handler的構造函數便可知道!
public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class
從構造函數中我們可以看到,從Looper裡面拿到目前線程的Looper對象,然後從裡面拿出來消息隊列,是以這裡就解釋了上面那個成員變量的問題
那麼繼續點之前的方法
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
到這裡位置,消息總算被送進了消息隊列中,上面講述了Handler是如何發送消息的。
####Handler是如何處理消息的
我們在上面的流程圖中可以看出,取出消息是誰幹的?是Looper,沒錯就是它,是以我們就去看Looper的代碼
Looper.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();
}
}
從這個方法的注釋來看,我們就可以知道這個方法的功能:
也就是此方法開啟了一個死循環來拿出每一個消息,拿出來之後,通過消息對象中的target對象(其實就是之前發送消息的Handler的一個引用)的dispatchMessage(Message msg)方法來分發(處理)消息,貼出分發(處理)的代碼:
```
/**
* 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);
}
}
代碼很簡單,判斷了兩個callback(後面解釋),都為null的話就調用handleMessage(msg),此方法就是我們使用handler的時候最常用的方法了,上面的示例代碼中就是重寫了這個方法,可以回頭看看~~~
handler中的callback是什麼?
在Handler源碼中有如下一個接口:
/**
* 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);
}
在Message對象中有這麼一段:
/*package*/ Bundle data;
/*package*/ Handler target;
/*package*/ Runnable callback;
是以上面提到的在分發(處理)消息的時候的mCallback其實就是一個接口,接口的方法也是handleMessage.
那還有一個是Message對象中的callback,這個是一個Runnable接口
請注意:這個雖然是Runnable接口,但是别看這個是線程中經常用到的接口,你就認為這裡面可以處理耗時的操作,這裡是不允許的,否則會阻塞主線程
是以你建立Handler的時候就可以為所欲為了:
1.使用Handler中的Callback接口
private Handler h = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
return false;
}
});
這裡的傳回值是控制消息是否繼續傳遞給handler中的handleMessage方法執行
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
從消息的分發(處理)這裡可以看到,如果傳回了true,那麼handler中的handleMessge方法是不會被執行的
2.使用Message對象中的callback
Message m = Message.obtain(h, new Runnable() {
@Override
public void run() {
//做一些事情,這個run方法是主線程調用的
}
});
h.sendMessage(m);
這就是Handler處理消息的過程,你是否對Handler的發送和處理消息有一定的了解了呢?
一些大家應該比較想問的問題
消息循環器,我并沒有開啟,也就是并沒有在主線程中調用Looper.loop,為什麼主線程中的消息循環器就有作用呢?
答案在這:
public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
SamplingProfilerIntegration.start();
// CloseGuard defaults to true and can be quite spammy. We
// disable it here, but selectively enable it later (via
// StrictMode) on debug builds, but using DropBox, not logs.
CloseGuard.setEnabled(false);
Environment.initForCurrentUser();
// Set the reporter for event logging in libcore
EventLogger.setReporter(new EventLoggingReporter());
AndroidKeyStoreProvider.install();
// Make sure TrustedCertificateStore looks in the right place for CA certificates
final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
TrustedCertificateStore.setDefaultUserDirectory(configDir);
Process.setArgV0("<pre-initialized>");
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);
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
這是ActivityThread類中的main方法,可以看到裡面幫我們初始化了Looper
對應的代碼是:Looper.prepareMainLooper()
開啟了消息循環器
對應的代碼是:Looper.loop();
建立多個Handler來處理消息,為什麼可以區分開,而不幹擾
在發送消息的時候,有這麼一段:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
可以很清楚的看到,消息對象Message對象的target就是這個發送消息的handler,是以處理消息的時候就是根據這個target找到原來的那個handler,然後交由它來處理
Looper中的loop()方法是死循環,為什麼沒有卡死
我用紅框框框住的就是從消息隊列中擷取下一個消息,後面有一句注釋,說這個方法可能會阻塞,是以在沒有消息的時候是會阻塞的
有的人可能還會有疑問:
那我沒有發送消息,一個app中都沒有發送任何handler消息,那為啥app還是正常走,也沒有卡死
答:你沒有發送消息,不代表系統中沒有發送消息,在Android中使用了大量的Handler消息機制。
以上。
筆記:
1.handler處理消息的時候
/**
* 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);
}
}
這2個分别是什麼時候處理?
第一個callback是msg的calback,是一個Runable,第二個callbak是Handler 中的一個interface。
Handler handler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
return false;
}
});
handler.post(new Runnable() {
@Override
public void run() {
}
});
點開post()
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), );
}
再點開getPostMessage(r):
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
這個時候我們初始化Runnable。
2. 建立Handler對象必須先初始化一個Looper
否則會出現如下錯誤:
Activity在被建立的時候,ActivityThread.java(package android.app;)會幫我們初始化一個Looper對象,是以在主線程中,我們不必去調用Looper.prepare()去初始化Looper對象。
這裡順便了解到 Toast為何必須在主線程或者初始化了Looper的線程中執行show操作 ,也是一個意思。具體的可以看看該文。