天天看點

這是一篇描述 Handler消息機制 的文章感謝大綱概念執行流程初步使用我的Demo源碼原理相關面試題

目錄

大綱

概念

概述

較長的描述

執行流程

初步使用

在主線程中使用

sendMessage(Message)

post(Runnable)

在次線程中使用

我的Demo

源碼原理

相關面試題

感謝

感謝developer.android.google!~

感謝各位大大提供了各種學習資料 !~

感謝自己 感謝你們!~

學習資料:

Handler消息傳遞機制淺析

Android多線程:手把手帶你深入Handler源碼分析(上)

Android多線程:手把手帶你深入Handler源碼分析(下)

大綱

這是一篇描述 Handler消息機制 的文章感謝大綱概念執行流程初步使用我的Demo源碼原理相關面試題

概念

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執行流程如圖所示:

這是一篇描述 Handler消息機制 的文章感謝大綱概念執行流程初步使用我的Demo源碼原理相關面試題

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)發送消息,所經過的所有方法:

這是一篇描述 Handler消息機制 的文章感謝大綱概念執行流程初步使用我的Demo源碼原理相關面試題

它們最大的差别就是: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面試題 請安利(不停更)