Broadcast 探究

  • Post author:
  • Post category:其他


最近终于把自己的状态调整过来了,看起书来也能有很多收获。

嗯,今天在看《Android艺术开发探索》这本书的时候(这本书还真的不错,每次看都有新的发现),看到了四大组件的工作原理,关于 Broadcast,有这样的一个疑问:

当App安装了没有运行的时候,如果App内部有静态注册的 Receiver,那么当一个满足 filter 条件的广播被发送的时候,是不是需要先启动这个 App,创建一个进程?关于 Activity 启动的时候,创建进程的代码,我还是有点印象的,但是 Broadcast 就完全不了解,所以既然有了疑问,那么就肯定要去找出答案才行。

下面所写的都是我翻了很多文章才记录下来的,嗯,希望通过写这次记录,能够对 Broadcast 有更好的理解,毕竟,我们项目里面用的很浅。


PS

:我刚开始是翻了很多文章,有 《Android 应用的安装流程解析》,《Broadcast 的注册》,《Broadcast 的工作原理》之类的但是都没有我想要的。但是最后我找了一篇非常牛逼的文章

品茗论道说广播(Broadcast内部机制讲解)

,看了之后恍然大悟,写的真的思路清晰,这篇文章基本都是此文的内容,只是按照我自己的思路整理了一下。


注1

:代码版本是 api 21,突然发现代码版本越低代码越好理解,反正本质是不会变的,一些行为变化可以专门去了解。


注2

:请原谅我盗图,我懒得自己画了。



总结

  • 广播分 普通、有序、粘性 三类。
  • 广播注册分两种:动态与静态。他们的区别,使用的区别,静态注册的广播的特殊处理(有序的原因)。
  • Flag标识,响应未运行的进程。
  • 前台队列与后台队列的区别。



概述

我们知道,Activity 的都是由 AMS 管理的,其实 Broadcast 也是,当一个广播被发送的时候,AMS 会对自己管理的所有 Receiver 做一个决策,将回调满足条件的 Receiver 的 onReceive 方法。

在这里插入图片描述

在Android系统中,接收广播的组件叫作receiver,而且

receiver还分为动态和静态的

。动态receiver是在运行期通过调用registerReceiver()注册的,而静态receiver则是在AndroidManifest.xml中声明的。

除了这个,Android 还有有序广播和普通广播的概念。有序广播就是广播接收者会按照优先级依次接受广播,而且还可以中断广播,让后面的收不到。



两种 Receiver

这里我们先介绍动态的 Receiver。



动态 Receiver

动态的 Receiver 就是在代码中注册的 Receiver,当App没有运行的时候(进程不存在),是无法接受到广播的,说的更直白一点,就是 AMS 那里没有这个 Receiver 相关的信息(这个后面会说到源码的东西)。

这里我们看一下注册相关的源码,因为有几个类非常的重要,毕竟我们阅读源码还是要对几个常见的类熟悉一下,以后阅读起来就会更快。

首先,当我们使用代码注册一个 receiver 的时候,会调用到下面的方法:

android.app.ContextImpl#registerReceiverInternal

    private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId,
            IntentFilter filter, String broadcastPermission,
            Handler scheduler, Context context) {
        IIntentReceiver rd = null;
        if (receiver != null) {
            // mPackageInfo 与 context 都不为空
            if (mPackageInfo != null && context != null) {
                if (scheduler == null) {
                    scheduler = mMainThread.getHandler();
                }
                // 查找和context对应的“子哈希表”里的ReceiverDispatcher,如果找不到,就重新new一个
                // 这个 rd 对象非常重要
                rd = mPackageInfo.getReceiverDispatcher(
                    receiver, context, scheduler,
                    mMainThread.getInstrumentation(), true);
            } else {
                if (scheduler == null) {
                    scheduler = mMainThread.getHandler();
                }
                rd = new LoadedApk.ReceiverDispatcher(
                        receiver, context, scheduler, null, true).getIIntentReceiver();
            }
        }
        try {
            // 看到 ActivityManagerNative 这种东西,就知道会调到 AMS 里面
            return ActivityManagerNative.getDefault().registerReceiver(
                    mMainThread.getApplicationThread(), mBasePackageName,
                    rd, filter, broadcastPermission, userId);
        } catch (RemoteException e) {
            return null;
        }
    }

ReceiverDispatcher 这个类要重点说一下。

