美文网首页
2020-04-19-Android-前台广播和后台广播

2020-04-19-Android-前台广播和后台广播

作者: 耿望 | 来源:发表于2020-04-20 23:38 被阅读0次

前面介绍全局广播的时候,提到过根据intent的flag不同,广播会被加入到不同的队列中。

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

前台广播

默认情况下,Intent是不带FLAG_RECEIVER_FOREGROUND的flag的,所以我们默认使用的都是后台广播。
如果要使用前台广播,也很简单,只需要加上flag即可。过程中遇到一个问题,在android新版本上,隐式调用的广播,不能通过静态注册接收了。

intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);

接下来看看前后台广播队列的构造有什么区别

        // 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);

构造函数有5个参数,后面两个参数不同,第一个参数BroadcastConstants主要设置广播参数,比如超时时间等。我们可以看到前台广播超时时间是10秒,后台是60秒。不过,只有串行广播,也就是有序广播才需要考虑超时。还有一点需要注意的是,静态注册的广播实际上都是一种串行的方式。

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

实际上超时的机制在前面介绍全局广播的时候有提到过,在processNextBroadcastLocked方法中,有一处调用了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方法中发送了一个延时消息,提醒ANR问题。

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

超时时间到达之后,会调用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处会重新判断一次超时时间,如果广播已经分发给下一个receiver,则timeoutTime 大于now,return,不会发生ANR。
注释2处调用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处,如果当前的广播接收者和下一个广播接收者不处于同一个进程,且当前有正在启动的后台服务(用户维度)那么BroadcastQueue进入WAITING_SERVICES状态。
该状态对processNextBroadcast内对有序广播的处理以及广播超时方法broadcastTimeoutLocked都有一定的影响。
这可能就是我们常说前台广播比后台快的原因,因为前台广播不需要等待后台服务启动。

参考

说说Android的广播(4) - 前台广播为什么比后台广播快?
Broadcast之前/后台广播队列
Android Broadcast广播机制分析
Android广播的超时机制
Android中有序广播的基本使用方法
Android Q上broadcast 的新特性:广播延时方案

相关文章

  • 2020-04-19-Android-前台广播和后台广播

    前面介绍全局广播的时候,提到过根据intent的flag不同,广播会被加入到不同的队列中。 前台广播 默认情况下,...

  • BroadcastReceiver广播原理分析

    一、 Boradcast前题概要 1、广播分为前台广播和后台广播 发送前台广播(Intent.FLAG_RECEI...

  • 说说Android的广播(4) - 前台广播为什么比后台广播快?

    说说Android的广播(4) - 前台广播为什么比后台广播快? 前台广播为什么比后台广播快 讨论超时的细节之前,...

  • Broadcast(六)总结

    1、前台广播,单个receiver处理超过10s就ANR,后台广播(默认也是后台),单个receiver处理超过6...

  • Service实现轮询更新Ui

    Service定时轮询来更新前台的信息 Activity内部定义一个广播内部内,用于接受Service发送的广播,...

  • 有序广播和无序广播

    1、 发送无序广播:创建Intent,设置action,通过sendBroadcast(intent)就可以把广播...

  • 入门第五天

    广播接收器 分为动态广播器和静态广播器,静态广播器只能接受显式广播 广播 分为标准广播和有序广播,标准广播使得所有...

  • 安卓广播机制学习笔记

    一,基本概念 1.广播队列 安卓原生有两个广播队列,在AMS中初始化,由构造函数可以看出5个构造参数意义 前台广播...

  • 进程保活

    Android 进程的 优先级 前台进程正在和用户交互 的act,前台运行的service,广播接受者的 onRe...

  • 第五章 全局大喇叭,详解广播机制

    5.1广播机制简介 广播分为标准广播和有序广播 标准广播:异步执行广播,广播发出后,所有的广播接收器都会几乎在同时...

网友评论

      本文标题:2020-04-19-Android-前台广播和后台广播

      本文链接:https://www.haomeiwen.com/subject/ieambhtx.html