2020-04-19-Android-前臺(tái)廣播和后臺(tái)廣播

前面介紹全局廣播的時(shí)候猫态,提到過(guò)根據(jù)intent的flag不同该园,廣播會(huì)被加入到不同的隊(duì)列中伐弹。

    BroadcastQueue broadcastQueueForIntent(Intent intent) {
        if (isOnOffloadQueue(intent.getFlags())) {
            if (DEBUG_BROADCAST_BACKGROUND) {
                Slog.i(TAG_BROADCAST,
                        "Broadcast intent " + intent + " on offload queue");
            }
            return mOffloadBroadcastQueue;
        }

        final boolean isFg = (intent.getFlags() & Intent.FLAG_RECEIVER_FOREGROUND) != 0;
        if (DEBUG_BROADCAST_BACKGROUND) Slog.i(TAG_BROADCAST,
                "Broadcast intent " + intent + " on "
                + (isFg ? "foreground" : "background") + " queue");
        return (isFg) ? mFgBroadcastQueue : mBgBroadcastQueue;
    }

前臺(tái)廣播

默認(rèn)情況下拉馋,Intent是不帶FLAG_RECEIVER_FOREGROUND的flag的,所以我們默認(rèn)使用的都是后臺(tái)廣播。
如果要使用前臺(tái)廣播煌茴,也很簡(jiǎn)單随闺,只需要加上flag即可。過(guò)程中遇到一個(gè)問題蔓腐,在android新版本上矩乐,隱式調(diào)用的廣播,不能通過(guò)靜態(tài)注冊(cè)接收了回论。

intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);

接下來(lái)看看前后臺(tái)廣播隊(duì)列的構(gòu)造有什么區(qū)別

        // Broadcast policy parameters
        final BroadcastConstants foreConstants = new BroadcastConstants(
                Settings.Global.BROADCAST_FG_CONSTANTS);
        foreConstants.TIMEOUT = BROADCAST_FG_TIMEOUT;

        final BroadcastConstants backConstants = new BroadcastConstants(
                Settings.Global.BROADCAST_BG_CONSTANTS);
        backConstants.TIMEOUT = BROADCAST_BG_TIMEOUT;
        mFgBroadcastQueue = new BroadcastQueue(this, mHandler,
                "foreground", foreConstants, false);
        mBgBroadcastQueue = new BroadcastQueue(this, mHandler,
                "background", backConstants, true);

構(gòu)造函數(shù)有5個(gè)參數(shù)散罕,后面兩個(gè)參數(shù)不同,第一個(gè)參數(shù)BroadcastConstants主要設(shè)置廣播參數(shù)傀蓉,比如超時(shí)時(shí)間等欧漱。我們可以看到前臺(tái)廣播超時(shí)時(shí)間是10秒,后臺(tái)是60秒僚害。不過(guò)硫椰,只有串行廣播,也就是有序廣播才需要考慮超時(shí)萨蚕。還有一點(diǎn)需要注意的是靶草,靜態(tài)注冊(cè)的廣播實(shí)際上都是一種串行的方式。

    BroadcastQueue(ActivityManagerService service, Handler handler,
            String name, BroadcastConstants constants, boolean allowDelayBehindServices) {
        mService = service;
        mHandler = new BroadcastHandler(handler.getLooper());
        mQueueName = name;
        mDelayBehindServices = allowDelayBehindServices;

        mConstants = constants;
        mDispatcher = new BroadcastDispatcher(this, mConstants, mHandler, mService);
    }
    private void updateConstants() {
        synchronized (mParser) {
            try {
                mParser.setString(Settings.Global.getString(mResolver, mSettingsKey));
            } catch (IllegalArgumentException e) {
                Slog.e(TAG, "Bad broadcast settings in key '" + mSettingsKey + "'", e);
                return;
            }

            // Unspecified fields retain their current value rather than revert to default
            TIMEOUT = mParser.getLong(KEY_TIMEOUT, TIMEOUT);
            SLOW_TIME = mParser.getLong(KEY_SLOW_TIME, SLOW_TIME);
            DEFERRAL = mParser.getLong(KEY_DEFERRAL, DEFERRAL);
            DEFERRAL_DECAY_FACTOR = mParser.getFloat(KEY_DEFERRAL_DECAY_FACTOR,
                    DEFERRAL_DECAY_FACTOR);
            DEFERRAL_FLOOR = mParser.getLong(KEY_DEFERRAL_FLOOR, DEFERRAL_FLOOR);
            ALLOW_BG_ACTIVITY_START_TIMEOUT = mParser.getLong(KEY_ALLOW_BG_ACTIVITY_START_TIMEOUT,
                    ALLOW_BG_ACTIVITY_START_TIMEOUT);
        }
    }

