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

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

目录
相关文章
|
2月前
|
Android开发 Kotlin
Android面试题之Kotlin中如何实现串行和并行任务?
本文介绍了 Kotlin 中 `async` 和 `await` 在并发编程中的应用,包括并行与串行任务的处理方法。并通过示例代码展示了如何启动并收集异步任务的结果。
32 0
|
2月前
|
Java 调度 Android开发
Android面试题之Kotlin中async 和 await实现并发的原理和面试总结
本文首发于公众号“AntDream”,详细解析了Kotlin协程中`async`与`await`的原理及其非阻塞特性,并提供了相关面试题及答案。协程作为轻量级线程,由Kotlin运行时库管理,`async`用于启动协程并返回`Deferred`对象,`await`则用于等待该对象完成并获取结果。文章还探讨了协程与传统线程的区别,并展示了如何取消协程任务及正确释放资源。
35 0
|
6月前
|
安全 Android开发 Kotlin
Android面试题之Kotlin协程并发问题和互斥锁
Kotlin的协程提供轻量级并发解决方案,如`kotlinx.coroutines`库。`Mutex`用于同步,确保单个协程访问共享资源。示例展示了`withLock()`、`lock()`、`unlock()`和`tryLock()`的用法,这些方法帮助在协程中实现线程安全,防止数据竞争。
78 1
|
物联网 Android开发
Android 解析蓝牙广播数据
Android 解析蓝牙广播数据
1153 1
Android 解析蓝牙广播数据
|
存储 消息中间件 Android开发
Android源码分析--广播的注册、发送和接收
Android源码分析--广播的注册、发送和接收
295 0
Android源码分析--广播的注册、发送和接收
|
消息中间件 存储 Android开发
Android 四大组件之一:BroadCastReceiver动态注册广播流程
Android 四大组件之一:BroadCastReceiver动态注册广播流程
288 0
Android 四大组件之一:BroadCastReceiver动态注册广播流程
|
设计模式 Android开发
Android移动应用基础教程【广播机制】
Android移动应用基础教程【广播机制】
246 0
Android移动应用基础教程【广播机制】
|
消息中间件 Android开发
【Android 异步操作】HandlerThread 示例 ( 初始化并执行 | 获取Looper | 获取 Handler | 获取消息队列 | 设置空闲队列 | 代码示例 )
【Android 异步操作】HandlerThread 示例 ( 初始化并执行 | 获取Looper | 获取 Handler | 获取消息队列 | 设置空闲队列 | 代码示例 )
249 0
【Android 异步操作】HandlerThread 示例 ( 初始化并执行 | 获取Looper | 获取 Handler | 获取消息队列 | 设置空闲队列 | 代码示例 )
|
算法 安全 Java
【Android 内存优化】垃圾回收算法 ( 分代收集算法 | Serial 收集器 | ParNew 收集器 | Parallel Scavenge 收集器 | CMS 并发标记清除收集器 )
【Android 内存优化】垃圾回收算法 ( 分代收集算法 | Serial 收集器 | ParNew 收集器 | Parallel Scavenge 收集器 | CMS 并发标记清除收集器 )
226 0
【Android 内存优化】垃圾回收算法 ( 分代收集算法 | Serial 收集器 | ParNew 收集器 | Parallel Scavenge 收集器 | CMS 并发标记清除收集器 )
|
Android开发
Android并发修改异常:java.util.ConcurrentModificationException
Android并发修改异常:java.util.ConcurrentModificationException
352 0