我们知道,

BroadcastReceiver 作为一个 Android 的组件是不能直接跨进程传递的,它有另外一个类可以实现跨进程传递,就是 IIntentReceiver 类,它是一个 Binder 接口,具体实现是 LoadedApk.ReceiverDispatcher.InnerReceiver

ReceiverDispatcher 这个类内部同时保存了 BroadcastReceiver 和 InnerReceiver。BroadcastReceiver 就是App这边注册的,InnerReceiver 用于跨进程传输给与 AMS 通信。等到 AMS 通知app端,需要触发 BroadcastReceiver 的 onReceive 方法的时候,就可以通过这个 ReceiverDispatcher 来找到 BroadcastReceiver 。

在这里插入图片描述

由于一个应用里可能会注册多个动态receiver,所以这种一一对应关系最好整理成表,这个表就位于LoadedApk中。

在Android的架构里,应用进程里是用LoadedApk来对应一个apk的,进程里加载了多少个apk,就会有多少LoadedApk。每个LoadedApk里会有一张”

关于本apk动态注册的所有receiver

“的哈希表(mReceivers)。

android.app.LoadedApk

    private final ArrayMap<Context, ArrayMap<BroadcastReceiver, ReceiverDispatcher>> mReceivers
        = new ArrayMap<Context, ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>>();

该表的key项是我们比较熟悉的Context,也就是说可以是Activity、Service或Application。而value项则是另一张“子哈希表”。

这个“子哈希表”,key值为BroadcastReceiver,value项为ReceiverDispatcher。

我们回想一下上面的内容,根据 context,我们可以找到一个“表中表”,根据 BroadcastReceiver,我们可以从“表中表”里面获取到 ReceiverDispatcher,因为 ReceiverDispatcher 里面存了 InnerReceiver 对象,所以这样关系就对应起来了。为啥要说这个,是因为这就是数据结构哒,有蛋疼的面试官会问的。

现在,我们就有了一个大概的注册模型了,还是上一张图吧。嗯,画的不太好,反正理解就好了。

在这里插入图片描述

好的,我们接着往下看:

com.android.server.am.ActivityManagerService#registerReceiver

// 其中的receiver参数为IIntentReceiver型,正对应着ReceiverDispatcher中那个binder实体。
// 也就是说,每个客户端的ReceiverDispatcher,会对应AMS端的一个ReceiverList。
ReceiverList rl = (ReceiverList)mRegisteredReceivers.get(receiver.asBinder());
if (rl == null) {
    rl = new ReceiverList(this, callerApp, callingPid, callingUid,
                          userId, receiver);
    if (rl.app != null) {
        rl.app.receivers.add(rl);
    } else {
        try {
            receiver.asBinder().linkToDeath(rl, 0);
        } catch (RemoteException e) {
            return sticky;
        }
        rl.linkedToDeath = true;
    }
    // 如果map里面没有,就创建一个新的,存进去
    mRegisteredReceivers.put(receiver.asBinder(), rl);
}
// 创建 BroadcastFilter
// filter参数指明了用户对哪些intent感兴趣。
// 对同一个BroadcastReceiver对象来说,可以注册多个感兴趣的filter
BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage,
                                         permission, callingUid, userId);
// 最终 IntentFilter 信息汇总到AMS的mRegisteredReceivers表中。
rl.add(bf);


ReceiverList继承于ArrayList<BroadcastFilter>

,而BroadcastFilter又继承于IntentFilter,所以ReceiverList可以被理解为一个IntentFilter数组列表。因为一个广播可以添加多个 IntentFilter,所以一个 BroadcastReceiver 对应一个列表,方便遍历。

在这里插入图片描述



静态 Receiver

静态receiver是指那些在AndroidManifest.xml文件中声明的receiver,它们的信息会在系统启动时,由Package Manager Service(PMS)解析并记录下来。以后,当AMS调用PMS的接口来查询“和intent匹配的组件”时,PMS内部就会去查询当初记录下来的数据,并把结果返回AMS。有的同学认为静态receiver是常驻内存的,这种说法并不准确。因为常驻内存的只是静态receiver的描述性信息,并不是receiver实体本身。


这部分的东西,可以去查找应用安装的过程源码解析。



发送广播

使用 sendBroadcast() 就可以发送一个广播。而sendOrderedBroadcast(),则是用来向系统发出有序广播(Ordered broadcast)的。

