说说Android的广播(2) - 并发队列和串行队列

简介: 前面我们讲了,消息分为普通消息和有序消息两大类。普通消息是可以并发的,由于是并发的,这些广播的处理者之间互相是不依赖的。 另外,并发队列和串行队列都各维护了一条后台广播队列和前台广播队列。如果这个消息足够重要,想走快速通道的话,可以选择使用前台广播队列。 对于并发队列,如果是进程活着,动

并发队列和串行队列

前面我们讲了,消息分为普通消息和有序消息两大类。普通消息是可以并发的,由于是并发的,这些广播的处理者之间互相是不依赖的。

另外,并发队列和串行队列都各维护了一条后台广播队列和前台广播队列。如果这个消息足够重要,想走快速通道的话,可以选择使用前台广播队列。

对于并发队列,如果是进程活着,动态注册到队列里的,系统会通过并发的方式迅速将消息广播出去,就跟大家所想象的一样。
但是如果需要通过启动新进程才能处理消息的情况,为了避免同时启动大量进程,系统还是采用串行的方式来处理的。后面我们会分析这个过程的细节。
我们先来看一张思维导图来个整体的印象:
Android_

队列的定义

这两个队列定义于frameworks/base/services/core/java/com/android/server/am/BroadcastQueue.java中

/**
 * Lists of all active broadcasts that are to be executed immediately
 * (without waiting for another broadcast to finish).  Currently this only
 * contains broadcasts to registered receivers, to avoid spinning up
 * a bunch of processes to execute IntentReceiver components.  Background-
 * and foreground-priority broadcasts are queued separately.
 */
final ArrayList<BroadcastRecord> mParallelBroadcasts = new ArrayList<>();

/**
 * List of all active broadcasts that are to be executed one at a time.
 * The object at the top of the list is the currently activity broadcasts;
 * those after it are waiting for the top to finish.  As with parallel
 * broadcasts, separate background- and foreground-priority queues are
 * maintained.
 */
final ArrayList<BroadcastRecord> mOrderedBroadcasts = new ArrayList<>();

前台队列和后台队列

在发送广播时,可以通过设置Intent.FLAG_RECEIVER_FOREGROUND属性来指定使用前台队列。
ActivityManagerService中直接就定义了两个BroadcastQueue:

440    BroadcastQueue mFgBroadcastQueue;
441    BroadcastQueue mBgBroadcastQueue;
442    // Convenient for easy iteration over the queues. Foreground is first
443    // so that dispatch of foreground broadcasts gets precedence.
444    final BroadcastQueue[] mBroadcastQueues = new BroadcastQueue[2];

如何获取这个队列呢,AMS中提供了broadcastQueueForIntent方法,很简单,就是判断Intent.FLAG_RECEIVER_FOREGROUND啦:

446    BroadcastQueue broadcastQueueForIntent(Intent intent) {
447        final boolean isFg = (intent.getFlags() & Intent.FLAG_RECEIVER_FOREGROUND) != 0;
448        if (DEBUG_BROADCAST_BACKGROUND) Slog.i(TAG_BROADCAST,
449                "Broadcast intent " + intent + " on "
450                + (isFg ? "foreground" : "background") + " queue");
451        return (isFg) ? mFgBroadcastQueue : mBgBroadcastQueue;
452    }

如何加入到广播队列中

BroadcastQueue中定义了enqueueParallelBroadcastLocked方法,可以将BroadcastRecord对象加入到并发队列中。

public void enqueueParallelBroadcastLocked(BroadcastRecord r) {
    mParallelBroadcasts.add(r);
    r.enqueueClockTime = System.currentTimeMillis();
}

当然,也有对应的串行队列的入队列方法:

public void enqueueOrderedBroadcastLocked(BroadcastRecord r) {
    mOrderedBroadcasts.add(r);
    r.enqueueClockTime = System.currentTimeMillis();
}

BroadcastRecord

不管是并发队列还是串行队列,都是BroadcastRecord对象的ArrayList。所有后面对这个队列的处理,都是基于这里面的对象。大部分的字段看起来都是蛮眼熟的哈,基本上上面我们写代码时传进来的,取一个公共集,可以适用于三种大的消息类型。

final class BroadcastRecord extends Binder {
    final Intent intent;    // the original intent that generated us
    final ComponentName targetComp; // original component name set on the intent
    final ProcessRecord callerApp; // process that sent this
    final String callerPackage; // who sent this
    final int callingPid;   // the pid of who sent this
    final int callingUid;   // the uid of who sent this
    final boolean ordered;  // serialize the send to receivers?
    final boolean sticky;   // originated from existing sticky data?
    final boolean initialSticky; // initial broadcast from register to sticky?
    final int userId;       // user id this broadcast was for
    final String resolvedType; // the resolved data type
    final String[] requiredPermissions; // permissions the caller has required
    final int appOp;        // an app op that is associated with this broadcast
    final BroadcastOptions options; // BroadcastOptions supplied by caller
    final List receivers;   // contains BroadcastFilter and ResolveInfo
    IIntentReceiver resultTo; // who receives final result if non-null
    long enqueueClockTime;  // the clock time the broadcast was enqueued
    long dispatchTime;      // when dispatch started on this set of receivers
    long dispatchClockTime; // the clock time the dispatch started
    long receiverTime;      // when current receiver started for timeouts.
    long finishTime;        // when we finished the broadcast.
    int resultCode;         // current result code value.
    String resultData;      // current result data value.
    Bundle resultExtras;    // current result extra data values.
    boolean resultAbort;    // current result abortBroadcast value.
    int nextReceiver;       // next receiver to be executed.
    IBinder receiver;       // who is currently running, null if none.
    int state;
    int anrCount;           // has this broadcast record hit any ANRs?
    BroadcastQueue queue;   // the outbound queue handling this broadcast

