Handler異步消息與同步屏障

Handler Message

在Handler中,大致分為3種Message面睛,分別是同步消息辟宗,異步消息和同步屏障碗硬,他們?nèi)叨际荕essage,只是成員變量有些區(qū)別绵咱;

同步消息

也就是我一般使用的Message碘饼,再通過Handler進(jìn)行sendMessage到消息隊列,前提是構(gòu)造Handler時候傳的構(gòu)造參數(shù)async為false

Handler mHandler = new Handler()
//或者
Handler mHandler = new Handler(false)

通過以上mHandler發(fā)送的Message都是同步消息悲伶,且Message會與該mHandler綁定艾恼,即:

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    //將Handler賦值給Message的target變量
    msg.target = this;
    //mAsynchronous為false,為同步消息
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}

以上就是發(fā)送一個同步消息流程

異步消息

根據(jù)前面同步消息的發(fā)送流程可以知道拢切,只要通過構(gòu)造參async為true的Handler發(fā)送的Message都為異步消息蒂萎,即:

//構(gòu)造一個發(fā)送處理異步消息的Handler
Handler mHandler = new Handle(true)

所以通過這個mHandler發(fā)送的Message,在queueMessage時候淮椰,都會把Message的asynchronous設(shè)置為true五慈,即異步消息

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    //將Handler賦值給Message的target變量
    msg.target = this;
    //mAsynchronous為true,為異步消息
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}

以上就是發(fā)送一個異步消息的流程

同步屏障

雖然沒有消息兩字主穗,但是同步屏障本質(zhì)也是Message對象泻拦,同步屏障個人覺得可以把它解釋為:屏障同步消息的消息,它和前面同步異步消息的最大區(qū)別就是忽媒,Message.target變量為null争拐,它的作用就是在消息隊列(MessageQueue)取出消息的時候,屏蔽掉同步消息晦雨,優(yōu)先獲取異步消息的作用

沒有同步屏障

假設(shè)下圖是沒有添加同步屏障的消息隊列

graph LR
B[同步消息A] --> C[同步消息B]
C[同步消息B] --> D[異步消息A]

那么在loop()循環(huán)從MessageQueue取出消息的順序是

  1. 同步消息A
  2. 同步消息B
  3. 異步消息A
  4. 睡眠等待消息插入...

有同步屏障

如果在消息隊列中插入同步屏障架曹,如下圖所示

graph LR
A[同步屏障]-->B[同步消息A]
B[同步消息A] --> C[同步消息B]
C[同步消息B] --> D[異步消息A]

那么在loop()循環(huán)從MessageQueue取出消息的順序是

  1. 異步消息A
  2. 睡眠等待消息插入...

此時消息隊列變?yōu)?/p>

graph LR
A[同步屏障]-->B[同步消息A]
B[同步消息A] --> C[同步消息B]

因為同步屏障在獲取消息的時候隘冲,屏蔽掉同步消息,只取出異步消息绑雄,那同步消息怎么辦展辞?只有當(dāng)同步屏障被屏蔽被移除的時候才能獲取到同步消息;

通過代碼在消息隊列添加同步屏障

//在MessageQueue類中万牺,添加同步屏障可以通過postSyncBarrier方法罗珍,該方法是被隱藏的,如果要調(diào)用的話只能通過反射機(jī)制進(jìn)行調(diào)用
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++;
        //創(chuàng)建一個Message對象脚粟,所以說同步屏障的本質(zhì)就是Message
        //而且在這里看不到Message.target的賦值覆旱,所以可以說同步屏障和同步異步消息的區(qū)別就是target是否為空
        final Message msg = Message.obtain();
        msg.markInUse();
        msg.when = when;
        //記錄該同步屏障,后面可以通過token來移除該同步屏障
        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;
        }
        //最后返回token扣唱,可以根據(jù)這個token移除同步屏障
        return token;
    }
}

//移除同步屏障
//token就是添加同步屏障時候返回的唯一標(biāo)示
public void removeSyncBarrier(int token) {
    // Remove a sync barrier token from the queue.
    // If the queue is no longer stalled by a barrier then wake it.
    synchronized (this) {
        Message prev = null;
        Message p = mMessages;
        //找到要移除的同步屏障位置
        while (p != null && (p.target != null || p.arg1 != token)) {
            prev = p;
            p = p.next;
        }
        if (p == null) {
            throw new IllegalStateException("The specified message queue synchronization "
                    + " barrier token has not been posted or has already been removed.");
        }
        final boolean needWake;
        if (prev != null) {
            prev.next = p.next;
            needWake = false;
        } else {
            mMessages = p.next;
            needWake = mMessages == null || mMessages.target != null;
        }
        p.recycleUnchecked();

        // If the loop is quitting then it is already awake.
        // We can assume mPtr != 0 when mQuitting is false.
        if (needWake && !mQuitting) {
            nativeWake(mPtr);
        }
    }
}

