深入了解Android Handler機(jī)制原理詳解

前言

原文:深入了解Android Handler機(jī)制原理詳解 - 知乎 (zhihu.com)

在android開發(fā)中,經(jīng)常會在子線程中進(jìn)行一些操作,當(dāng)操作完畢后會通過handler發(fā)送一些數(shù)據(jù)給主線程,通知主線程做相應(yīng)的操作。 探索其背后的原理:子線程 handler 主線程 其實(shí)構(gòu)成了線程模型中的經(jīng)典問題 生產(chǎn)者-消費(fèi)者模型。 生產(chǎn)者-消費(fèi)者模型:生產(chǎn)者和消費(fèi)者在同一時間段內(nèi)共用同一個存儲空間,生產(chǎn)者往存儲空間中添加數(shù)據(jù),消費(fèi)者從存儲空間中取走數(shù)據(jù)。

image

好處: - 保證數(shù)據(jù)生產(chǎn)消費(fèi)的順序(通過MessageQueue,先進(jìn)先出) - 不管是生產(chǎn)者(子線程)還是消費(fèi)者(主線程)都只依賴緩沖區(qū)(handler),生產(chǎn)者消費(fèi)者之間不會相互持有,使他們之間沒有任何耦合

源碼分析

  • Handler

  • Handler機(jī)制的相關(guān)類

  • 創(chuàng)建Looper

  • 創(chuàng)建MessageQueue以及Looper與當(dāng)前線程的綁定

  • Looper.loop()

  • 創(chuàng)建Handler

  • 創(chuàng)建Message

  • Message和Handler的綁定

  • Handler發(fā)送消息

  • Handler處理消息

Handler機(jī)制的相關(guān)類

Hanlder:發(fā)送和接收消息 Looper:用于輪詢消息隊(duì)列,一個線程只能有一個Looper Message: 消息實(shí)體 MessageQueue: 消息隊(duì)列用于存儲消息和管理消息

創(chuàng)建Looper

創(chuàng)建Looper的方法是調(diào)用Looper.prepare() 方法

在ActivityThread中的main方法中為我們prepare了

public static void main(String[] args) {
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
        //其他代碼省略...
        Looper.prepareMainLooper(); //初始化Looper以及MessageQueue

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

Looper.prepareMainLooper();

 public static void prepareMainLooper() {
        prepare(false);//消息隊(duì)列不可以quit
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

prepare有兩個重載的方法,主要看 prepare(boolean quitAllowed) quitAllowed的作用是在創(chuàng)建MessageQueue時標(biāo)識消息隊(duì)列是否可以銷毀, 主線程不可被銷毀 下面有介紹

  public static void prepare() {
        prepare(true);//消息隊(duì)列可以quit
    }
    //quitAllowed 主要
    private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {//不為空表示當(dāng)前線程已經(jīng)創(chuàng)建了Looper
            throw new RuntimeException("Only one Looper may be created per thread");
            //每個線程只能創(chuàng)建一個Looper
        }
        sThreadLocal.set(new Looper(quitAllowed));//創(chuàng)建Looper并設(shè)置給sThreadLocal,這樣get的時候就不會為null了
    }

創(chuàng)建MessageQueue以及Looper與當(dāng)前線程的綁定

   private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);//創(chuàng)建了MessageQueue
        mThread = Thread.currentThread(); //當(dāng)前線程的綁定
   }

MessageQueue的構(gòu)造方法

 MessageQueue(boolean quitAllowed) {
 //mQuitAllowed決定隊(duì)列是否可以銷毀 主線程的隊(duì)列不可以被銷毀需要傳入false, 在MessageQueue的quit()方法就不貼源碼了
        mQuitAllowed = quitAllowed;
        mPtr = nativeInit();
    }

Looper.loop()

同時是在main方法中 Looper.prepareMainLooper() 后Looper.loop(); 開始輪詢

public static void loop() {
        final Looper me = myLooper();//里面調(diào)用了sThreadLocal.get()獲得剛才創(chuàng)建的Looper對象
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }//如果Looper為空則會拋出異常
        final MessageQueue queue = me.mQueue;

        // Make sure the identity of this thread is that of the local process,
        // and keep track of what that identity token actually is.
        Binder.clearCallingIdentity();
        final long ident = Binder.clearCallingIdentity();

        for (;;) {
            //這是一個死循環(huán),從消息隊(duì)列不斷的取消息
            Message msg = queue.next(); // might block
            if (msg == null) {
                //由于剛創(chuàng)建MessageQueue就開始輪詢,隊(duì)列里是沒有消息的,等到Handler sendMessage enqueueMessage后
                //隊(duì)列里才有消息
                // 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
            Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }

            msg.target.dispatchMessage(msg);//msg.target就是綁定的Handler,詳見后面Message的部分,Handler開始
            //后面代碼省略.....

            msg.recycleUnchecked();
        }
    }

