相信Handler對於做爲Android開發者的小夥伴們來講並不陌生,某些階段的面試中Handler甚至是必考題之一。那麼Handler到底有什麼用呢?爲何要用Handler呢?java
咱們都知道在Android中分有UI線程和非UI線程,其中有一條規定就是隻能在UI線程操做UI組件,這是爲了防止多個線程併發的操做一個UI組件帶來的問題。同時咱們也知道不能在UI線程中作耗時操做,那麼這個時候就帶來了問題,若是咱們要在一個耗時操做結束後再去操做某個UI組件,那要怎麼作呢?好比咱們須要下載一份文件,而且在文件下載完成以後須要讓TextView顯示下載完成來提醒用戶。面試
這個時候就輪到咱們的Handler出場了:緩存
public class MainActivity extends AppCompatActivity {
TextView tv;
private static final int MSG_DOWNLOAD_FINISH=10;
//建立Handler,同時會發現as會提示不建議咱們這麼寫,這個稍後再說
private Handler handler=new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case MSG_DOWNLOAD_FINISH:
tv.setText("下載成功!");
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv=findViewById(R.id.tv);
tv.setText("downloading ...");
new Thread(new Runnable() {
@Override
public void run() {
try {
//這裏模擬一個耗時操做,能夠看到這裏是在非UI線程運行的
Thread.sleep(3000);
Message message = handler.obtainMessage(MSG_DOWNLOAD_FINISH);
message.sendToTarget();
//上面部分也能夠這麼寫,效果是同樣的
// Message message=new Message();
// message.what=MSG_DOWNLOAD_FINISH;
// handler.sendMessage(message);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
}
複製代碼
運行上面的代碼咱們能夠發如今子線程sleep 3s後,成功的修改了tv的文字爲 下載成功。咱們成功的知足了Android對UI線程的兩條規定,咱們憑藉Handler成功的在子線程作耗時操做而且在完成以後更新UI界面。數據結構
到此咱們否則發現Handler能夠作線程直接的通訊使用,經過Handler發送的一個Message,UI線程就能收到子線程要傳達的消息,那麼Handler爲何能作到線程間的通訊呢?併發
咱們先來對Handler的工做流程有一個總體的瞭解less
假設有AB兩個線程。在A線程建立一個Handler對象h,而且把h發送給B線程。此時A線程中經過Looper.loop()達到死循環不停的從A線程的MessageQueue中嘗試獲取一個Message,若是獲取到Message就會經過message.target獲取到A線程中建立的h,並調用h.dispatchMessage()方法(注意這一部分都是在A線程中進行的)。這時B線程獲取到h對象和經過h.sendMessage(msg)將一個msg插入到了A線程的MessageQueue中,這樣子一個流程就完成了。async
再看源碼以前咱們先說下Handler的總體結構,話很少說看圖。 ide
能夠看到主要涉及到三個類函數
值得注意的是線程不是一開始就擁有和本身綁定的Looper,MessageQueue的,在使用Handler以前須要咱們去調用Looper.prepare()方法來初始化當前線程的Looper,MessageQueue對象。咱們能夠看下這個方法作了什麼事情:oop
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方法最終會構建一個Looper對象並放入ThreadLocal中。關於ThreadLocal小夥伴們能夠簡單理解爲不一樣線程從同一個ThreadLocal中得到的對象是不一樣的,是獨屬於本身線程的。
經過上述的代碼咱們也可以發現同一個線程只能擁有一個Looper對象。 看到這裏的小夥伴可能會奇怪了,在咱們上面的簡單使用中咱們並無調用Looper.prepare(),爲何還能使用Handler呢?
這是應爲Android系統在啓動當前APP建立出UI線程後就會去執行Looper.prepareMainLooper()方法,系統已經幫咱們初始化過了UI線程的Looper,因此咱們能夠直接使用Handler對象了。
而說了Looper.prepare()就不得不提下Looper.loop()方法了。事實上咱們要想在子線程成功的new 出Handler而且順利使用的話,必需要再調用下Looper.loop()方法。 loop方法是一個是創建一個死循環,不停的嘗試從當前的MessageQueue中獲取一個Message。
public static void loop() {
//myLooper()就是經過ThreadLocal獲取當前線程的Looper實例
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;
//...省略部分代碼
for (;;) {
/** * 經過MessageQueue獲取一個msg。這裏小夥伴會問了不是說好了是循環的嗎? * 可是這裏若是queue.next()返回爲空不就return掉了嗎? * 這裏大可放心,queue.next()內部又是一個死循環,只有當如下狀況是纔會返回空 * 1.調用MessageQueue的quite方法 * 2.Application某些狀況下嘗試去重啓looper(這部分存疑) * 博主只發現這兩個狀況,有其餘狀況歡迎指出 */
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
//....
try {
//msg.target就是發送該msg的Handler對象
/** * 調用Handler的dispatchMessage分爲三種狀況 * 1.當msg含有Callback時候會調用msg的callback * 2.當msg不含有Callback可是Handler有設置Callback時候會調用Handler的callback。若是Handler的callback返回爲true的話就不會在執行handleMessage方法 * 3.不知足以上二者時會調用Handler的handleMessage方法,也就是咱們例子中實現的方法 */
msg.target.dispatchMessage(msg);
} finally {
//...
}
//...
//這裏可見Message在使用以後會被清空數據並緩存
msg.recycleUnchecked();
}
}
複製代碼
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
private static void handleCallback(Message message) {
//這裏的callback是一個Runnable。
//調用handler.post(new Runnable())方法就是生成一個帶callback的msg並投入到MessageQueue中
message.callback.run();
}
複製代碼
看完了取出Message並處理的操做,咱們看看發送Message部分的邏輯。 sendMessage(msg) ->sendMessageDelayed(msg, 0) ->sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis) ->enqueueMessage(queue, msg, uptimeMillis) ->queue.enqueueMessage(msg, uptimeMillis) 能夠看到上面這一串調用鏈以後最終會調用MessageQueue的enqueueMessage方法。而這上面這一串調主要作了一些常規的檢測操做,同時把當前的Handler賦值給msg.target。這部分很少說,咱們重點看入隊操做
boolean enqueueMessage(Message msg, long when) {
//常規性檢測,注意下msg.isInUse判斷,表明一個msg只能入隊一次
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) {
//若是調用過stop,此時判斷就會爲true
if (mQuitting) {
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;
}
複製代碼
看到這裏,小夥伴們應該能瞭解Handler的整套工做流程了。關於Handler中的線程切換,若是有點迷糊的話能夠這麼想 無論Handler被傳遞了什麼線程,不論是在什麼線程發送的消息。最終對該消息的處理都是在最初建立Handler的線程上。
由於Android系統在啓動APP的時候已經調用過Looper.prepareMainLooper();和Looper.loop()了 ActivityThread.java的main方法
public static void main(String[] args){
...
Looper.prepareMainLooper();
//初始化Looper
...
ActivityThread thread = new ActivityThread();
//實例化一個ActivityThread
thread.attach(false);
//創建Binder通道
...
Looper.loop();
//主線程進入無限循環狀態,等待接收消息
}
複製代碼
這部分參考知乎上的一個答案 Android中爲何主線程不會由於Looper.loop()裏的死循環卡死?
這個問題我在最初的例子中寫到了,as不建議咱們這麼寫Handler,這是應爲非靜態內部類會持有外部內的引用。那麼Handler將會持有Activity的引用,咱們知道handler是會被msg.target持有的,而msg又在MessageQueue隊列中,那麼當消息隊列中擁有未消費的Message時,會致使Activity即便finish了也沒法被GC回收,最終致使內存泄漏。爲了不這個問題咱們能夠將Handler寫成外部內或者靜態的內部類,而且傳遞的Activity引用能夠用WeakReference弱引用來持有,同時能夠在Activity的onDestory中使用Handler.removeCallbacksAndMessages(null);來清空消息隊列
因爲Handler還有一部分涉及到native層面,而對這一層面博主並不瞭解,因此沒有能提到這部分的東西,但願之後能有時間補充這部分的內容。以上內容如有錯誤之處歡迎你們指出,你們一塊兒進步。