这种有序广播对应的所有接收器只能按照一定的优先级顺序,依次接收intent。这些优先级一般记录在AndroidManifest.xml文件中,具体位置在元素的android:priority属性中,其数值越大表示优先级越高,取值范围为-1000到1000。另外,有时候我们也可以调用IntentFilter对象的setPriority()方法来设置优先级。

对于有序广播而言,前面的接收者可以对接收到的广播intent进行处理,并将处理结果放置到广播intent中,然后传递给下一个接收者。需要注意的是,前面的接收者有权终止广播的进一步传播。也就是说,如果广播被前面的接收者终止了,那么后面的接收器就再也无法接收到广播了。

还有一个怪东西,叫做sticky广播,它又是什么呢?简单地说,sticky广播可以保证“在广播递送时尚未注册的receiver”,一旦日后注册进系统,就能够马上接到“错过”的sticky广播。

感觉有些不好理解,但是幸运的是它现在被废弃了。

下面,举一个例子,从代码入手:

mContext = getApplicationContext(); 
Intent intent = new Intent();  
intent.setAction("com.android.xxxxx");  
intent.setFlags(1);  
mContext.sendBroadcast(intent);

sendBroadcast 会通过 ContextImpl 走到 AMS 侧。然后通过层层调用又会调用到 broadcastIntentLocked 方法。

com.android.server.am.ActivityManagerService#broadcastIntentLocked

在分析这个方法之前,我们可以思考一下几个问题。

首先,有些广播intent只能由具有特定权限的进程发送,而有些广播intent在发送之前需要做一些其他动作。当然,如果发送方进程是系统进程、phone进程、shell进程,或者具有root权限的进程,那么必然有权发出广播。

发送广播时,还需要考虑所发送的广播是否需要有序(ordered)递送。而且,receiver本身又分为动态注册和静态声明的,这让我们面对的情况更加复杂。从目前的代码来看,静态receiver一直是按照有序方式递送的,而动态receiver则需要根据ordered参数的值,做不同的处理。当我们需要有序递送时,AMS会把动态receivers和静态receivers合并到一张表中,这样才能依照receiver的优先级,做出正确的处理,此时动态receivers和静态receivers可能呈现一种交错顺序。

另一方面,有些广播是需要发给特定目标组件的,这个也要加以考虑。

现在我们来分析broadcastIntentLocked()函数,我们可以将其逻辑大致整理成以下几步:

1) 为intent添加FLAG_EXCLUDE_STOPPED_PACKAGES标记;

2) 处理和package相关的广播;

3) 处理其他一些系统广播;

4) 判断当前是否有权力发出广播;

5) 如果要发出sticky广播,那么要更新一下系统中的sticky广播列表;

6) 查询和intent匹配的静态receivers;

7) 查询和intent匹配的动态receivers;

8) 尝试向并行receivers递送广播;

9) 整合(剩下的)并行receivers,以及静态receivers,形成一个串行receivers表;

10) 尝试逐个向串行receivers递送广播。

下面我们来详细说这几个部分。



为intent添加FLAG_EXCLUDE_STOPPED_PACKAGES标记

com.android.server.am.ActivityManagerService#broadcastIntentLocked

        intent = new Intent(intent);

        // By default broadcasts do not go to stopped apps.
        intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES);

为什么intent要添加FLAG_EXCLUDE_STOPPED_PACKAGES标记呢?

原因是这样的,在Android 3.1之后,PMS加强了对“处于停止状态的”应用的管理。如果一个应用在安装后从来没有启动过,或者已经被用户强制停止了,那么这个应用就处于停止状态(stopped state)。为了达到精细调整的目的,Android增加了2个flag:FLAG_INCLUDE_STOPPED_PACKAGES和FLAG_EXCLUDE_STOPPED_PACKAGES,以此来表示intent是否要激活“处于停止状态的”应用。

**默认情况下,AMS是不会把intent广播发给“处于停止状态的”应用的。**据说Google这样做是为了防止一些流氓软件或病毒干坏事。当然,如果广播的发起者认为自己的确需要广播到“处于停止状态的”应用的话,它可以让intent携带FLAG_INCLUDE_STOPPED_PACKAGES标记,从这个标记的注释可以了解到,

如果这两个标记同时设置的话,那么FLAG_INCLUDE_STOPPED_PACKAGES标记会“取胜”,它会覆盖掉framework自动添加的FLAG_EXCLUDE_STOPPED_PACKAGES标记。