創(chuàng)建Handler

最常見的創(chuàng)建handler

        Handler handler=new Handler(){
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
            }
        };

在內(nèi)部調(diào)用 this(null, false);

public Handler(Callback callback, boolean async) {
        //前面省略
        mLooper = Looper.myLooper();//獲取Looper,**注意不是創(chuàng)建Looper**!
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;//創(chuàng)建消息隊(duì)列MessageQueue
        mCallback = callback; //初始化了回調(diào)接口
        mAsynchronous = async;
    }

Looper.myLooper();

    //這是Handler中定義的ThreadLocal  ThreadLocal主要解多線程并發(fā)的問題
  // sThreadLocal.get() will return null unless you've called prepare().
   static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

 public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

sThreadLocal.get() will return null unless you’ve called prepare(). 這句話告訴我們get可能返回null 除非先調(diào)用prepare()方法創(chuàng)建Looper。在前面已經(jīng)介紹了

創(chuàng)建Message

可以直接new Message 但是有更好的方式 Message.obtain。因?yàn)榭梢詸z查是否有可以復(fù)用的Message,用過復(fù)用避免過多的創(chuàng)建、銷毀Message對象達(dá)到優(yōu)化內(nèi)存和性能的目地

public static Message obtain(Handler h) {
        Message m = obtain();//調(diào)用重載的obtain方法
        m.target = h;//并綁定的創(chuàng)建Message對象的handler

        return m;
    }

public static Message obtain() {
        synchronized (sPoolSync) {//sPoolSync是一個Object對象,用來同步保證線程安全
            if (sPool != null) {//sPool是就是handler dispatchMessage 后 通過recycleUnchecked 回收用以復(fù)用的Message 
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; // clear in-use flag
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }

Message和Handler的綁定

創(chuàng)建Message的時候可以通過 Message.obtain(Handler h) 這個構(gòu)造方法綁定。當(dāng)然可以在 在Handler 中的 enqueueMessage()也綁定了,所有發(fā)送Message的方法都會調(diào)用此方法入隊(duì),所以在創(chuàng)建Message的時候是可以不綁定的

  private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this; //綁定
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

Handler發(fā)送消息

Handler發(fā)送消息的重載方法很多,但是主要只有2種。 sendMessage(Message) sendMessage方法通過一系列重載方法的調(diào)用,sendMessage調(diào)用sendMessageDelayed,繼續(xù)調(diào)用sendMessageAtTime,繼續(xù)調(diào)用enqueueMessage,繼續(xù)調(diào)用messageQueue的enqueueMessage方法,將消息保存在了消息隊(duì)列中,而最終由Looper取出,交給Handler的dispatchMessage進(jìn)行處理

我們可以看到在dispatchMessage方法中,message中callback是一個Runnable對象,如果callback不為空,則直接調(diào)用callback的run方法,否則判斷mCallback是否為空,mCallback在Handler構(gòu)造方法中初始化,在主線程通直接通過無參的構(gòu)造方法new出來的為null,所以會直接執(zhí)行后面的handleMessage()方法。

public void dispatchMessage(Message msg) {
    if (msg.callback != null) {//callback在message的構(gòu)造方法中初始化或者使用handler.post(Runnable)時候才不為空
        handleCallback(msg);
    } else {
        if (mCallback != null) {//mCallback是一個Callback對象,通過無參的構(gòu)造方法創(chuàng)建出來的handler,該屬性為null,此段不執(zhí)行
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);//最終執(zhí)行handleMessage方法
    }
}

 private static void handleCallback(Message message) {
        message.callback.run();
    }

Handler處理消息

在handleMessage(Message)方法中,我們可以拿到message對象,根據(jù)不同的需求進(jìn)行處理,整個Handler機(jī)制的流程就結(jié)束了。

小結(jié)

handler.sendMessage 發(fā)送消息到消息隊(duì)列MessageQueue,然后looper調(diào)用自己的loop()函數(shù)帶動MessageQueue從而輪詢messageQueue里面的每個Message,當(dāng)Message達(dá)到了可以執(zhí)行的時間的時候開始執(zhí)行,執(zhí)行后就會調(diào)用message綁定的Handler來處理消息。大致的過程如下圖所示

image

handler機(jī)制就是一個傳送帶的運(yùn)轉(zhuǎn)機(jī)制。

1)MessageQueue就像履帶。

2)Thread就像背后的動力,就是我們通信都是基于線程而來的。

