從isAsynchronous方法來(lái)看看Handler異步消息與同步屏障(SyncBarrier)

前言

Android的消息機(jī)制之前有一篇文章有寫(xiě),里面具體講到了Handler怎么發(fā)送和處理消息的整個(gè)過(guò)程萎攒。感興趣的同學(xué)可以先跳轉(zhuǎn)過(guò)去看看 從Handler.post(Runnable r)再一次梳理Android的消息機(jī)制(以及handler的內(nèi)存泄露)

在看消息機(jī)制的時(shí)候挪略,不管是把消息加入隊(duì)列链瓦,還是取出隊(duì)列,Message有個(gè)isAsynchronous方法一直沒(méi)關(guān)注搓谆,今天來(lái)看看這個(gè)方法到底是做什么的刁愿。

    /**
     * Returns true if the message is asynchronous, meaning that it is not
     * subject to {@link Looper} synchronization barriers.
     *
     * @return True if the message is asynchronous.
     *
     * @see #setAsynchronous(boolean)
     */
    public boolean isAsynchronous() {
        return (flags & FLAG_ASYNCHRONOUS) != 0;
    }

Handler同步屏障(SyncBarrier)

要理解這個(gè)方法的含義绰寞,我們要先了解一下Handler的同步屏障機(jī)制。通常我們使用Handler發(fā)消息的時(shí)候,都是用的默認(rèn)的構(gòu)造方法生成Handler克握,然后用send方法來(lái)發(fā)送消息蕾管,其實(shí)這時(shí)候我們發(fā)送的都是同步消息枷踏,發(fā)出去之后就會(huì)在消息隊(duì)列里面排隊(duì)處理菩暗。我們都知道,Android系統(tǒng)16ms會(huì)刷新一次屏幕旭蠕,如果主線程的消息過(guò)多停团,在16ms之內(nèi)沒(méi)有執(zhí)行完,必然會(huì)造成卡頓或者掉幀掏熬。那怎么才能不排隊(duì)佑稠,沒(méi)有延時(shí)的處理呢?這個(gè)時(shí)候就需要異步消息旗芬,在處理異步消息的時(shí)候舌胶,我們就需要同步屏障,讓異步消息不用排隊(duì)等候處理疮丛♂I可以理解為同步屏障是一堵墻,把同步消息隊(duì)列攔住誊薄,先處理異步消息履恩,等異步消息處理完了,這堵墻就會(huì)取消呢蔫,然后繼續(xù)處理同步消息切心。

怎么來(lái)使用這個(gè)同步屏障呢?在MessageQueue里面有postSyncBarrier方法:

    public int postSyncBarrier() {
        return postSyncBarrier(SystemClock.uptimeMillis());
    }

    private int postSyncBarrier(long when) {
        // Enqueue a new sync barrier token.
        // We don't need to wake the queue because the purpose of a barrier is to stall it.
        synchronized (this) {
            final int token = mNextBarrierToken++;
            final Message msg = Message.obtain();
            msg.markInUse();
            msg.when = when;
            msg.arg1 = token;

            Message prev = null;
            Message p = mMessages;
            if (when != 0) {
                while (p != null && p.when <= when) {
                    prev = p;
                    p = p.next;
                }
            }
            if (prev != null) { // invariant: p == prev.next
                msg.next = p;
                prev.next = msg;
            } else {
                msg.next = p;
                mMessages = msg;
            }
            return token;
        }
    }

其實(shí)很簡(jiǎn)單片吊,就是創(chuàng)建了一個(gè)消息绽昏,但是值得注意的是,這個(gè)消息沒(méi)有target俏脊,普通的消息的必須有target的(不然交給誰(shuí)來(lái)處理消息呢全谤?具體的可以看開(kāi)頭的文章鏈接)。然后我們來(lái)看看怎么取出消息联予。

處理異步消息

來(lái)到MessageQueuenext方法:

    Message next() {
        省略代碼
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }

            nativePollOnce(ptr, nextPollTimeoutMillis);

            synchronized (this) {
                // Try to retrieve the next message.  Return if found.
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                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());
                }
            省略代碼
        }
    }