    static final int IDLE = 0;
    static final int APP_RECEIVE = 1;
    static final int CALL_IN_RECEIVE = 2;
    static final int CALL_DONE_RECEIVE = 3;
    static final int WAITING_SERVICES = 4;

    // The following are set when we are calling a receiver (one that
    // was found in our list of registered receivers).
    BroadcastFilter curFilter;

    // The following are set only when we are launching a receiver (one
    // that was found by querying the package manager).
    ProcessRecord curApp;       // hosting application of current receiver.
    ComponentName curComponent; // the receiver class that is currently running.
    ActivityInfo curReceiver;   // info about the receiver that is currently running.

发送消息

只放到广播队列里面还只是第一步,我们还需要通过消息队列将消息发送出去。
mBroadcastsScheduled用来标识发送的状态,如果已经处于此状态,就直接返回。如果没有发送,就发送一条BROADCAST_INTENT_MSG消息出去。

public void scheduleBroadcastsLocked() {
    if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Schedule broadcasts ["
            + mQueueName + "]: current="
            + mBroadcastsScheduled);

    if (mBroadcastsScheduled) {
        return;
    }
    mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, this));
    mBroadcastsScheduled = true;
}

这个方法对于并发队列和串行队列都是一样的。

目录
相关文章
|
存储 消息中间件 Android开发
Android源码分析--广播的注册、发送和接收
Android源码分析--广播的注册、发送和接收
245 0
Android源码分析--广播的注册、发送和接收
|
Android开发
Android Service全屏广播
场景 产品需要一个全屏广播,不管用户在那个界面每隔一段时间都会弹出一个滚动的文字,而且这个不是用推送来做的,后台返回一组数据,然后客户端自己进行处理!如果有更好的方法或者建议都可以跟我说一下,万分感谢,第一次做这个也不知道用什么合适!
171 0
|
消息中间件 存储 Android开发
Android 四大组件之一:BroadCastReceiver动态注册广播流程
Android 四大组件之一:BroadCastReceiver动态注册广播流程
242 0
Android 四大组件之一:BroadCastReceiver动态注册广播流程
|
设计模式 Android开发
Android移动应用基础教程【广播机制】
Android移动应用基础教程【广播机制】
204 0
Android移动应用基础教程【广播机制】
|
物联网 Android开发
Android 解析蓝牙广播数据
Android 解析蓝牙广播数据
1064 1
Android 解析蓝牙广播数据
|
API Android开发 UED
【Android 进程保活】应用进程拉活 ( 应用进程拉活简介 | 广播拉活 | 显示广播与隐式广播 | 全家桶拉活 )
【Android 进程保活】应用进程拉活 ( 应用进程拉活简介 | 广播拉活 | 显示广播与隐式广播 | 全家桶拉活 )
377 0
|
消息中间件 Android开发
【Android 异步操作】HandlerThread 示例 ( 初始化并执行 | 获取Looper | 获取 Handler | 获取消息队列 | 设置空闲队列 | 代码示例 )
【Android 异步操作】HandlerThread 示例 ( 初始化并执行 | 获取Looper | 获取 Handler | 获取消息队列 | 设置空闲队列 | 代码示例 )
215 0
【Android 异步操作】HandlerThread 示例 ( 初始化并执行 | 获取Looper | 获取 Handler | 获取消息队列 | 设置空闲队列 | 代码示例 )
|
Java Android开发 开发者
【Android 电量优化】JobScheduler 源码分析 ( JobServiceContext 源码分析 | 闭环操作总结 | 用户提交任务 | 广播接收者接受相关广播触发任务执行 )★
【Android 电量优化】JobScheduler 源码分析 ( JobServiceContext 源码分析 | 闭环操作总结 | 用户提交任务 | 广播接收者接受相关广播触发任务执行 )★
175 0
|
缓存 Android开发
【Android 电量优化】电量优化 ( 充电状态获取 | 主动获取充电状态 | 广播接受者监听充电状态 | 被动获取充电状态 | 注册空广播接受者获取历史广播 )
【Android 电量优化】电量优化 ( 充电状态获取 | 主动获取充电状态 | 广播接受者监听充电状态 | 被动获取充电状态 | 注册空广播接受者获取历史广播 )
391 0
|
缓存 监控 安全
【Android 内存优化】Bitmap 内存缓存 ( Bitmap 内存复用 | 弱引用 | 引用队列 | 针对不同 Android 版本开发不同的 Bitmap 复用策略 | 工具类代码 )
【Android 内存优化】Bitmap 内存缓存 ( Bitmap 内存复用 | 弱引用 | 引用队列 | 针对不同 Android 版本开发不同的 Bitmap 复用策略 | 工具类代码 )
324 0