3)傳送帶的滾動需要一個開關(guān)給電機(jī)通電,那么就相當(dāng)于我們的loop函數(shù),而這個loop里面的for循環(huán)就會帶著不斷的滾動,去輪詢messageQueue

4)Message就是 我們的貨物了。

難點(diǎn)問題

1. 線程同步問題

Handler是用于線程間通信的,但是它產(chǎn)生的根本并不只是用于UI處理,而更多的是handler是整個app通信的框架,大家可以在ActivityThread里面感受到,整個App都是用它來進(jìn)行線程間的協(xié)調(diào)。Handler既然這么重要,那么它的線程安全就至關(guān)重要了,那么它是如何保證自己的線程安全呢?

Handler機(jī)制里面最主要的類MessageQueue,這個類就是所有消息的存儲倉庫,在這個倉庫中,我們?nèi)绾蔚墓芾砗孟?,這個就是一個關(guān)鍵點(diǎn)了。消息管理就2點(diǎn):1)消息入庫(enqueueMessage),2)消息出庫(next),所以這兩個接口是確保線程安全的主要檔口。

enqueueMessage 源碼如下:

boolean enqueueMessage(Message msg, long when) {
    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) {
        if (mQuitting) {
            IllegalStateException e = new IllegalStateException(
                    msg.target + " sending message to a Handler on a dead thread");
            Log.w(TAG, e.getMessage(), e);
            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);
        }
    }
    //鎖結(jié)束的地方

synchronized鎖是一個內(nèi)置鎖,也就是由系統(tǒng)控制鎖的lock unlock時機(jī)的。在多線程的課程中我們有詳細(xì)分析過,有問題的同學(xué)可以去研究一下。

synchronized (this)

這個鎖,說明的是對所有調(diào)用同一個MessageQueue對象的線程來說,他們都是互斥的,然而,在我們的Handler里面,一個線程是對應(yīng)著一個唯一的Looper對象,而Looper中又只有一個唯一的MessageQueue(這個在上文中也有介紹)。所以,我們主線程就只有一個MessageQueue對象,也就是說,所有的子線程向主線程發(fā)送消息的時候,主線程一次都只會處理一個消息,其他的都需要等待,那么這個時候消息隊(duì)列就不會出現(xiàn)混亂。

另外,在看next函數(shù)

Message next() {

    ....

    for (;;) {
        ....

        nativePollOnce(ptr, nextPollTimeoutMillis);

        synchronized (this) {
            // Try to retrieve the next message.  Return if found.
                   ...
                    return msg;
                }
            } else {
                // No more messages.
                nextPollTimeoutMillis = -1;
            }

             ...
        }//synchronized 結(jié)束之處

        // Run the idle handlers.
        // We only ever reach this code block during the first iteration.
        for (int i = 0; i < pendingIdleHandlerCount; i++) {
            final IdleHandler idler = mPendingIdleHandlers[i];
            mPendingIdleHandlers[i] = null; // release the reference to the 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);
                }
            }
        }

        // Reset the idle handler count to 0 so we do not run them again.
        pendingIdleHandlerCount = 0;

        // While calling an idle handler, a new message could have been delivered
        // so go back and look again for a pending message without waiting.
        nextPollTimeoutMillis = 0;
    }
}

next函數(shù)很多同學(xué)會有疑問:我從線程里面取消息,而且每次都是隊(duì)列的頭部取,那么它加鎖是不是沒有意義呢?答案是否定的,我們必須要在next里面加鎖,因?yàn)椋@樣由于synchronized(this)作用范圍是所有 this正在訪問的代碼塊都會有保護(hù)作用,也就是它可以保證 next函數(shù)和 enqueueMessage函數(shù)能夠?qū)崿F(xiàn)互斥。這樣才能真正的保證多線程訪問的時候messagequeue的有序進(jìn)行。

小結(jié): 這個地方是面試官經(jīng)常問的點(diǎn),而且他們會基于這個點(diǎn)來拓展問你多線程,所以,這個地方請大家重視。

2. 消息機(jī)制之同步屏障

同步屏障,view繪制中用 <https://juejin.im/post/6844903910113705998

同步屏障的概念,在Android開發(fā)中非常容易被人忽略,因?yàn)檫@個概念在我們普通的開發(fā)中太少見了,很容易被忽略。