實(shí)際上超時(shí)的機(jī)制在前面介紹全局廣播的時(shí)候有提到過(guò)岳遥,在processNextBroadcastLocked方法中奕翔,有一處調(diào)用了setBroadcastTimeoutLocked。

        if (! mPendingBroadcastTimeoutMessage) {
            long timeoutTime = r.receiverTime + mConstants.TIMEOUT;
            if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
                    "Submitting BROADCAST_TIMEOUT_MSG ["
                    + mQueueName + "] for " + r + " at " + timeoutTime);
            setBroadcastTimeoutLocked(timeoutTime);
        }

在setBroadcastTimeoutLocked方法中發(fā)送了一個(gè)延時(shí)消息浩蓉,提醒ANR問題派继。

    final void setBroadcastTimeoutLocked(long timeoutTime) {
        if (! mPendingBroadcastTimeoutMessage) {
            Message msg = mHandler.obtainMessage(BROADCAST_TIMEOUT_MSG, this);
            mHandler.sendMessageAtTime(msg, timeoutTime);
            mPendingBroadcastTimeoutMessage = true;
        }
    }

超時(shí)時(shí)間到達(dá)之后,會(huì)調(diào)用broadcastTimeoutLocked方法捻艳。

    final void broadcastTimeoutLocked(boolean fromMsg) {
        if (fromMsg) {
            mPendingBroadcastTimeoutMessage = false;
        }
        //……
        long now = SystemClock.uptimeMillis();
        BroadcastRecord r = mDispatcher.getActiveBroadcastLocked();
        if (fromMsg) {
            if (!mService.mProcessesReady) {
                // Only process broadcast timeouts if the system is ready; some early
                // broadcasts do heavy work setting up system facilities
                return;
            }
            //……
            long timeoutTime = r.receiverTime + mConstants.TIMEOUT;//1
            if (timeoutTime > now) {
                // We can observe premature timeouts because we do not cancel and reset the
                // broadcast timeout message after each receiver finishes.  Instead, we set up
                // an initial timeout then kick it down the road a little further as needed
                // when it expires.
                if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
                        "Premature timeout ["
                        + mQueueName + "] @ " + now + ": resetting BROADCAST_TIMEOUT_MSG for "
                        + timeoutTime);
                setBroadcastTimeoutLocked(timeoutTime);
                return;
            }
        }
        //……
        // Move on to the next receiver.
        finishReceiverLocked(r, r.resultCode, r.resultData,
                r.resultExtras, r.resultAbort, false);//2
        scheduleBroadcastsLocked();
        //……
    }

注釋1處會(huì)重新判斷一次超時(shí)時(shí)間驾窟,如果廣播已經(jīng)分發(fā)給下一個(gè)receiver,則timeoutTime 大于now认轨,return绅络,不會(huì)發(fā)生ANR。
注釋2處調(diào)用finishReceiverLocked方法嘁字。

    public boolean finishReceiverLocked(BroadcastRecord r, int resultCode,
            String resultData, Bundle resultExtras, boolean resultAbort, boolean waitForServices) {
        //……
        // If we want to wait behind services *AND* we're finishing the head/
        // active broadcast on its queue
        if (waitForServices && r.curComponent != null && r.queue.mDelayBehindServices
                && r.queue.mDispatcher.getActiveBroadcastLocked() == r) {
            //……
            // Don't do this if the next receive is in the same process as the current one.
            if (receiver == null || nextReceiver == null
                    || receiver.applicationInfo.uid != nextReceiver.applicationInfo.uid
                    || !receiver.processName.equals(nextReceiver.processName)) {
                // In this case, we are ready to process the next receiver for the current broadcast,
                // but are on a queue that would like to wait for services to finish before moving
                // on.  If there are background services currently starting, then we will go into a
                // special state where we hold off on continuing this broadcast until they are done.
                if (mService.mServices.hasBackgroundServicesLocked(r.userId)) {
                    Slog.i(TAG, "Delay finish: " + r.curComponent.flattenToShortString());
                    r.state = BroadcastRecord.WAITING_SERVICES;//1
                    return false;
                }
            }
        }
        //……
        return state == BroadcastRecord.APP_RECEIVE
                || state == BroadcastRecord.CALL_DONE_RECEIVE;
    }