处理和package相关的广播

接下来需要处理一些系统级的“Package广播”,这些主要从PKMS(Package Manager Service)处发来。比如,当PKMS处理APK的添加、删除或改动时,一般会发出类似下面的广播:ACTION_PACKAGE_ADDED、ACTION_PACKAGE_REMOVED、ACTION_PACKAGE_CHANGED、ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE、ACTION_UID_REMOVED。

AMS必须确保发送“包广播”的发起方具有BROADCAST_PACKAGE_REMOVED权限,如果没有,那么AMS会抛出异常(SecurityException)。接着,AMS判断如果是某个用户id被删除了的(Intent.ACTION_UID_REMOVED),那么必须把这件事通知给“电池状态服务”(Battery Stats Service)。另外,如果是SD卡等外部设备上的应用不可用了,这常常是因为卡被unmount了,此时PKMS会发出Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE,而AMS则需要把SD卡上的所有包都强制停止(forceStopPackageLocked()),并立即发出另一个“Package广播”——EXTERNAL_STORAGE_UNAVAILABLE。

如果只是某个外部包被删除或改动了,则要进一步判断intent里是否携带了EXTRA_DONT_KILL_APP额外数据,如果没有携带,说明需要立即强制结束package,否则,不强制结束package。看来有些应用即使在删除或改动了包后,还会在系统(内存)中保留下来并继续运行。另外,如果是删除包的话,此时要发出PACKAGE_REMOVED广播。



处理其他一些系统广播

broadcastIntentLocked()不但要对“Package广播”进行处理,还要关心其他一些系统广播。比如ACTION_TIMEZONE_CHANGED、ACTION_CLEAR_DNS_CACHE、PROXY_CHANGE_ACTION等等,感兴趣的同学可以自行研究这些广播的意义。



必要时更新一下系统中的sticky广播列表

一开始会判断一下发起方是否具有发出sticky广播的能力,比如说要拥有android.Manifest.permission.BROADCAST_STICKY权限等等。判断合格后,broadcastIntentLocked()会更新AMS里的一张表——mStickyBroadcasts。

看一下它的结构:

com.android.server.am.ActivityManagerService#mStickyBroadcasts

    /**
     * State of all active sticky broadcasts per user.  Keys are the action of the
     * sticky Intent, values are an ArrayList of all broadcasted intents with
     * that action (which should usually be one).  The SparseArray is keyed
     * by the user ID the sticky is for, and can include UserHandle.USER_ALL
     * for stickies that are sent to all users.
     */
    final SparseArray<ArrayMap<String, ArrayList<Intent>>> mStickyBroadcasts =
            new SparseArray<ArrayMap<String, ArrayList<Intent>>>();

SparseArray 就不介绍了。它的key是user id,我们知道在Android里面,每个应用都有一个自己的user id,所以这里储存了所有应用的sticky广播。它的 value 是一个 ArrayMap<String, ArrayList<Intent>>。上面的英文注释解释的比较清楚了,String是action,ArrayList 是 Intent 集合。也就是说当我们注册一个 receiver 的时候,有可能会收到多个 sticky 广播。

在这里插入图片描述



尝试向并行receivers递送广播

com.android.server.am.ActivityManagerService#broadcastIntentLocked

        int NR = registeredReceivers != null ? registeredReceivers.size() : 0;
		// 非有序广播
        if (!ordered && NR > 0) {
            // If we are not serializing this broadcast, then send the
            // registered receivers separately so they don't wait for the
            // components to be launched.
            final BroadcastQueue queue = broadcastQueueForIntent(intent);
            BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
                    callerPackage, callingPid, callingUid, resolvedType, requiredPermission,
                    appOp, registeredReceivers, resultTo, resultCode, resultData, map,
                    ordered, sticky, false, userId);
            if (DEBUG_BROADCAST) Slog.v(
                    TAG, "Enqueueing parallel broadcast " + r);
            final boolean replaced = replacePending && queue.replaceParallelBroadcastLocked(r);
            if (!replaced) {
                queue.enqueueParallelBroadcastLocked(r);
                queue.scheduleBroadcastsLocked();
            }
            // 将集合清空
            registeredReceivers = null;
            NR = 0;
        }

