子線程中:new Handler需要做哪些準備?消息隊列中無消息的時候,Looper的處理方案是什麼?

爲什麼主線程可以new Handler?

在ActivityThread.java裏有一個main()函數,它是Android每一個應用最早執行的函數。

    public static void main(String[] args) {

        .....

        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");
    }

它先prepare的,看這個prepare幹了些什麼事情?

    public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

prepare是初始化,初始化了一個Looper。

然後又接着調用的Looper.loop()函數,這個loop()函數其實就是執行了那個for循環,它不斷的調用next()函數,通過調用next()函數去輪詢我們的MessageQueue。

如果不調用prepare(),Looper沒有被初始化;如果不調用loop(),Looper的機制滾動不起來。所以,所有的執行必須先prepare(),然後再loop()。

回到問題,爲什麼主線程不需要做這些操作呢?

因爲主線程一啓動的時候,在main()函數中,由系統已經幫我們完成了,我們主線程中的所有代碼,全都運行在這兩個函數(prepare() 和 loop())之間。

 

在子線程中new Handler需要做哪些準備?

在上一題 爲什麼主線程可以new Handler?中其實已經解答,所有的線程都必須要prepare()和loop(),如果子線程中想要進行Handler操作,就必須在子線程中執行prepare() 和 loop()。

 

子線程中維護的Looper,消息隊列無消息的時候的處理方案是什麼?

我們先來創建一個子線程,然後子線程中創建了一個Looper,並且發送了一個消息,消息處理完了,看一下會發生什麼樣的事情。

    Handler threadHandler;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        new Thread(new Runnable() {
            @Override
            public void run() {
                if (threadHandler == null){
                    Looper.prepare();
                    threadHandler = new Handler(){
                        @Override
                        public void handleMessage(Message msg) {
                            super.handleMessage(msg);
                            Log.e("seas","handle Message");
                        }
                    };
                    Log.e("seas","Looper loop() UP");
                    Looper.loop();
                    Log.e("seas","Looper loop() DOWN");
                }
            }
        }).start();

    }

    public void click(View view){
        Log.e("seas","click");
        threadHandler.sendMessage(threadHandler.obtainMessage());

    }

我運行之後,再點擊執行click方法,就出現瞭如下的log。

如果這個子線程結束,那麼他應該是有調用到 Log.e("seas","Looper loop() DOWN"); 這個語句纔對,但是它並沒有,這就意味着這個Looper一直處於一個阻塞狀態。

也就是說它一直卡在了這個地方:

        for (;;) {
            Message msg = queue.next(); // might block

            .....
        }

所以它就一直在等待,這個子線程也已經幹不了其他的事情了,其實也被卡死了。

子線程中維護的Looper,消息隊列無消息的時候的處理方案是什麼?有什麼用?

在Handler機制裏面有一個Looper,在Looper機制裏面有一個函數,叫做quitSafely()和quit()函數,這兩個函數是調用的MessageQueue的quit()。

    /**
     * Quits the looper.
     * <p>
     * Causes the {@link #loop} method to terminate without processing any
     * more messages in the message queue.
     * </p><p>
     * Any attempt to post messages to the queue after the looper is asked to quit will fail.
     * For example, the {@link Handler#sendMessage(Message)} method will return false.
     * </p><p class="note">
     * Using this method may be unsafe because some messages may not be delivered
     * before the looper terminates.  Consider using {@link #quitSafely} instead to ensure
     * that all pending work is completed in an orderly manner.
     * </p>
     *
     * @see #quitSafely
     */
    public void quit() {
        mQueue.quit(false);
    }

    /**
     * Quits the looper safely.
     * <p>
     * Causes the {@link #loop} method to terminate as soon as all remaining messages
     * in the message queue that are already due to be delivered have been handled.
     * However pending delayed messages with due times in the future will not be
     * delivered before the loop terminates.
     * </p><p>
     * Any attempt to post messages to the queue after the looper is asked to quit will fail.
     * For example, the {@link Handler#sendMessage(Message)} method will return false.
     * </p>
     */
    public void quitSafely() {
        mQueue.quit(true);
    }

再進入到MessageQueue的quit()函數。

    void quit(boolean safe) {
        if (!mQuitAllowed) {
            throw new IllegalStateException("Main thread not allowed to quit.");
        }

        synchronized (this) {
            if (mQuitting) {
                return;
            }
            mQuitting = true;

            if (safe) {
                removeAllFutureMessagesLocked();
            } else {
                removeAllMessagesLocked();
            }

            // We can assume mPtr != 0 because mQuitting was previously false.
            nativeWake(mPtr);
        }
    }

它會remove消息,把消息隊列中的全部消息給幹掉。

把消息全部幹掉,也就釋放了內存

    private void removeAllFutureMessagesLocked() {
        final long now = SystemClock.uptimeMillis();
        Message p = mMessages;
        if (p != null) {
            if (p.when > now) {
                removeAllMessagesLocked();
            } else {
                Message n;
                for (;;) {
                    n = p.next;
                    if (n == null) {
                        return;
                    }
                    if (n.when > now) {
                        break;
                    }
                    p = n;
                }
                p.next = null;
                do {
                    p = n;
                    n = p.next;
                    p.recycleUnchecked();
                } while (n != null);
            }
        }
    }

而在quit()函數的最後一行,有一個nativeWake()函數。

            // We can assume mPtr != 0 because mQuitting was previously false.
            nativeWake(mPtr);

這個函數的調用,就會叫醒等待的地方,醒來之後,就接着往下執行。

            //native的方法,在沒有消息的時候回阻塞管道讀取端,只有nativePollOnce返回之後才能往下執行
            //阻塞操作,等待nextPollTimeoutMillis時長
            nativePollOnce(ptr, nextPollTimeoutMillis);

往下執行後,發現 Message msg = mMessages; 是空的,然後就執行了這個,就接着往下走。

                if (msg != null) {

                   ......

                } else {
                    // No more messages.
                	//沒有消息,nextPollTimeoutMillis復位
                    nextPollTimeoutMillis = -1;
                }

然後又調用了這個方法,並且return了null。

                // Process the quit message now that all pending messages have been handled.
                //如果消息隊列正在處於退出狀態返回null,調用dispose();釋放該消息隊列
                if (mQuitting) {
                    dispose();
                    return null;
                }

所以說,這個時候Looper就結束了(跳出了死循環),則達成了第二個作用:釋放線程

 

如果我在剛纔的例子上加入退出操作。

    public void stopclick(View view){
        Log.e("seas","click");
        threadHandler.getLooper().quit();

    }

打印如下:

這個線程就真正的執行了,否則這個線程就一直在等待。

如果在子線程中創建了一個Handler,那麼就必須做三個操作:

1. prepare();

2. loop();

3. quit();

 

主線程中能否調用quit()方法?

是不能的。它會拋出一個異常,讓程序掛掉。

    void quit(boolean safe) {
        if (!mQuitAllowed) {
            throw new IllegalStateException("Main thread not allowed to quit.");
        }
        ......
    }

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章