通過自己代碼給消息隊列添加同步屏障

//獲取當(dāng)前線程Looper對象的消息隊列
MessageQueue queue=handler.getLooper().getQueue();
//通過反射調(diào)用
Method method=MessageQueue.class.getDeclaredMethod("postSyncBarrier");
token= (int) method.invoke(queue);

//移除同步屏障
//獲取當(dāng)前線程Looper對象的消息隊列
MessageQueue queue=handler.getLooper().getQueue();
Method method=MessageQueue.class.getDeclaredMethod("removeSyncBarrier",int.class);
method.invoke(queue,token);
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市厕宗,隨后出現(xiàn)的幾起案子画舌,更是在濱河造成了極大的恐慌堕担,老刑警劉巖已慢,帶你破解...
    沈念sama閱讀 216,997評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異霹购,居然都是意外死亡佑惠,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,603評論 3 392
  • 文/潘曉璐 我一進(jìn)店門齐疙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來膜楷,“玉大人,你說我怎么就攤上這事贞奋《奶” “怎么了?”我有些...
    開封第一講書人閱讀 163,359評論 0 353
  • 文/不壞的土叔 我叫張陵轿塔,是天一觀的道長特愿。 經(jīng)常有香客問我,道長勾缭,這世上最難降的妖魔是什么揍障? 我笑而不...
    開封第一講書人閱讀 58,309評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮俩由,結(jié)果婚禮上毒嫡,老公的妹妹穿的比我還像新娘。我一直安慰自己幻梯,他們只是感情好兜畸,可當(dāng)我...
    茶點故事閱讀 67,346評論 6 390
  • 文/花漫 我一把揭開白布努释。 她就那樣靜靜地躺著,像睡著了一般咬摇。 火紅的嫁衣襯著肌膚如雪洽洁。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,258評論 1 300
  • 那天菲嘴,我揣著相機(jī)與錄音饿自,去河邊找鬼。 笑死龄坪,一個胖子當(dāng)著我的面吹牛昭雌,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播健田,決...
    沈念sama閱讀 40,122評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼烛卧,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了妓局?” 一聲冷哼從身側(cè)響起总放,我...
    開封第一講書人閱讀 38,970評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎好爬,沒想到半個月后局雄,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,403評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡存炮,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,596評論 3 334
  • 正文 我和宋清朗相戀三年炬搭,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片穆桂。...
    茶點故事閱讀 39,769評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡宫盔,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出享完,到底是詐尸還是另有隱情灼芭,我是刑警寧澤,帶...
    沈念sama閱讀 35,464評論 5 344
  • 正文 年R本政府宣布般又,位于F島的核電站彼绷,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏倒源。R本人自食惡果不足惜苛预,卻給世界環(huán)境...
    茶點故事閱讀 41,075評論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望笋熬。 院中可真熱鬧热某,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,705評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至秘遏,卻和暖如春丘薛,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背邦危。 一陣腳步聲響...
    開封第一講書人閱讀 32,848評論 1 269
  • 我被黑心中介騙來泰國打工洋侨, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人倦蚪。 一個月前我還...
    沈念sama閱讀 47,831評論 2 370
  • 正文 我出身青樓希坚,卻偏偏與公主長得像,于是被迫代替她去往敵國和親陵且。 傳聞我的和親對象是個殘疾皇子裁僧,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,678評論 2 354

推薦閱讀更多精彩內(nèi)容

  • 異步消息處理線程啟動后會進(jìn)入一個無限的循環(huán)體之中,每循環(huán)一次慕购,從其內(nèi)部的消息隊列中取出一個消息聊疲,然后回調(diào)相應(yīng)的消息...
    cxm11閱讀 6,424評論 2 39
  • Android的View模型采用的是單線程模型,所有的視圖相關(guān)的操作都必須在主線程中進(jìn)行沪悲,否則會拋出異常获洲。某些耗時...
    CyanStone閱讀 1,077評論 0 4
  • ThreadLocal 是用來存儲指定線程的數(shù)據(jù)的,當(dāng)某些數(shù)據(jù)的作用域是該指定線程并且該數(shù)據(jù)需要貫穿該線程的所有執(zhí)...
    Hsicen閱讀 554評論 0 0
  • 前言 在Android開發(fā)的多線程應(yīng)用場景中可训,Handler機(jī)制十分常用 今天昌妹,我將手把手帶你深入分析Handle...
    BrotherChen閱讀 474評論 0 0
  • 我會通過講解 Handler/Looper/MessageQueue/Message 這幾個類的作用以及它們之間的...
    realxz閱讀 506評論 0 8