简单地说就是,new一个BroadcastRecord节点,并插入BroadcastQueue内的并行处理队列,最后发起实际的广播调度(scheduleBroadcastsLocked())。

scheduleBroadcastsLocked 实际上只是发送了一个消息,触发了下面的这个方法,这很常见。

com.android.server.am.BroadcastQueue#processNextBroadcast

在分析这个方法之前,我们先说说两个变量:

com.android.server.am.ActivityManagerService#broadcastIntentLocked

        // Figure out who all will receive this broadcast.
        List receivers = null;
        List<BroadcastFilter> registeredReceivers = null;

前文已经说过,有些广播是需要有序递送的。为了合理处理“有序递送”和“并行递送”,所以就搞出了两个集合。

receivers

主要用于记录“有序递送”的receiver

,而registeredReceivers则用于

记录与intent相匹配的动态注册的receiver。

于这两个list的大致运作是这样的,我们先利用包管理器的queryIntentReceivers()接口,查询出和intent匹配的所有静态receivers,此时所返回的查询结果本身已经排好序了,代码如下:

// 代码内部调用了 AppGlobals.getPackageManager().queryIntentReceivers(intent, resolvedType, STOCK_PM_FLAGS, user);
receivers = collectReceiverComponents(intent, resolvedType, callingUid, users);

而对于动态注册的receiver信息,就不是从包管理器获取了,这些信息本来就记录在AMS之中,此时只需调用:

registeredReceivers = mReceiverResolver.queryIntent(intent, resolvedType, false, userId);

就可以了。注意,此时返回的registeredReceivers中的子项是

没有经过排序

的。

如果我们要“并行递送”广播, 只需要遍历 registeredReceivers中的值就好了,遍历完成之后,需要将改值改为 null,因为代码下面的逻辑是合并 registeredReceivers 与 receivers,不置为null的话就会出问题(所以说,这块代码写得像裹脚布一样) 。

如果我们要“串行递送”广播,那么必须考虑把

registeredReceivers表合并到receivers表

中去。我们知道,

一开始receivers列表中只记录了一些静态receiver,这些receiver将会被“有序递送”。现在我们只需再遍历一下registeredReceivers列表,并将其中的每个子项插入到receivers列表的合适地方,就可以合并出一条顺序列表了

。当然,如果registeredReceivers已经被设为null了(如果广播是有序的话,就不会为空),就无所谓合并了。

为什么静态声明的receiver只会“有序递送”呢?我想也许和这种receiver的复杂性有关系,因为在需要递送广播时,receiver所属的进程可能还没有启动呢,所以也许会涉及到启动进程的流程或者启动多个进程,这些都是比较复杂的流程。

搞清楚了这两个变量,现在,我们回到

com.android.server.am.BroadcastQueue#processNextBroadcast

函数中:

com.android.server.am.BroadcastQueue#processNextBroadcast

            // First, deliver any non-serialized broadcasts right away.
            while (mParallelBroadcasts.size() > 0) {
                r = mParallelBroadcasts.remove(0);
                r.dispatchTime = SystemClock.uptimeMillis();
                r.dispatchClockTime = System.currentTimeMillis();
                final int N = r.receivers.size();
                if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG, "Processing parallel broadcast ["
                        + mQueueName + "] " + r);
                for (int i=0; i<N; i++) {
                    Object target = r.receivers.get(i);
                    if (DEBUG_BROADCAST)  Slog.v(TAG,
                            "Delivering non-ordered on [" + mQueueName + "] to registered "
                            + target + ": " + r);
                    deliverToRegisteredReceiverLocked(r, (BroadcastFilter)target, false);
                }
                addBroadcastToHistoryLocked(r);
                if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG, "Done with parallel broadcast ["
                        + mQueueName + "] " + r);
            }

可以看到是一个循环,调用了 deliverToRegisteredReceiverLocked 就不管了,也就是说,AMS 是不会管哪个进程先收到广播,反正我全部发出去了,这里就是无需广播的处理逻辑,至于怎么回调到 APP 端的,下面再说。



尝试逐个向串行receivers递送广播

现在要开始尝试逐个向串行receivers递送广播了。