這里可以看到有一個(gè)很關(guān)鍵的判斷啼县,上面我們知道屏障消息的target為空,所以這里判斷為true沸久,while循環(huán)直到取出異步消息終止季眷。接下來(lái)的處理就跟同步消息一樣了,這里不贅述卷胯。

怎么發(fā)送異步消息

那怎么來(lái)發(fā)送異步消息呢子刮?MessagesetAsynchronous方法可以直接設(shè)置為異步消息

    public void setAsynchronous(boolean async) {
        if (async) {
            flags |= FLAG_ASYNCHRONOUS;
        } else {
            flags &= ~FLAG_ASYNCHRONOUS;
        }
    }

還有就是可以看到Handler的構(gòu)造方法

public Handler(boolean async)
public Handler(@Nullable Callback callback, boolean async)
public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async)

async參數(shù)可以控制是否發(fā)送異步消息,如果設(shè)置為true,Handler發(fā)送的都將是異步消息挺峡。

哪里有應(yīng)用呢

我們平時(shí)要發(fā)送同步屏障postSyncBarrier需要反射才能使用

    public void postSyncBarrier() {
       Method method = MessageQueue.class.getDeclaredMethod("postSyncBarrier");
       token = (int) method.invoke(Looper.getMainLooper().getQueue());
   }

   public void removeSyncBarrier() {
       Method method = MessageQueue.class
    .     getDeclaredMethod("removeSyncBarrier", int.class);
        method.invoke(Looper.getMainLooper().getQueue(), token);}
   }

在Android系統(tǒng)里面為了更快響應(yīng)UI刷新在ViewRootImpl.scheduleTraversals也有應(yīng)用:

void scheduleTraversals() {
    if (!mTraversalScheduled) {
        mTraversalScheduled = true;
        mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
        mChoreographer.postCallback(
                Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
        if (!mUnbufferedInputDispatch) {
            scheduleConsumeBatchedInput();
        }
        notifyRendererOfFramePending();
        pokeDrawLockIfNeeded();
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末葵孤,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子橱赠,更是在濱河造成了極大的恐慌尤仍,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,651評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件狭姨,死亡現(xiàn)場(chǎng)離奇詭異宰啦,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)饼拍,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,468評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)赡模,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人师抄,你說(shuō)我怎么就攤上這事漓柑。” “怎么了叨吮?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,931評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵辆布,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我挤安,道長(zhǎng)谚殊,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,218評(píng)論 1 292
  • 正文 為了忘掉前任蛤铜,我火速辦了婚禮嫩絮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘围肥。我一直安慰自己剿干,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,234評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布穆刻。 她就那樣靜靜地躺著置尔,像睡著了一般。 火紅的嫁衣襯著肌膚如雪氢伟。 梳的紋絲不亂的頭發(fā)上榜轿,一...
    開(kāi)封第一講書(shū)人閱讀 51,198評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音朵锣,去河邊找鬼谬盐。 笑死,一個(gè)胖子當(dāng)著我的面吹牛诚些,可吹牛的內(nèi)容都是我干的飞傀。 我是一名探鬼主播,決...
    沈念sama閱讀 40,084評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼砸烦!你這毒婦竟也來(lái)了弃鸦?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 38,926評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤幢痘,失蹤者是張志新(化名)和其女友劉穎唬格,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體雪隧,經(jīng)...
    沈念sama閱讀 45,341評(píng)論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡西轩,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,563評(píng)論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了脑沿。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,731評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡马僻,死狀恐怖庄拇,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情韭邓,我是刑警寧澤措近,帶...
    沈念sama閱讀 35,430評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站女淑,受9級(jí)特大地震影響瞭郑,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜鸭你,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,036評(píng)論 3 326
  • 文/蒙蒙 一屈张、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧袱巨,春花似錦阁谆、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,676評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至嫉入,卻和暖如春焰盗,著一層夾襖步出監(jiān)牢的瞬間咒林,已是汗流浹背熬拒。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,829評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留映九,地道東北人梦湘。 一個(gè)月前我還...
    沈念sama閱讀 47,743評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親捌议。 傳聞我的和親對(duì)象是個(gè)殘疾皇子哼拔,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,629評(píng)論 2 354