大家經(jīng)過上面的學(xué)習(xí)應(yīng)該知道,線程的消息都是放到同一個MessageQueue里面,取消息的時候是互斥取消息,而且只能從頭部取消息,而添加消息是按照消息的執(zhí)行的先后順序進(jìn)行的排序,那么問題來了,同一個時間范圍內(nèi)的消息,如果它是需要立刻執(zhí)行的,那我們怎么辦,按照常規(guī)的辦法,我們需要等到隊(duì)列輪詢到我自己的時候才能執(zhí)行哦,那豈不是黃花菜都涼了。所以,我們需要給緊急需要執(zhí)行的消息一個綠色通道,這個綠色通道就是同步屏障的概念。

同步屏障是什么?

屏障的意思即為阻礙,顧名思義,同步屏障就是阻礙同步消息,只讓異步消息通過。如何開啟同步屏障呢?如下而已:

MessageQueue#postSyncBarrier()

我們看看它的源碼

   /**
     *
   @hide
   **/
      public int postSyncBarrier() {
           return postSyncBarrier(SystemClock.uptimeMillis());
      }
private int postSyncBarrier(long when) {
    // Enqueue a new sync barrier token
    synchronized (this) {
        final int token = mNextBarrierToken++;
        //從消息池中獲取Message
        final Message msg = Message.obtain();
        msg.markInUse();

        //就是這里?。?!初始化Message對象的時候,并沒有給target賦值,因此 target==null
        msg.when = when;
        msg.arg1 = token;

        Message prev = null;
        Message p = mMessages;

        if (when != 0) {
            while (p != null && p.when <= when) {
          //如果開啟同步屏障的時間(假設(shè)記為T)T不為0,且當(dāng)前的同步消息里有時間小于T,則prev也不為null
                prev = p;
                p = p.next;
            }
        }
        //根據(jù)prev是不是為null,將 msg 按照時間順序插入到 消息隊(duì)列(鏈表)的合適位置
        if (prev != null) { // invariant: p == prev.next
            msg.next = p;
            prev.next = msg;
        } else {
            msg.next = p;
            mMessages = msg;
        }
        return token;
    }
}

可以看到,Message 對象初始化的時候并沒有給 target 賦值,因此,target == null的 來源就找到了。上面消息的插入也做了相應(yīng)的注釋。這樣,一條target == null 的消息就進(jìn)入了消息隊(duì)列。

那么,開啟同步屏障后,所謂的異步消息又是如何被處理的呢?

如果對消息機(jī)制有所了解的話,應(yīng)該知道消息的最終處理是在消息輪詢器Looper#loop()中,而loop()循環(huán)中會調(diào)用MessageQueue#next()從消息隊(duì)列中進(jìn)行取消息。

//MessageQueue.java

Message next()

    .....//省略一些代碼
    int pendingIdleHandlerCount = -1; // -1 only during first iteration
    // 1.如果nextPollTimeoutMillis=-1,一直阻塞不會超時。
    // 2.如果nextPollTimeoutMillis=0,不會阻塞,立即返回。
    // 3.如果nextPollTimeoutMillis>0,最長阻塞nextPollTimeoutMillis毫秒(超時)
    //   如果期間有程序喚醒會立即返回。
    int nextPollTimeoutMillis = 0;
    //next()也是一個無限循環(huán)
    for (;;) {
        if (nextPollTimeoutMillis != 0) {
            Binder.flushPendingCommands();
        }
        nativePollOnce(ptr, nextPollTimeoutMillis);
        synchronized (this) {
            //獲取系統(tǒng)開機(jī)到現(xiàn)在的時間
            final long now = SystemClock.uptimeMillis();
            Message prevMsg = null;
            Message msg = mMessages; //當(dāng)前鏈表的頭結(jié)點(diǎn)

            //關(guān)鍵?。?!
            //如果target==null,那么它就是屏障,需要循環(huán)遍歷,一直往后找到第一個異步的消息
            if (msg != null && msg.target == null) {
            // Stalled by a barrier.  Find the next asynchronous message in the queue.
                do {
                    prevMsg = msg;
                    msg = msg.next;
                } while (msg != null && !msg.isAsynchronous());
            }
            if (msg != null) {
                //如果有消息需要處理,先判斷時間有沒有到,如果沒到的話設(shè)置一下阻塞時間,
                //場景如常用的postDelay
                if (now < msg.when) {
                   //計算出離執(zhí)行時間還有多久賦值給nextPollTimeoutMillis,
                   //表示nativePollOnce方法要等待nextPollTimeoutMillis時長后返回
                    nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                } else {
                    // 獲取到消息
                    mBlocked = false;
                   //鏈表操作,獲取msg并且刪除該節(jié)點(diǎn) 
                    if (prevMsg != null) 
                        prevMsg.next = msg.next;
                    } else {
                        mMessages = msg.next;
                    }
                    msg.next = null;
                    msg.markInUse();
                    //返回拿到的消息
                    return msg;
                }
            } else {
                //沒有消息,nextPollTimeoutMillis復(fù)位
                nextPollTimeoutMillis = -1;
            }
            .....//省略

}