com.android.server.am.ActivityManagerService#broadcastIntentLocked

        if ((receivers != null && receivers.size() > 0)
                || resultTo != null) {
            BroadcastQueue queue = broadcastQueueForIntent(intent);
            BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
                    callerPackage, callingPid, callingUid, resolvedType,
                    requiredPermission, appOp, receivers, resultTo, resultCode,
                    resultData, map, ordered, sticky, false, userId);
            if (DEBUG_BROADCAST) Slog.v(
                    TAG, "Enqueueing ordered broadcast " + r
                    + ": prev had " + queue.mOrderedBroadcasts.size());
            if (DEBUG_BROADCAST) {
                int seq = r.intent.getIntExtra("seq", -1);
                Slog.i(TAG, "Enqueueing broadcast " + r.intent.getAction() + " seq=" + seq);
            }
            boolean replaced = replacePending && queue.replaceOrderedBroadcastLocked(r); 
            if (!replaced) {
                queue.enqueueOrderedBroadcastLocked(r);
                queue.scheduleBroadcastsLocked();
            }
        }

与并行广播的逻辑差不多,都创建了新的 BroadcastRecord,只不过进入的队列不一样。并行广播被添加到了 mParallelBroadcasts 中,有序广播被添加到了 mOrderedBroadcasts 中。

我们接着看

com.android.server.am.BroadcastQueue#processNextBroadcast

中的处理逻辑:

嗯,代码比较长,我就不贴了,大致流程是这样的。

如果目标进程已经存在了,那么app.thread肯定不为null,直接调用processCurBroadcastLocked()即可,否则就需要启动新进程了。启动的过程是异步的,可能很耗时,所以要把BroadcastRecord节点记入mPendingBroadcast。

其实看到这里,我的疑问就已经解答了,但是我又有一个别的疑问了。

AMS 是如何挂起等待一个 APP 进程 收到广播才通知下一个 APP 进程的?是循环还是wait?

(既然使用了消息队列,那么肯定是在一个线程中处理的)

我在 com.android.server.am.BroadcastQueue#processNextBroadcast 方法中看到了下面的代码:

com.android.server.am.BroadcastQueue#processNextBroadcast

            if (mPendingBroadcast != null) {
                ...

                // 查看接收广播的进程死了没
                boolean isDead;
                synchronized (mService.mPidsSelfLocked) {
                    ProcessRecord proc = mService.mPidsSelfLocked.get(mPendingBroadcast.curApp.pid);
                    isDead = proc == null || proc.crashing;
                }
                // 如果没死,这里直接返回了,就是说不处理下一个接收者了
                // 那么等app端处理完之后,肯定还需要触发一下 AMS 的 processNextBroadcast
                // 不然循环就中断了
                if (!isDead) {
                    // It's still alive, so keep waiting
                    return;
                } else {
                    // 挂了就下一个
                    Slog.w(TAG, "pending app  ["
                            + mQueueName + "]" + mPendingBroadcast.curApp
                            + " died before responding to broadcast");
                    mPendingBroadcast.state = BroadcastRecord.IDLE;
                    mPendingBroadcast.nextReceiver = mPendingBroadcastRecvIndex;
                    mPendingBroadcast = null;
                }
            }

根据我们的思路,看看app端回调完了会不会触发 processNextBroadcast 这个方法,恢复循环。代码流程就不贴了,直接查看

这篇文章

。可以看到最后 AMS.finishReceiver 这个方法里面还是调用了这个方法的。

至于广播的超时与广播的拦截这里就不分析了,因为暂时对这些不太感兴趣,太概可以猜个七七八八,很多东西都是一样的,有兴趣的可以去看看原文。

额外说一个东西,我在跟踪消息队列的时候,发现了两个 BroadcastQueue。

com.android.server.am.ActivityManagerService

        mFgBroadcastQueue = new BroadcastQueue(this, mHandler,
                "foreground", BROADCAST_FG_TIMEOUT, false);
        mBgBroadcastQueue = new BroadcastQueue(this, mHandler,
                "background", BROADCAST_BG_TIMEOUT, true);

从前面的源码中我们可以看到,将一个广播放到哪个队列里面,是通过下面的方法:

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

看来,我们只需要通过 Intent 设置 flag,就可以指定放入的队列。

mFgBroadcastQueue 与 mBgBroadcastQueue 除了超时时间不一样(如果你想要更长的超时时间,可以指定到后台队列,默认是后台),还有一个参数不一样

mDelayBehindServices

。这个变量有注释,但是我还没太理解。它对有序广播会有一定的影响,大致的意思是在某个特殊的情况下,会延迟广播的发送。



版权声明:本文为weixin_43130724原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。