咱們平時在開發中,常常用到Handler,用來發送消息,處理消息。 或者作一些延遲發送消息,跨線程發消息,或者更新UI,或者去實現一些定時輪詢的操做。 安卓發展至今,已經有不少框架,能夠代替這樣原生Handler的通訊方式。 好比Eventbus, Rxjava,AyncTask...等等。可是實際上底層依然是對Handler的封裝。那麼Handler到底是什麼?java
簡而言之:面試
Handler就是Android系統提供給咱們,用來更新UI的一套機制,也是一套消息處理機制。經過Handler,能夠用來發送消息,處理消息。若是不遵循這樣的機制,就沒有辦法更新UI,會拋出異常。bash
1.Handler: 消息的發送和處理者框架
2.Message: 消息async
3.MessageQueue: 消息存放的隊列ide
4.Looper : 從消息隊列裏面一條一條取消息的消息偵聽器,或者消息泵oop
5.線程: 當前是在哪一個線程post
也許不少人在網上都看過不少資料去解釋這樣一個消息機制,可是若是向別人闡述這樣一個原理的時候,或者面試的時候,我相信不少人仍是模棱兩可,弄不太清楚。 那麼,經過一個簡單的生活案例,來幫助理解一下:ui
eg: 咱們都知道讀書的時候,咱們常常須要向老師請假之類。this
好比:報告老師,我要上洗手間。而後老師說:容許,快去吧。
過了一會。
又給老師報告:報告老師,後面有同窗踢我凳子。老師說:你先坐前面來,不要理他。
固然,這只是比喻。咱們經過這個比喻,來比較生動形象的去理解handler就容易多了。
1.handler:學生。
2.message:報告老師的內容
3.Looper :老師本身
4.messagequeue: 老師的耳朵和聽力記憶
解釋:
學生(handler)向老師(Looper)舉手
說 「我要上洗手間」這個報告(message),
而後老師的耳朵(messagequeue)聽到了這個報告,
先是反饋了學生的請求(dispatchHandle),告訴這個同窗,贊成他的請求,
因而學生本身就上洗手間去了(handleMessage)
ps:
老師:
在這個過程當中,就是一個消息的接受者,源源不斷的接受學生的各類報告或者消息,存在老師的記憶裏,
老師又沒有分身術,只有一張嘴,因此同一時間,只能根據記憶中的消息,一條一條的反饋給學生,處理他們的請求。
學生:
在這個過程當中,咱們能夠發現,學生既是消息的發出者,又是消息的處理者。
收到老師的贊成後,因而就高高興興的去上洗手間去了。
複製代碼
整個過程如圖:
由圖能夠看出:
1.handler負責發送消息,接收處理消息
2.Looper負責接收handler發過來的消息
3.MessageQueue就作爲Looper消息泵內部的消息容器 3.Looper在內部,將發送過來的消息,交給自身的消息隊列,並按時間順序加入其中 4.Looper在內部,經過不斷循環的方式,將消息從隊列中一個一個取出,回傳給handler
此時你有可能會問:那麼Handler它怎麼知道應該往哪發消息,而且發給哪一個Looper,加入這個Looper的消息隊列呢?答案在構造方法中。
Handler的建立,調用Handler的構造方法便可。 new Handler(). 那麼咱們看下Handler的構造方法都作了些什麼呢?Handler的構造方法有好幾個,咱們先從空構造看起:
//從這開始看起,空構造
public Handler() {
this(null, false);
}
public Handler(Callback callback) {
this(callback, false);
}
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);
}
//而後看到這個構造方法
public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
//構造方法內部,有一個默認的looper對象,而且這個對象是經過myLooper獲得,也就是當前線程的looper。
//當前線程:默認狀況就是主線程,爲何會是主線程,後面會講
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
複製代碼
上面代碼能夠看出,在handler的默認構造器中,有一句:mLooper=Looper.myLooper(). 這說明Handler 默認的時候,有一個Looper對象。可是這個Looper對象是怎麼建立得來的呢?點擊去發現:就是根據當前線程返回一個looper對象。當前線程是什麼呢?默認狀況下,就是主線程,也就是MainLooper.
/**
* Return the Looper object associated with the current thread. Returns
* null if the calling thread is not associated with a Looper.
*/
//解釋一下:根據當前線程,返回looper對象。若是線程沒有與之關聯的Looper,那麼返回空
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
複製代碼
好了,看到這裏,咱們就看到了Handler和一個默認looper對象如何關聯的。至於looper對象的如何建立,後面再詳細講解。
咱們能夠得出結論:
默認狀況下:new Handler()中的looper對象是主線程的looper對象 ,那麼消息就是發給主線程的handler進行處理。
須要和特定線程的Handler通訊,咱們就須要調用new Handler(Looper looper) ,傳入特定線程的Looper對象便可。
Looper對象是屬於什麼線程,那麼handler,就是往哪一個線程進行發送消息和處理消息。
搞清楚Handler和Looper如何進行關聯的關係之後,咱們從消息的發送開始理解。
Handler 如何發送消息,消息從哪裏產生? 要搞清楚消息的產生,咱們首先要知道handler發送一條空消息是sendEmptyMessage().那麼咱們就跟蹤這個方法: 方法的參數what ,就是一個標記位,先無論它。
/**
public final boolean sendEmptyMessage(int what)
{
return sendEmptyMessageDelayed(what, 0);
}
複製代碼
再繼續往下跟蹤
//這裏能夠看到,sendEmptyMessage()默認會調用sendEmptyMessageDelayed()方法
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
//內部,經過Message.obtain(),產生了一條消息Message
Message msg = Message.obtain();
msg.what = what;
//而後再調用sendMessageDelayed()方法
return sendMessageDelayed(msg, delayMillis);
}
複製代碼
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
複製代碼
這裏能夠看到發送空消息時,Message是在方法內部,經過Message.obtain產生的。 而且會層層調用,最終是這麼一個方法路徑:
sendEmptyMessage-->sendMessageDelay-->sendMessageAtTime ,最終都是調用sendMessageAtTime()來發送消息的。
經過上面sendMessageAtTime()發送消息,消息產生後,按照前面的圖的理解,它是會發送出一個Messgae,而且發給Looper,由Looper加入到消息隊列的。那麼代碼上是怎樣的呢?
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
//1.首先獲取到queue對象,
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
//2. 將消息,插入到這個消息queue隊列中去,而且附帶了一個時間值
return enqueueMessage(queue, msg, uptimeMillis);
}
複製代碼
咱們能夠看出:
在最終發送消息的地方,拿到了Queue隊列對象,而後就把消息加入到了這個Queue隊列中去了。 這個Queue對象的獲取,咱們能夠觀察handler的構造看到:每一次Handler建立的時候,會拿到looper對象,再經過looper對象來獲取到內部的這個Queue隊列對象。回顧handler的建立可發現,以下:
public Handler(Callback callback, boolean async) {
....
....
//1先拿到線程的looper
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
//2經過looper獲取到消息queue隊列
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
複製代碼
再看下消息加入queue隊列的過程
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
//這裏能夠發現 消息msg的target屬性:是this,也就是handler 自身當前對象
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
複製代碼
在加入隊列的時候,能夠看到消息msg的地址,也就是處理者target(由誰來最終處理消息),賦值爲this。
這也就說明了,handler默認狀況下,發送消息的是它本身,處理消息的也是它本身。
總結一下上面消息的發送過程:
發送出去 sendEmptyMessage(),消息的產生在這個裏面
調用sendEmptyMessageDelay()
內部調用sendMessageDelay()
內部調用sendMessageAtTime()
內部經過當前線程,首先獲取到mQueue對象,也就是 消息隊列對象MessageQueue
當queue不爲空的狀況下,設置 target,發送給誰, 默認是this --handler本身
而後把消息message,放到消息隊列中。queue.enqueueMessage(消息,時間);
咱們知道,消息發送到Looper中後,就進入到了隊列中,而後等待Looper的循環取出進行分發。那麼Looper.loop() 這個循環的過程是在何時觸發的呢?因爲Android默認的線程是主線程,因此在應用進程啓動以後,就會進入ActivityThread這個主線程中,而在這個ActivityThread中的main()方法,就會觸發Looper.loop()開始偵聽主線程的消息。
//main方法
public static void main(String[] args) {
...
...
...
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);
//1. 觸發loop
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
複製代碼
能夠看到,在主線程的ActivityThread main()方法內部,觸發Looper.loop()。學過java的人都知道,main方法是一個類的主入口.
那麼loop是如何進行取消息的呢?點進去繼續往下看
public static void loop() {
// 1.拿到當前線程looper對象
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
2. 獲得 looper對象內部的消息隊列queue
final MessageQueue queue = me.mQueue;
...
...
//3.死循環,配合隊列進行取消息
for (;;) {
//4.取出一條消息
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
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
final long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
final long traceTag = me.mTraceTag;
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
final long end;
try {
//4.分發消息
msg.target.dispatchMessage(msg);
end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
}
}
複製代碼
能夠看到:
2.經過myLooper,拿到一個Looper對象
3.取出looper中的消息隊列 mQueue
4.而後經過一個死循環,裏面經過mQueue.next來取消息 若是消息爲空了,就return掉,
5.若是消息不爲空, 就會調用 msg.target.dispatchMessage(消息)。將消息發出去
msg.target:就是handler本身 handler.dispatchMessage(消息)方法,將消息回調到dispatch中去
經過上面Looper取消息的過程。咱們看到了。消息在最終取出後,會經過 dispatchMsg進行回傳,交給處理者。 這個處理者,被賦值在Msg.target中. Msg的target本質上就是handler。
在前面發送消息,將消息加入隊列的時候,咱們看到過,msg.target=this,是在那個時候將消息的處理者進行了賦值。
因此這裏,當取出消息後,就又經過handler,經過它的dispatchMessage(msg)進行回傳的。
}
final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
final long end;
try {
//消息分發,回傳給處理者 。也就是handler本身。
msg.target.dispatchMessage(msg);
end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
複製代碼
繼續往下跟蹤能夠發現:
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
//若是消息的callback 處理者不爲空,就經過這個callback進行處理。這個callback是什麼呢? 後續再講解
if (msg.callback != null) {
handleCallback(msg);
} else {
//默認狀況下,走這個分支
if (mCallback != null) {
//若是全局的callback不爲空,就會執行這裏,再也不執行handlemessgae
if (mCallback.handleMessage(msg)) {
return;
}
}
// 回傳給消息處理者,處理消息,
handleMessage(msg);
}
}
複製代碼
分發消息後,默認會進入第二個分支。一般狀況下callback是沒有進行設置,因此直接就會回調handleMessage(msg).走完整個流程.進入處理消息流程
但是這個msg.callback是什麼呢? 還有全局的mCallback又是什麼呢? 分發消息的時候,若是這些不爲空。又會觸發什麼呢?繼續日後看
因爲咱們知道通知UI刷新的機制有4種方式:
咱們跟蹤一下view.post(runnable).進入view源碼:
//傳入一個runnable
public boolean post(Runnable action) {
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {
//而後將runnable,交給handler的post執行
return attachInfo.mHandler.post(action);
}
// Postpone the runnable until we know on which thread it needs to run.
// Assume that the runnable will be successfully placed after attach.
getRunQueue().post(action);
return true;
}
複製代碼
而後進入到handler的內部中post
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
複製代碼
能夠看到runnable,被包裝進入到了發送消息的getPostMessage(r)方法中。跟進去:
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
//看到沒!!! callback,原來就是指 咱們使用view.post(runnbale)的這個對象
//這個runnable,會賦值給msg.callback。
//因此當咱們使用別的方式進行更新UI或者消息通訊的時候,在handleMessage前,就會進入callback不爲空的判斷
m.callback = r;
return m;
}
複製代碼
也就是這裏msg.callback: 再貼一遍代碼
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
//若是消息的callback 處理者不爲空,就經過這個callback進行處理。這裏就是使用別的方式進行更新UI或者消息的時候,進入這裏處理,再也不進入handleMessage
if (msg.callback != null) {
handleCallback(msg);
} else {
//默認狀況下,走這個分支
if (mCallback != null) {
//若是全局的callback不爲空,就會執行這裏,再也不執行handlemessgae
if (mCallback.handleMessage(msg)) {
return;
}
}
// 回傳給消息處理者,處理消息,
handleMessage(msg);
}
}
複製代碼
而mCallback 則是咱們初始化構造handler的時候,傳入的callback,進行攔截消息處理使用。
總結一下
咱們能夠發現,最終都是進入到了handler的post方法中,經過handler機制來實現。
繼續講解更深刻的細節...
這裏發現有一個sThreadLocal對象,咱們的looper對象就是從這個對象中獲取的。這個ThreadLocal對象,其實就是一個與線程相關的對象,保存了線程的相關變量,狀態等等。再繼續往下看:發現最終就是從這個線程相關的對象中,內部有一個map對象,從裏面獲取得來。這個地方只是一個單純的get
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
複製代碼
這個地方依然不是咱們想要的答案,咱們想看的是Looper對象是在哪裏建立的。