目錄
大綱
概念
概述
較長的描述
執行流程
初步使用
在主線程中使用
sendMessage(Message)
post(Runnable)
在次線程中使用
我的Demo
源碼原理
相關面試題
感謝
感謝developer.android.google!~
感謝各位大大提供了各種學習資料 !~
感謝自己 感謝你們!~
學習資料:
Handler消息傳遞機制淺析
Android多線程:手把手帶你深入Handler源碼分析(上)
Android多線程:手把手帶你深入Handler源碼分析(下)
大綱
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLiAzNfRHLGZkRGZkRfJ3bs92YsYTMfVmepNHL4tGRONTTq1keJpHW4Z0MMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnL0QjM5ATNykDMxMTNwkTMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
概念
Handler,英[ˈhændlə(r)] 美[ˈhændlər] ,譯為處理器;句柄;處理程式;處理者;處理
概述
很多時候,耗時任務都是需要放到次線程去執行的,次線程任務執行完畢之後,主線程需要及時的進行一個UI的更新與回報以告知使用者,那麼如何能夠及時?主線程何時才知道次線程完成了任務?——Handler,就是主次線程溝通的一個橋梁。
較長的描述
查閱官方對于Handler的描述可知,Handler 繼承于Object,它允許我們發送和處理與線程MessageQueue關聯的Message與Runnable對象。每個Handler都與一個線程以及該線程的消息隊列關聯。當我們建立一個新的Handler時,它被綁定到正在建立它的線程/該線程的消息隊列——從那時起,它将消息和runnables傳遞給該消息隊列并在消息從消息隊列中取出時,執行它們。
Handler有兩個主要用途:
(1)安排messages和runnables在将來的某個時刻被執行;
(2)将在不同的線程上執行的操作排入隊列。
整個Handler的消息排程離不開以下幾個方法:
(1)post(Runnable)
(2)postAtTime(java.lang.Runnable, long)
(3)postDelayed(Runnable, Object, long)
(4)sendEmptyMessage(int)
(5)sendMessage(Message)
(6)sendMessageAtTime(Message, long)
(7)sendMessageDelayed(Message, long)
post(Runnable),将Runnable對象添加到消息隊列message queue中,該Runnable對象将在該Handler所連接配接的線程上運作;sendMessage(Message),它将在消息隊列message queue的末尾添加一條消息,而這條消息,它是在目前時間之前的所有挂起消息之後。它将以handleMessage(Message)的形式在附加到此Handler的線程中接收。
當向Handler釋出或發送消息時,可以允許在message queue準備好處理該項時立即處理該項,或者指定處理該項之前的延遲或處理該項的絕對時間。後兩者允許實作逾時、标記和其他基于時間的行為。
為應用程式建立程序時,其主線程專用于運作消息隊列message queue,該隊列負責管理頂級應用程式對象(活動,廣播接收器等)及其建立的任何視窗。我們可以建立自己的線程,并通過Handler與主應用程式線程進行通信。這是通過調用post或sendMessage方法完成的,然後,将在Handler的消息隊列message queue中排程給定的Runnable或Message,并在适當時進行處理。
執行流程
Handler執行流程如圖所示:
4個關鍵詞:
//1,Looper:循環器,用于管理MessageQueue,不斷地循環消息,不斷地從中取出Message分發給對應的Handler處理,每個線程隻能夠有一個Looper。
//2,Message:消息對象。
//3,MessageQueue:存放消息對象的消息隊列,隊列它是一種先進先出的資料結構。
//4,Handler:處理消息對象的處理器。
//注意:系統在建立主線程的時候,會初始化一個Looper對象,同時也會把它關聯的MessageQueue建立好,是以我們的Handler在主線程中執行個體化時,不需要額外的對Looper進行操作(就可以進行資訊的發送與處理了)。
初步使用
在主線程中使用
sendMessage(Message)
使用sendMessage(Message),有如下幾個步驟:
//1:在主線程建立一個Handler靜态内部類
//2:在主線程執行個體化第1步中建立的靜态内部類
//3:建立一個子線程
//4:在子線程中,通過Handler對象調用sendMessage(Message)方法,将消息發送到消息隊列中
//請看代碼:
public class HandlerSendMessageTest extends AppCompatActivity {
TextView test;
Handler myHandler;
//1:在主線程建立一個Handler靜态内部類
// 靜态内部類不會持有外部類的引用 再結合WeakReference使用 可以預防記憶體洩漏
private static class MyHandler extends Handler {
WeakReference<MainActivity> weakReferenceAct;
public MyHandler(MainActivity mainActivity) {
weakReferenceAct = new WeakReference<MainActivity>(mainActivity);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case 1:
weakReferenceAct.get().test.setText("myHandler.sendMessage(msg)");
break;
default:
break;
}
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
test = findViewById(R.id.test);
//2:執行個體化内部類
myHandler = new MyHandler(this);
//3:舉個栗子 建立一個線程
new Thread() {
@Override
public void run() {
super.run();
SystemClock.sleep(2000);
Message msg = Message.obtain();
msg.what = 1;
msg.obj = "nhan";
//4:在子線程中,通過Handler對象調用sendMessage(Message)方法,将消息發送到消息隊列中
myHandler.sendMessage(msg);
}
}.start();
}
}
post(Runnable)
//1:在主線程中執行個體化一個Handler,得到一個Handler的對象
//2:建立一個子線程
//3:在子線程中,直接調用Handler對象.post(Runnable)方法傳入一個Runnable,而後進行相關的UI操作
//請看代碼:
public class HandlerPostMessageTest extends AppCompatActivity {
TextView test;
Handler handler;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
test = findViewById(R.id.test);
//1:在主線程中執行個體化一個Handler,得到一個Handler的對象
handler = new Handler();
//2:舉個栗子 建立一個子線程
new Thread() {
@Override
public void run() {
super.run();
//3:在子線程中,直接調用Handler對象.post(Runnable)方法傳入一個Runnable,而後進行相關的UI操作
handler.post(new Runnable() {
@Override
public void run() {
test.setText("handler.post(Runnable)");
}
});
}
}.start();
}
}
在次線程中使用
public class LooperPrepare extends AppCompatActivity {
Handler handler;
TextView test;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
test = findViewById(R.id.test);
//1:建立一個子線程
new Thread() {
@Override
public void run() {
super.run();
//2:在子線程中 準備Looper對象
Looper.prepare();
//3:執行個體化Handler 并重寫handleMessage()方法處理消息
handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case 1:
test.setText("在子線程中使用Handler");
break;
default:
break;
}
}
};
//4:調用Looper.loop() 運作消息隊列
Looper.loop();
}
}.start();
}
@Override
protected void onResume() {
super.onResume();
Message msg = Message.obtain();
msg.what = 1;
msg.obj = "nhan";
handler.sendMessage(msg);
}
}
我的Demo
先欠着吧?回頭一定補上 .. - - ,
源碼原理
Handler消息機制能夠正常運作,如前面的執行流程圖所示,它跟Looper、MessageQueue之間是密不可分的,是以對于Handler消息機制源碼的分析,以Handler在次線程中的使用為栗子,從頭到尾進行分析,那麼就慢慢來吧 ——
在子線程中使用Handler,我們首先要能夠擷取到一個Looper的對象,對于Looper,它最重要的兩塊,就是prepare()和loop()兩個方法:
prepare()源碼如下:
/** 将目前線程初始化為一個looper.
* 這使我們有機會在實際循環開始之前建立引用該looper的handlers.
* 請確定在調用此方法後調用{@link #loop()},并通過調用{@link #quit()}結束該方法。
*/
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
//由這裡可以看出,一個線程,隻能建立一個Looper
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
我們可以看到,prepare()最終是傳回一個sThreadLocal.set(new Looper(quitAllowed)):
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
而這個new Looper(quitAllowed)是作為參數,傳入到sThreadLocal.set(new Looper(quitAllowed))當中,可以看到Looper操作中,有new一個消息隊列MessageQueue:
private Looper(boolean quitAllowed) {
//建立一個MessageQueue對象
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
是以我們說,系統在建立主線程的時候,會初始化一個Looper對象,同時也會把它關聯的MessageQueue建立好,是以我們的Handler在主線程中執行個體化時,不需要額外的對Looper進行操作。
那麼在建立好Looper對象與MessageQueue之後,就需要在建立好的線程内對Handler進行執行個體化、重寫handleMessage()方法 .. 并開啟消息隊列的循環,進而去對消息隊列中的消息進行輪詢。
此時開啟消息隊列的循環,loop()就派上了用場,代碼有點長,可以慢慢看:
/**
* 在此線程中運作消息隊列。請務必調用 {@link #quit ()} 以結束循環。
*/
public static void loop() {
//擷取目前的Looper對象
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
//拿到目前Looper對象的消息隊列
final MessageQueue queue = me.mQueue;
//確定此線程的辨別是本地程序的辨別, 并跟蹤該辨別令牌的實際身份。
Binder.clearCallingIdentity();//重置目前線程上傳入 IPC 的辨別
final long ident = Binder.clearCallingIdentity();
//中間這裡忽略了不重要的代碼
//開啟循環
for (;;) {
Message msg = queue.next(); // 從消息隊列中取出消息
if (msg == null) {
// 沒有消息則表明消息隊列正在退出
return;
}
//中間這裡忽略了不重要的代碼
//開始分發
final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
//結束分發
final long dispatchEnd;
try {
msg.target.dispatchMessage(msg);//分發消息
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
//中間這裡忽略了不重要的代碼
//回收可能正在使用的消息
msg.recycleUnchecked();
}
}
由代碼我們也可以看到,在不斷的輪詢過程中,消息隊列是通過next()方法将消息從消息隊列中移出來的:
Message next() {
// 如果消息循環已退出并已釋放, 則傳回此處。
// 可能會發生這種情況:如果應用程式在退出後嘗試重新啟動一個不受支援的looper。
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
// pendingIdleHandlerCount 挂起的Handler總數
int pendingIdleHandlerCount = -1; // -1 僅在第一次疊代期間
int nextPollTimeoutMillis = 0;
//開啟循環
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();//Binder重新整理挂起的指令
}
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// 嘗試檢索下一條消息。 如果找到, 則将其傳回。
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
// 查找隊列中的下一條異步消息。
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 {
// 得到一個消息。
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;
}
// 當所有挂起的消息都被處理, 則處理退出消息。
if (mQuitting) {
dispose();//釋放基礎消息隊列
return null;
}
// 如果第一次空閑, 則擷取要運作的空閑者數。
// 空閑handles僅在隊列為空或隊列中的第一條消息将來将被處理時才運作。
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
// 沒有空閑的handler要運作。 循環, 再等一會兒。
mBlocked = true;
continue;
}
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// 運作空閑的handlers.
// 我們隻在第一次疊代期間到達此代碼塊。
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // 釋放對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);
}
}
}
// 将空閑handler總數重置為 0, 這樣我們就不會再次運作它們。
pendingIdleHandlerCount = 0;
// 在調用空閑handler時, 新的消息可以被傳遞, 以便傳回并再次查找挂起的消息無需等待。
nextPollTimeoutMillis = 0;
}
}
不斷的輪詢,不斷的将消息取出,不斷的去執行對應的消息,這就是我們的Handler消息機制了。
最後,我們分析sendMessage(Message)與post(Runnable)到底有什麼差别?
如下圖,是sendMessage(Message)與post(Runnable)發送消息,所經過的所有方法:
它們最大的差别就是:sendMessage(Message)需要傳入Message,而post(Runnable)需要傳入Runnable。
細細分析,post(Runnable)最終return的是sendMessageDelayed(getPostMessage(r), 0):
/**
* 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(getPostMessage(r), 0)我們又可以看到,它需要傳入一個參數getPostMessage(r),這個參數,最終傳回的也是一個Message類型的對象。
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
private static Message getPostMessage(Runnable r, Object token) {
Message m = Message.obtain();
m.obj = token;
m.callback = r;
return m;
}
接下來,post(Runnable)走的都是與sendMessage(Message)相同的方式,是以我們可以認為,它們的本質其實都是相同的,最終的最終,也隻是為了把消息壓入消息隊列MessageQueue中。
自此,整個流程分析完畢。
相關面試題
面試題傳送門:200斤牌面試必備Handler面試題 請安利(不停更)