注釋1處恩急,如果當(dāng)前的廣播接收者和下一個(gè)廣播接收者不處于同一個(gè)進(jìn)程,且當(dāng)前有正在啟動(dòng)的后臺(tái)服務(wù)(用戶維度)那么BroadcastQueue進(jìn)入WAITING_SERVICES狀態(tài)纪蜒。
該狀態(tài)對(duì)processNextBroadcast內(nèi)對(duì)有序廣播的處理以及廣播超時(shí)方法broadcastTimeoutLocked都有一定的影響衷恭。
這可能就是我們常說(shuō)前臺(tái)廣播比后臺(tái)快的原因,因?yàn)榍芭_(tái)廣播不需要等待后臺(tái)服務(wù)啟動(dòng)纯续。

參考

說(shuō)說(shuō)Android的廣播(4) - 前臺(tái)廣播為什么比后臺(tái)廣播快随珠?
Broadcast之前/后臺(tái)廣播隊(duì)列
Android Broadcast廣播機(jī)制分析
Android廣播的超時(shí)機(jī)制
Android中有序廣播的基本使用方法
Android Q上broadcast 的新特性:廣播延時(shí)方案

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末灭袁,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子窗看,更是在濱河造成了極大的恐慌简卧,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,509評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件烤芦,死亡現(xiàn)場(chǎng)離奇詭異举娩,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)构罗,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門铜涉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人遂唧,你說(shuō)我怎么就攤上這事芙代。” “怎么了盖彭?”我有些...
    開封第一講書人閱讀 163,875評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵纹烹,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我召边,道長(zhǎng)铺呵,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,441評(píng)論 1 293
  • 正文 為了忘掉前任隧熙,我火速辦了婚禮片挂,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘贞盯。我一直安慰自己音念,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,488評(píng)論 6 392
  • 文/花漫 我一把揭開白布躏敢。 她就那樣靜靜地躺著闷愤,像睡著了一般。 火紅的嫁衣襯著肌膚如雪件余。 梳的紋絲不亂的頭發(fā)上讥脐,一...
    開封第一講書人閱讀 51,365評(píng)論 1 302
  • 那天,我揣著相機(jī)與錄音蛾扇,去河邊找鬼攘烛。 笑死魏滚,一個(gè)胖子當(dāng)著我的面吹牛镀首,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播鼠次,決...
    沈念sama閱讀 40,190評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼更哄,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼芋齿!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起成翩,我...
    開封第一講書人閱讀 39,062評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤觅捆,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后麻敌,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體栅炒,經(jīng)...
    沈念sama閱讀 45,500評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,706評(píng)論 3 335
  • 正文 我和宋清朗相戀三年术羔,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了赢赊。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,834評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡级历,死狀恐怖释移,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情寥殖,我是刑警寧澤玩讳,帶...
    沈念sama閱讀 35,559評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站嚼贡,受9級(jí)特大地震影響熏纯,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜粤策,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,167評(píng)論 3 328
  • 文/蒙蒙 一豆巨、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧掐场,春花似錦往扔、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,779評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至嚷堡,卻和暖如春蝗罗,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背蝌戒。 一陣腳步聲響...
    開封第一講書人閱讀 32,912評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工串塑, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人北苟。 一個(gè)月前我還...
    沈念sama閱讀 47,958評(píng)論 2 370
  • 正文 我出身青樓桩匪,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親友鼻。 傳聞我的和親對(duì)象是個(gè)殘疾皇子傻昙,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,779評(píng)論 2 354

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