從上面可以看出,當(dāng)消息隊(duì)列開啟同步屏障的時候(即標(biāo)識為msg.target == null),消息機(jī)制在處理消息的時候,優(yōu)先處理異步消息。這樣,同步屏障就起到了一種過濾和優(yōu)先級的作用。

下面用示意圖簡單說明:

image

如上圖所示,在消息隊(duì)列中有同步消息和異步消息(黃色部分)以及一道墻----同步屏障(紅色部分)。有了同步屏障的存在,msg_2 和 msg_M 這兩個異步消息可以被優(yōu)先處理,而后面的 msg_3 等同步消息則不會被處理。那么這些同步消息什么時候可以被處理呢?那就需要先移除這個同步屏障,即調(diào)用removeSyncBarrier()。

同步消息的應(yīng)用場景

似乎在日常的應(yīng)用開發(fā)中,很少會用到同步屏障。那么,同步屏障在系統(tǒng)源碼中有哪些使用場景呢?Android 系統(tǒng)中的 UI 更新相關(guān)的消息即為異步消息,需要優(yōu)先處理。

比如,在 View 更新時,draw、requestLayout、invalidate 等很多地方都調(diào)用了ViewRootImpl#scheduleTraversals(),如下:

//ViewRootImpl.java

void scheduleTraversals() {
    if (!mTraversalScheduled) {
        mTraversalScheduled = true;
        //開啟同步屏障
        mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
        //發(fā)送異步消息
        mChoreographer.postCallback(
                Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
        if (!mUnbufferedInputDispatch) {
            scheduleConsumeBatchedInput();
        }
        notifyRendererOfFramePending();
        pokeDrawLockIfNeeded();
    }
}

postCallback()最終走到了ChoreographerpostCallbackDelayedInternal()

  private void postCallbackDelayedInternal(int callbackType,
            Object action, Object token, long delayMillis) {
        if (DEBUG_FRAMES) {
            Log.d(TAG, "PostCallback: type=" + callbackType- ", action=" + action + ",                      token=" + token  =" + delayMillis);
        }
        synchronized (mLock) {
        final long now = SystemClock.uptimeMillis();
        final long dueTime = now + delayMillis;
        mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);

        if (dueTime <= now) {
            scheduleFrameLocked(now);
        } else {
            Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
            msg.arg1 = callbackType;
            msg.setAsynchronous(true);   //異步消息
            mHandler.sendMessageAtTime(msg, dueTime);
        }
    }
}

這里就開啟了同步屏障,并發(fā)送異步消息,由于 UI 更新相關(guān)的消息是優(yōu)先級最高的,這樣系統(tǒng)就會優(yōu)先處理這些異步消息。

最后,當(dāng)要移除同步屏障的時候需要調(diào)用ViewRootImpl#unscheduleTraversals()。

    void unscheduleTraversals() {
        if (mTraversalScheduled) {
            mTraversalScheduled = false;
            //移除同步屏障
            mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
            mChoreographer.removeCallbacks(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
        }
    }

總結(jié)

同步屏障的設(shè)置可以方便地處理那些優(yōu)先級較高的異步消息。當(dāng)我們調(diào)用Handler.getLooper().getQueue().postSyncBarrier() 并設(shè)置消息的setAsynchronous(true)時,target 即為 null ,也就開啟了同步屏障。當(dāng)在消息輪詢器 Looper 在loop()中循環(huán)處理消息時,如若開啟了同步屏障,會優(yōu)先處理其中的異步消息,而阻礙同步消息。

Handler常問面試題(探討與思考)

1. 一個線程有幾個 Handler?

2.一個線程有幾個 Looper?如何保證?

3.Handler內(nèi)存泄漏原因? 為什么其他的內(nèi)部類沒有說過有這個問題?

4.為何主線程可以new Handler?如果想要在子線程中new Handler 要做些什么準(zhǔn)備?

5.子線程中維護(hù)的Looper,消息隊(duì)列無消息的時候的處理方案是什么?有什么用?

6.既然可以存在多個 Handler 往 MessageQueue 中添加數(shù)據(jù)(發(fā)消息時各個 Handler 可能處于不同線程),那它內(nèi)部是如何確保線程安全的?取消息呢?

7.我們使用 Message 時應(yīng)該如何創(chuàng)建它?

8.Looper死循環(huán)為什么不會導(dǎo)致應(yīng)用卡死

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容