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

并发队列和串行队列

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

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

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

队列的定义

这两个队列定义于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;
}

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

时间: 2024-12-02 20:34:59

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

axis2客户端无法实现并发只能是串行,不知道怎么并发实现请指教

问题描述 publicclassTestStub3implementsRunnable{publicstaticLoggerlogger=Logger.getLogger(TestStub3.class);privatestaticConfigurationContextctx=null;privatestaticResourceBundlerb=ResourceBundle.getBundle("usif");static{try{ctx=ConfigurationContextFa

说说Android的广播(3) - 什么样的广播是并发的?

什么样的广播是并发的? 现在让我们开始破解Android中的一个trick,普通广播都是并发的吗?带着这个问题,我们来看ActivityManagerService.broadcastIntentLocked中的实现逻辑. broadcastIntentLocked中的细节很多,我们放到后面讲,我们先只把跟并发和串行队列有关的部分专门提炼出来. receivers和registeredReceivers 画重点了,画重点了啊!下面我们要学习两个重要的概念,一个是receivers,另一个是reg

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

说说Android的广播(4) - 前台队列为什么比后台队列快? 前台队列为什么比后台队列快 讨论超时的细节之前,我们先讲讲对应用开发有帮助的,为什么前台队列比后台队列要快?应用开发的同学在给系统团队提意见的时候讲,说以前我们都是靠通过将广播消息设成前台广播的方式来做workaround来解决一些广播的性能问题的,你们系统为什么不能将后台广播做得跟前台广播一样快呢?这一定是设计上的问题. 其实,这种前台广播的设计,就是为了加速广播的性能而设计的.二者在设计思想上就有不同.根据应用层实际的需求,决

说说Android的广播(5) - 广播的历史

说说Android的广播(5) - 广播的历史 广播的历史 AMS中的历史信息 处理完广播之后,BroadcastQueue会记录一段历史用于调试: mBroadcastHistory记录最近的BroadcastRecord mBroadcastSummaryHistory记录最近的Intent mSummaryHistoryEnqueueTime记录最近的enqueueTime mSummaryHistoryDispatchTime记录最近的dispatchTime mSummaryHisto

说说Android的广播(1) - 普通广播,有序广播和粘性广播

说说Android的广播 对于Activity的启动流程,我们已经有了几个版本的分析了.这里我们分析一个更容易一些的,四大组件中最简单的Broadcast Receiver. 关于Broadcast,有几点需要了解.首先是广播的类型,然后是广播的发送方法,最后是广播是如何被接收的.这三者相辅相承的,比如普通广播和有序广播只有在详细了解了广播的接收过程了之后,才能真正明白它的含义. 广播的类型:普通广播,有序广播和粘性广播 普通的广播是不在意顺序的,最简单的理解是同时可以收到这个广播.如果应用是动

Android 注册广播方式及优缺点

 在android下,要想接受广播信息,那么这个广播接收器就得我们自己来实现了,我们可以继承BroadcastReceiver,就可以有一个广播接收器了.有个接收器还不够,我们还得重写BroadcastReceiver里面的onReceiver方法,然后注册广播 有两种方法,一种是代码动态注册: //生成广播处理  smsBroadCastReceiver = new SmsBroadCastReceiver();  //实例化过滤器并设置要过滤的广播 IntentFilter intentFi

android-关于Android的广播如何判断某个广播存在??

问题描述 关于Android的广播如何判断某个广播存在?? 就是我想判断一个广播是否存在,如果不存在就开启广播. (就在确定一个广播一直在) 解决方案 通过注销异常可以判断是否存在 try { unregisterReceiver(receiver); } catch (IllegalArgumentException e) { } 抛异常就是不存在,不报异常就是存在 解决方案二: 你的 问 题突 然改变了?

activity传数据-新手学Android的广播,为什么接收不到广播数据,急急急急

问题描述 新手学Android的广播,为什么接收不到广播数据,急急急急 我是动态注册的 ,代码如下: public class LoginActivity extends Activity implements OnClickListener{ public static String ACTION= "ok"; private Button button; @Override protected void onCreate(Bundle savedInstanceState) { s

android开发-android中单单消息对象才能放入到队列当中吗

问题描述 android中单单消息对象才能放入到队列当中吗 android中单单消息对象才能放入到队列当中吗 其他对象可以放到队列当中吗 队列实质就是一个对象吧, 那岂不是对象和对象结合生成新的对象,这么理解对吗 解决方案 不知道你说的是什么队列,消息队列当然是存放消息的队列,一般的队列当然是什么都可以放. 除了消息队列,我们还有任务队列,打印队列,算法中用到的数据结构的队列等等. 解决方案二: 消息对象可存放在消息队列中.队列肯定是对象,java中除了八种基本类型之外,一切皆对象.对象和对象结