您的位置:首页 > 移动开发 > Android开发

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

2016-06-06 19:14 309 查看

并发队列和串行队列

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

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

对于并发队列,如果是进程活着,动态注册到队列里的,系统会通过并发的方式迅速将消息广播出去,就跟大家所想象的一样。

但是如果需要通过启动新进程才能处理消息的情况,为了避免同时启动大量进程,系统还是采用串行的方式来处理的。后面我们会分析这个过程的细节。

我们先来看一张思维导图来个整体的印象:



队列的定义

这两个队列定义于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 broadcast 并发