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

…衆ロ難τιáo~ 提交于 2020-04-11 12:12:24

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

 

侯 亮

 

1 概述

        我们在编写Android程序时,常常会用到广播(Broadcast)机制。从易用性的角度来说,使用广播是非常简单的。不过,这个不是本文关心的重点,我们希望探索得再深入一点儿。我想,许多人也不想仅仅停留在使用广播的阶段,而是希望了解一些广播机制的内部机理。如果是这样的话,请容我斟一杯红茶,慢慢道来。

        简单地说,Android广播机制的主要工作是为了实现一处发生事情,多处得到通知的效果。这种通知工作常常要牵涉跨进程通讯,所以需要由AMS(Activity Manager Service)集中管理。

 

        在Android系统中,接收广播的组件叫作receiver,而且receiver还分为动态和静态的。动态receiver是在运行期通过调用registerReceiver()注册的,而静态receiver则是在AndroidManifest.xml中声明的。动态receiver比较简单,静态的就麻烦一些了,因为在广播递送之时,静态receiver所从属的进程可能还没有启动呢,这就需要先启动新的进程,费时费力。另一方面,有些时候用户希望广播能够按照一定顺序递送,为此,Android又搞出了ordered broadcast的概念。

        细节如此繁杂,非一言可以说清。我们先从receiver这一侧入手吧。

 

2 两种receiver

        Android中的receiver,分为“动态receiver”和“静态receiver”。

2.1 动态receiver

        动态receiver必须在运行期动态注册,其实际的注册动作由ContextImpl对象完成:

@Override
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) 
{    
    return registerReceiver(receiver, filter, null, null);
}
@Override
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
                               String broadcastPermission, Handler scheduler) 
{   
    return registerReceiverInternal(receiver, filter, broadcastPermission,
                                    scheduler, getOuterContext());
}

注册之时,用户会把一个自定义的receiver对象作为第一个参数传入。当然,用户的receiver都是继承于BroadcastReceiver的。使用过广播机制的程序员,对这个BroadcastReceiver应该都不陌生,这里就不多说了。我们需要关心的是,这个registerReceiverInternal()内部还包含了什么重要的细节。

        registerReceiverInternal()代码的截选如下:

private Intent registerReceiverInternal(BroadcastReceiver receiver,
                                        IntentFilter filter, String broadcastPermission,
                                        Handler scheduler, Context context) 
{
    IIntentReceiver rd = null;    
    if (receiver != null) 
    {        
        if (mPackageInfo != null && context != null) 
        {            
            if (scheduler == null) 
            {
                scheduler = mMainThread.getHandler();
            }            
            // 查找和context对应的“子哈希表”里的ReceiverDispatcher,如果找不到,就重新new一个
            rd = mPackageInfo.getReceiverDispatcher(receiver, context, scheduler,
                                                    mMainThread.getInstrumentation(), true);
        } 
        . . . . . .
    }    
    try 
    {        
        return ActivityManagerNative.getDefault().registerReceiver(
                mMainThread.getApplicationThread(), mBasePackageName,
                rd, filter, broadcastPermission);
    } 
    catch (RemoteException e) 
    {        
        return null;
    }
}

请大家注意那个rd对象(IIntentReceiver rd)。我们知道,在Android架构中,广播动作最终其实都是由AMS递送出来的。AMS利用binder机制,将语义传递给各个应用进程,应用进程再辗转调用到receiver的onReceive(),完成这次广播。而此处的rd对象正是承担“语义传递工作“的binder实体。

        为了管理这个重要的binder实体,Android搞出了一个叫做ReceiveDispatcher的类。该类的定义截选如下:

【frameworks/base/core/java/android/app/LoadedApk.java】

static final class ReceiverDispatcher 
{
    final static class InnerReceiver extends IIntentReceiver.Stub {
        . . . . . .
        . . . . . .
    }
    final IIntentReceiver.Stub mIIntentReceiver;   // 请注意这个域!它就是传到外面的rd。
    final BroadcastReceiver mReceiver;
    final Context mContext;
    final Handler mActivityThread;
    final Instrumentation mInstrumentation;
    final boolean mRegistered;
    final IntentReceiverLeaked mLocation;
    RuntimeException mUnregisterLocation;
    boolean mForgotten;
    . . . . . .

        这样看来,“动态注册的BroadcastReceiver”和“ReceiverDispatcher节点”具有一一对应的关系。示意图如下:

 

一个应用里可能会注册多个动态receiver,所以这种一一对应关系最好整理成表,这个表就位于LoadedApk中。前文mPackageInfo.getReceiverDispatcher()一句中的mPackageInfo就是LoadedApk对象。

        在Android的架构里,应用进程里是用LoadedApk来对应一个apk的,进程里加载了多少个apk,就会有多少LoadedApk。每个LoadedApk里会有一张“关于本apk动态注册的所有receiver”的哈希表(mReceivers)。当然,在LoadedApk初创之时,这张表只是个空表。

        mReceivers表的定义如下:

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

该表的key项是我们比较熟悉的Context,也就是说可以是Activity、Service或Application。而value项则是另一张“子哈希表”。这是个“表中表”的形式。言下之意就是,每个Context(比如一个activity),是可以注册多个receiver的,这个很好理解。mReceivers里的“子哈希表”的key值为BroadcastReceiver,value项为ReceiverDispatcher,示意图如下:

 

图:客户进程中的mReceivers表

        接下来我们继续看registerReceiverInternal(),它最终调用到

ActivityManagerNative.getDefault().registerReceiver(
                    mMainThread.getApplicationThread(), mBasePackageName,
                    rd, filter, broadcastPermission);

registerReceiver()函数的filter参数指明了用户对哪些intent感兴趣。对同一个BroadcastReceiver对象来说,可以注册多个感兴趣的filter,就好像声明静态receiver时,也可以为一个receiver编写多个<intent-filter>一样。这些IntentFilter信息会汇总到AMS的mRegisteredReceivers表中。在AMS端,我们可以这样访问相应的汇总表:

ReceiverList rl = (ReceiverList)mRegisteredReceivers.get(receiver.asBinder());

其中的receiver参数为IIntentReceiver型,正对应着ReceiverDispatcher中那个binder实体。也就是说,每个客户端的ReceiverDispatcher,会对应AMS端的一个ReceiverList。

        ReceiverList的定义截选如下:

class ReceiverList extends ArrayList<BroadcastFilter>
        implements IBinder.DeathRecipient 
{
    final ActivityManagerService owner; 
    public final IIntentReceiver receiver;    
    public final ProcessRecord app;    
    public final int pid;    
    public final int uid;
    BroadcastRecord curBroadcast = null;
    boolean linkedToDeath = false;
    String stringName;
    . . . . . .

ReceiverList继承于ArrayList<BroadcastFilter>,而BroadcastFilter又继承于IntentFilter,所以ReceiverList可以被理解为一个IntentFilter数组列表。

class BroadcastFilter extends IntentFilter {
    final ReceiverList receiverList;
    final String packageName;
    final String requiredPermission;
    . . . . . .

        现在,我们可以绘制一张完整一点儿的图:

 

这张图只画了一个用户进程,在实际的系统里当然会有很多用户进程了,不过其关系是大致统一的,所以我们不再重复绘制。关于动态receiver的注册,我们就先说这么多。至于激发广播时,又会做什么动作,我们会在后文阐述,现在我们先接着说明和动态receiver相对的静态receiver。

 

2.2 静态receiver

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

        在PKMS内部,会有一个针对receiver而设置的Resolver(决策器),其示意图如下:

 

        关于PKMS的查询动作的细节,可参考PKMS的相关文档。目前我们只需知道,PKMS向外界提供了queryIntentReceivers()函数,该函数可以返回一个List<ResolveInfo>列表。

        我们举个实际的例子:

Intent intent = new Intent(Intent.ACTION_PRE_BOOT_COMPLETED);
List<ResolveInfo> ris = null;try {
    ris = AppGlobals.getPackageManager().queryIntentReceivers(intent, null, 0, 0);
} catch (RemoteException e) {}

这是AMS的systemReady()函数里的一段代码,意思是查找有多少receiver对ACTION_PRE_BOOT_COMPLETED感兴趣。

         ResolveInfo的定义截选如下:

public class ResolveInfo implements Parcelable 
{    
    public ActivityInfo activityInfo;    
    public ServiceInfo serviceInfo;    
    public IntentFilter filter;    
    public int priority;    
    public int preferredOrder;    
    public int match;
    . . . . . .
    . . . . . .

        总之,当系统希望发出一个广播时,PKMS必须能够决策出,有多少静态receiver对这个广播感兴趣,而且这些receiver的信息分别又是什么。

        关于receiver的注册动作,我们就先说这么多。下面我们来看看激发广播时的动作。

 

3 激发广播

        大家常见的激发广播的函数有哪些呢?从ContextImpl.java文件中,我们可以看到一系列发送广播的接口,列举如下:

  • public void sendBroadcast(Intent intent)

  • public void sendBroadcast(Intent intent, int userId)

  • public void sendBroadcast(Intent intent, String receiverPermission)

  • public void sendOrderedBroadcast(Intent intent, String receiverPermission)

  • public void sendOrderedBroadcast(Intent intent, String receiverPermission, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData, Bundle initialExtras)

  • public void sendStickyBroadcast(Intent intent)

  • public void sendStickyOrderedBroadcast(Intent intent, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData, Bundle initialExtras)

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

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

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

        在发送方,我们熟悉的调用sendBroadcast()的代码片段如下:

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

上面的mContext的内部其实是在调用一个ContextImpl对象的同名函数,所以我们继续查看ContextImpl.java文件。

【frameworks/base/core/java/android/app/ContextImpl.java】

@Override
public void sendBroadcast(Intent intent) 
{
    String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());    
    try 
    {
        intent.setAllowFds(false);
        ActivityManagerNative.getDefault().broadcastIntent(
            mMainThread.getApplicationThread(), intent, resolvedType, null,
            Activity.RESULT_OK, null, null, null, false, false,
            Binder.getOrigCallingUser());
    } catch (RemoteException e) {
    }
}

简单地调用broadcastIntent()向AMS发出请求了。

 

3.1 AMS一侧的broadcastIntentLocked()

        用户进程把发送广播的语义传递到AMS之后,最终会由AMS的broadcastIntentLocked()处理。其原型如下:

private final int broadcastIntentLocked(ProcessRecord callerApp,
                                        String callerPackage, 
                                        Intent intent, String resolvedType,
                                        IIntentReceiver resultTo, int resultCode, 
                                        String resultData,
                                        Bundle map, String requiredPermission,
                                        boolean ordered, boolean sticky, 
                                        int callingPid, int callingUid,                   
                                        int userId)

broadcastIntentLocked()需要考虑以下方面的技术细节。

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

        另外,有时候用户希望发送sticky广播,以便日后注册的receiver可以收到“错过”的sticky广播。要达到这个目的,系统必须在内部维护一张sticky广播表,在具体的实现中,AMS会把广播intent加入mStickyBroadcasts映射表中。mStickyBroadcasts是一张哈希映射表,其key值为intent的action字符串,value值为“与这个action对应的intent数组列表”的引用。当我们发送sticky广播时,新的广播intent要么替换掉intent数组列表中的某项,要么作为一个新项被添加进数组列表,以备日后使用。

        发送广播时,还需要考虑所发送的广播是否需要有序(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递送广播。

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

3.1.1 为intent添加FLAG_EXCLUDE_STOPPED_PACKAGES标记

        对应的代码为:

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

/**
 * If set, this intent will not match any components in packages that
 * are currently stopped.  If this is not set, then the default behavior
 * is to include such applications in the result.
 */
public static final int FLAG_EXCLUDE_STOPPED_PACKAGES = 0x00000010;
/**
 * If set, this intent will always match any components in packages that
 * are currently stopped.  This is the default behavior when
 * {@link #FLAG_EXCLUDE_STOPPED_PACKAGES} is not set.  If both of these
 * flags are set, this one wins (it allows overriding of exclude for
 * places where the framework may automatically set the exclude flag).
 */
public static final int FLAG_INCLUDE_STOPPED_PACKAGES = 0x00000020;

        从上面的broadcastIntentLocked()函数可以看到,在默认情况下,AMS是不会把intent广播发给“处于停止状态的”应用的。据说Google这样做是为了防止一些流氓软件或病毒干坏事。当然,如果广播的发起者认为自己的确需要广播到“处于停止状态的”应用的话,它可以让intent携带FLAG_INCLUDE_STOPPED_PACKAGES标记,从这个标记的注释可以了解到,如果这两个标记同时设置的话,那么FLAG_INCLUDE_STOPPED_PACKAGES标记会“取胜”,它会覆盖掉framework自动添加的FLAG_EXCLUDE_STOPPED_PACKAGES标记。

3.1.2 处理和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广播。

3.1.3 处理其他一些系统广播

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

3.1.4 判断当前是否有权力发出广播

        接着,broadcastIntentLocked()会判断当前是否有权力发出广播,代码截选如下:

/*
 * Prevent non-system code (defined here to be non-persistent
 * processes) from sending protected broadcasts.
 */
if (callingUid == Process.SYSTEM_UID || callingUid == Process.PHONE_UID
        || callingUid == Process.SHELL_UID || callingUid == 0) 
{
    // Always okay.
} 
else if (callerApp == null || !callerApp.persistent) 
{
    try 
    {
        if (AppGlobals.getPackageManager().isProtectedBroadcast(intent.getAction())) 
        {
            String msg = "Permission Denial: not allowed to send broadcast "
                    + intent.getAction() + " from pid="
                    + callingPid + ", uid=" + callingUid;
            Slog.w(TAG, msg);
            throw new SecurityException(msg);
        }
    } 
    catch (RemoteException e) 
    {
        Slog.w(TAG, "Remote exception", e);
        return ActivityManager.BROADCAST_SUCCESS;
    }
}

        如果发起方的Uid为SYSTEM_UID、PHONE_UID或SHELL_UID,或者发起方具有root权限,那么它一定有权力发送广播。

        另外,还有一个“保护性广播”的概念,也要考虑进来。网上有一些人询问AndroidManifest.xml中的一级标记<protected-broadcast>是什么意思。简单地说,Google认为有一些广播是只能由系统发送的,如果某个系统级AndroidManifest.xml中写了这个标记,那么在PKMS解析该文件时,就会把“保护性广播”标记中的名字(一般是Action字符串)记录下来。在系统运作起来之后,如果某个不具有系统权限的应用试图发送系统中的“保护性广播”,那么到AMS的broadcastIntentLocked()处就会被拦住,AMS会抛出异常,提示"Permission Denial: not allowed to send broadcast"。

        我们在frameworks/base/core/res/AndroidManifest.xml文件中,可以看到<protected-broadcast>标记的具体写法,截选如下:

 

 

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

        接着,broadcastIntentLocked()中会判断当前是否在发出sticky广播,如果是的话,必须把广播intent记录下来。

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

    ArrayList<Intent> list = mStickyBroadcasts.get(intent.getAction());
    if (list == null) 
    {
        list = new ArrayList<Intent>();
        mStickyBroadcasts.put(intent.getAction(), list);
    }
    int N = list.size();
    int i;
    for (i=0; i<N; i++) 
    {
        if (intent.filterEquals(list.get(i))) 
        {
            // This sticky already exists, replace it.
            list.set(i, new Intent(intent));
            break;
        }
    }
    if (i >= N) 
    {
        list.add(new Intent(intent));
    }

mStickyBroadcasts的定义是这样的:

    final HashMap<String, ArrayList<Intent>> mStickyBroadcasts =
            new HashMap<String, ArrayList<Intent>>();

上面代码的filterEquals()函数会比较两个intent的action、data、type、class以及categories等信息,但不会比较extra数据。如果两个intent的action是一样的,但其他信息不同,那么它们在ArrayList<Intent>中会被记成两个不同的intent。而如果发现新发送的intent在ArrayList中已经有个“相等的”旧intent时,则会用新的替掉旧的。

        以后,每当注册新的动态receiver时,注册动作中都会遍历一下mStickyBroadcast表,看哪些intent可以和新receiver的filter匹配,只有匹配的intent才会递送给新receiver,示意图如下:

 

图中新receiver的filter只对a1和a3这两个action感兴趣,所以遍历时就不会考虑mStickyBroadcast表中的a2表项对应的子表,而a1、a3子表所对应的若干intent中又只有一部分可以和filter匹配,比如a1的intent1以及a3的intent2,所以图中只选择了这两个intent递送给新receiver。

        除了记入mStickyBoradcast表的动作以外,sticky广播和普通广播在broadcastIntentLocked()中的代码是一致的,并没有其他什么不同了。

 

3.1.6 尝试向并行receivers递送广播

        然后broadcastIntentLocked()会尝试向并行receivers递送广播。此时会调用到queue.scheduleBroadcastsLocked()。相关代码截选如下:

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, requiredPermission,
            registeredReceivers, resultTo, resultCode, resultData, map,
            ordered, sticky, false);
    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())。关于上面代码中的registeredReceivers列表,我们会在后文说明,这里先跳过。

        其实不光并行处理部分需要一个BroadcastRecord节点,串行处理部分也需要BroadcastRecord节点。也就是说,要激发一次广播,AMS必须构造一个或两个BroadcastRecord节点,并将之插入合适的广播队列(mFgBroadcastQueue或mBgBroadcastQueue)。插入成功后,再执行队列的scheduleBroadcastsLocked()动作,进行实际的派发调度。示意图如下:

 

请注意图中BroadcastRecord节点所携带的节点链。在mParallelBroadcasts表中,每个BroadcastRecord只可能携带BroadcastFilter,因为平行处理的节点只会对应动态receiver,而所有静态receiver只能是串行处理的。另一方面,在mOrderedBroadcasts表中,BroadcastRecord中则既可能携带BroadcastFilter,也可能携带ResolveInfo。这个其实很容易理解,首先,ResolveInfo对应静态receiver,放到这里自不待言,其次,如果用户在发送广播时明确指定要按ordered方式发送的话,那么即使目标方的receiver是动态注册的,它对应的BroadcastFilter也会被强制放到这里。

        好,现在让我们再整合一下思路。BroadcastRecord节点内部的receivers列表,记录着和这个广播动作相关的目标receiver信息,该列表内部的子节点可能是ResolveInfo类型的,也可能是BroadcastFilter类型的。ResolveInfo是从PKMS处查到的静态receiver的描述信息,它的源头是PKMS分析的那些AndroidManifest.xml文件。而BroadcastFilter事实上来自于本文一开始阐述动态receiver时,提到的AMS端的mRegisteredReceivers哈希映射表。现在,我们再画一张示意图:

 

因为BroadcastRecord里的BroadcastFilter,和AMS的mRegisteredReceivers表中(间接)所指的对应BroadcastFilter是同一个对象,所以我是用虚线将它们连起来的。

         Ok,我们接着看scheduleBroadcastsLocked()动作。scheduleBroadcastsLocked()的代码如下:

【frameworks/base/services/java/com/android/server/am/BroadcastQueue.java】

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

发出BROADCAST_INTENT_MSG消息。

        上面用到的mHandler是这样创建的:

final Handler mHandler = new Handler() 
{
    public void handleMessage(Message msg) 
    {
        switch (msg.what) 
        {
            case BROADCAST_INTENT_MSG: 
            {
                if (DEBUG_BROADCAST) 
                    Slog.v(TAG, "Received BROADCAST_INTENT_MSG");
                processNextBroadcast(true);
            } 
            break;
            
            case BROADCAST_TIMEOUT_MSG: 
            {
                synchronized (mService) 
                {
                    broadcastTimeoutLocked(true);
                }
            } 
            break;
        }
    }
};

       也就是说,AMS端会在BroadcastQueue.java中的processNextBroadcast()具体处理广播。

 

3.1.7 整理两个receiver列表

        我们前文已经说过,有些广播是需要有序递送的。为了合理处理“有序递送”和“平行递送”,broadcastIntentLocked()函数内部搞出了两个list:

List receivers = null;
List<BroadcastFilter> registeredReceivers = null;

其中,receivers主要用于记录“有序递送”的receiver,而registeredReceivers则用于记录与intent相匹配的动态注册的receiver。

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

receivers = AppGlobals.getPackageManager().queryIntentReceivers(intent, resolvedType, STOCK_PM_FLAGS, userId);

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

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

就可以了。注意,此时返回的registeredReceivers中的子项是没有经过排序的。而关于PKMS的queryIntentReceivers(),我们可以参考PKMS的专题文档,此处不再赘述。

        如果我们要“并行递送”广播, registeredReceivers中的各个receiver会在随后的queue.scheduleBroadcastsLocked()动作中被并行处理掉。如果大家折回头看看向并行receivers递送广播的代码,会发现在调用完queue.scheduleBroadcastsLocked()后,registeredReceivers会被强制赋值成null值。

        如果我们要“串行递送”广播,那么必须考虑把registeredReceivers表合并到receivers表中去。我们知道,一开始receivers列表中只记录了一些静态receiver,这些receiver将会被“有序递送”。现在我们只需再遍历一下registeredReceivers列表,并将其中的每个子项插入到receivers列表的合适地方,就可以合并出一条顺序列表了。当然,如果registeredReceivers已经被设为null了,就无所谓合并了。

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

        当然,上面所说的是没有明确指定目标组件的情况,如果intent里含有明确的目标信息,那么就不需要调用包管理器的queryIntentReceivers()了,只需new一个ArrayList,并赋值给receivers,然后把目标组件对应的ResolveInfo信息添加进receivers数组列表即可。

 

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

        当receivers列表整理完毕之后,现在要开始尝试逐个向串行receivers递送广播了。正如前文所说,这里要重新new一个新的BroadcastRecord节点:

if ((receivers != null && receivers.size() > 0)
    || resultTo != null) 
{
    BroadcastQueue queue = broadcastQueueForIntent(intent);
    BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
            callerPackage, callingPid, callingUid, requiredPermission,
            receivers, resultTo, resultCode, resultData, map, ordered,
            sticky, false);
    . . . . . .
    boolean replaced = replacePending && queue.replaceOrderedBroadcastLocked(r); 
    if (!replaced) {
        queue.enqueueOrderedBroadcastLocked(r);
        queue.scheduleBroadcastsLocked();
    }
}

而scheduleBroadcastsLocked()最终会间接导致走到 BroadcastQueue.java中的processNextBroadcast()。这一点和前文所说的“向并行receivers递送广播”的动作基本一致。

        下面我们来看,递送广播动作中最重要的processNextBroadcast()。

 

3.2 最重要的processNextBroadcast()

        从processNextBroadcast()的代码,我们就可以看清楚前面说的“平行广播”、“有序广播”和“动态receiver”、“静态receiver”之间的关系了。

        我们在前文已经说过,所有的静态receiver都是串行处理的,而动态receiver则会按照发广播时指定的方式,进行“并行”或“串行”处理。能够并行处理的广播,其对应的若干receiver一定都已经存在了,不会牵扯到启动新进程的操作,所以可以在一个while循环中,一次性全部deliver。而有序广播,则需要一个一个地处理,其滚动处理的手段是发送事件,也就是说,在一个receiver处理完毕后,会利用广播队列(BroadcastQueue)的mHandler,发送一个BROADCAST_INTENT_MSG事件,从而执行下一次的processNextBroadcast()。

        processNextBroadcast()的代码逻辑大体是这样的:先尝试处理BroadcastQueue中的“平行广播”部分。这需要遍历并行列表(mParallelBroadcasts)的每一个BroadcastRecord以及其中的receivers列表。对于平行广播而言,receivers列表中的每个子节点是个BroadcastFilter。我们直接将广播递送出去即可:

while (mParallelBroadcasts.size() > 0) 
{
    r = mParallelBroadcasts.remove(0);
    r.dispatchTime = SystemClock.uptimeMillis();
    r.dispatchClockTime = System.currentTimeMillis();
    final int N = r.receivers.size();
    . . . . . . 
    for (int i=0; i<N; i++) 
    {
        Object target = r.receivers.get(i);
        . . . . . .
        deliverToRegisteredReceiverLocked(r, (BroadcastFilter)target, false);
    }
    . . . . . .
}

 

3.2.1 用deliverToRegisteredReceiverLocked()递送到平行动态receiver

        deliverToRegisteredReceiverLocked()的代码截选如下:

【frameworks/base/services/java/com/android/server/am/BroadcastQueue.java】

private final void deliverToRegisteredReceiverLocked(BroadcastRecord r,
                                                     BroadcastFilter filter, 
                                                     boolean ordered) 
{
    . . . . . .
    . . . . . .
    if (!skip) 
    {
        if (ordered) 
        {
            r.receiver = filter.receiverList.receiver.asBinder();
            r.curFilter = filter;
            filter.receiverList.curBroadcast = r;
            r.state = BroadcastRecord.CALL_IN_RECEIVE;
            if (filter.receiverList.app != null) 
            {
                r.curApp = filter.receiverList.app;
                filter.receiverList.app.curReceiver = r;
                mService.updateOomAdjLocked();
            }
        }
        
            . . . . . .
            performReceiveLocked(filter.receiverList.app, 
                                 filter.receiverList.receiver,
                                 new Intent(r.intent), r.resultCode,
                                 r.resultData, r.resultExtras, 
                                 r.ordered, r.initialSticky);
            if (ordered) 
            {
                r.state = BroadcastRecord.CALL_DONE_RECEIVE;
            }
        
        . . . . . .
}

 

private static void performReceiveLocked(ProcessRecord app, IIntentReceiver receiver,
        Intent intent, int resultCode, String data, Bundle extras,
        boolean ordered, boolean sticky) throws RemoteException 
{
    // Send the intent to the receiver asynchronously using one-way binder calls.
    if (app != null && app.thread != null) 
    {
        // If we have an app thread, do the call through that so it is
        // correctly ordered with other one-way calls.
        app.thread.scheduleRegisteredReceiver(receiver, intent, resultCode,
                data, extras, ordered, sticky);
    } 
    else 
    {
        receiver.performReceive(intent, resultCode, data, extras, ordered, sticky);
    }
}

终于通过app.thread向用户进程传递语义了。注意scheduleRegisteredReceiver()的receiver参数,它对应的就是前文所说的ReceiverDispatcher的Binder实体——InnerReceiver了。

        总之,当语义传递到用户进程的ApplicationThread以后,走到:

【frameworks/base/core/java/android/app/ActivityThread.java】

// This function exists to make sure all receiver dispatching is
// correctly ordered, since these are one-way calls and the binder driver
// applies transaction ordering per object for such calls.
public void scheduleRegisteredReceiver(IIntentReceiver receiver, Intent intent,
        int resultCode, String dataStr, Bundle extras, boolean ordered,
        boolean sticky) throws RemoteException 
{
    receiver.performReceive(intent, resultCode, dataStr, extras, ordered, sticky);
}

终于走到ReceiverDispatcher的InnerReceiver了:

【frameworks/base/core/java/android/app/LoadedApk.java】

static final class ReceiverDispatcher 
{
    final static class InnerReceiver extends IIntentReceiver.Stub 
    {
        . . . . . .
        . . . . . .
        public void performReceive(Intent intent, int resultCode,
                                   String data, Bundle extras, 
                                   boolean ordered, boolean sticky) 
        {
            LoadedApk.ReceiverDispatcher rd = mDispatcher.get();
            . . . . . .
            if (rd != null) {
                rd.performReceive(intent, resultCode, data, extras,
                                  ordered, sticky);
            } 
            . . . . . .
        }
    }
    . . . . . .
    public void performReceive(Intent intent, int resultCode,
                               String data, Bundle extras, 
                               boolean ordered, boolean sticky) 
    {
        . . . . . .
        Args args = new Args(intent, resultCode, data, extras, ordered, sticky);
        if (!mActivityThread.post(args)) // 请注意这一句!
        {
            if (mRegistered && ordered) 
            {
                IActivityManager mgr = ActivityManagerNative.getDefault();
                if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
                        "Finishing sync broadcast to " + mReceiver);
                args.sendFinished(mgr);
            }
        }
    }
}

请注意mActivityThread.post(args)一句,这样,事件泵最终会回调Args参数的run()成员函数:

final class Args extends BroadcastReceiver.PendingResult implements Runnable 
{
    . . . . . .
    . . . . . .
    public void run() 
    {
        final BroadcastReceiver receiver = mReceiver;
        . . . . . .
        try {
            ClassLoader cl =  mReceiver.getClass().getClassLoader();
            intent.setExtrasClassLoader(cl);
            setExtrasClassLoader(cl);
            receiver.setPendingResult(this);
            receiver.onReceive(mContext, intent);  // 回调具体receiver的onReceive()
        } catch (Exception e) {
            . . . . . .
        }
        
        if (receiver.getPendingResult() != null) {
            finish();
        }
        . . . . . .
    }
}

其中的那句receiver.onReceive(this),正是回调我们具体receiver的onReceive()成员函数的地方。噢,终于看到应用程序员熟悉的onReceive()了。这部分的示意图如下:

 

3.2.2 静态receiver的递送

        说完动态递送,我们再来看静态递送。对于静态receiver,情况会复杂很多,因为静态receiver所从属的进程有可能还没有运行起来呢。此时BroadcastRecord节点中记录的子列表的节点是ResolveInfo对象。

ResolveInfo info = (ResolveInfo)nextReceiver;
. . . . . .
r.state = BroadcastRecord.APP_RECEIVE;
String targetProcess = info.activityInfo.processName;

那么我们要先找到receiver所从属的进程的进程名。

         processNextBroadcast()中启动进程的代码如下:

ProcessRecord app = mService.getProcessRecordLocked(targetProcess, 
info.activityInfo.applicationInfo.uid);
. . . . . .
if (app != null && app.thread != null) 
{
    . . . . . .
    app.addPackage(info.activityInfo.packageName);
    processCurBroadcastLocked(r, app);
    return;
    . . . . . .
}
r.curApp = mService.startProcessLocked(targetProcess,
                               info.activityInfo.applicationInfo, true,
                               r.intent.getFlags() | Intent.FLAG_FROM_BACKGROUND,
                               "broadcast", r.curComponent,              
                               (r.intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0, 
                               false)
. . . . . .
mPendingBroadcast = r;
mPendingBroadcastRecvIndex = recIdx;

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

3.2.2.1 processCurBroadcastLocked()

        我们先说processCurBroadcastLocked()。

private final void processCurBroadcastLocked(BroadcastRecord r,
                        ProcessRecord app) throws RemoteException 
{
    . . . . . .
    r.receiver = app.thread.asBinder();
    r.curApp = app;
    app.curReceiver = r;
    . . . . . .
    . . . . . .
        app.thread.scheduleReceiver(new Intent(r.intent), r.curReceiver,
              mService.compatibilityInfoForPackageLocked(r.curReceiver.applicationInfo),
              r.resultCode, r.resultData, r.resultExtras, r.ordered);
        . . . . . .
        started = true;
    . . . . . .
}

其中最重要的是调用app.thread.scheduleReceiver()的那句。

这个函数对应于ActivityThread一侧的同名函数,代码如下:

void scheduleReceiver(Intent intent, ActivityInfo info, 
                      CompatibilityInfo compatInfo,                      
                      int resultCode, String data, 
                      Bundle extras, boolean sync) 
                      throws RemoteException;

其中ActivityInfo info参数,记录着目标receiver的信息。可以看到,递送静态receiver时,是不会用到RecevierDispatcher的。

        用户进程里handleMessage()这样处理RECEIVER消息:

case RECEIVER:
    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "broadcastReceiveComp");
    handleReceiver((ReceiverData)msg.obj);
    maybeSnapshot();
    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);    
    break;

        ActivityThread中,会运用反射机制,创建出BroadcastReceiver对象,而后回调该对象的onReceive()成员函数。

private void handleReceiver(ReceiverData data) 
{
    . . . . . .
    IActivityManager mgr = ActivityManagerNative.getDefault();
    BroadcastReceiver receiver;
    try {
        java.lang.ClassLoader cl = packageInfo.getClassLoader();
        data.intent.setExtrasClassLoader(cl);
        data.setExtrasClassLoader(cl);
        receiver = (BroadcastReceiver)cl.loadClass(component).newInstance();
    } catch (Exception e) {
        . . . . . .
    }
    try {
        . . . . . .
        receiver.setPendingResult(data);
        receiver.onReceive(context.getReceiverRestrictedContext(), data.intent);
    } catch (Exception e) {
        . . . . . .
    } finally {
        sCurrentBroadcastIntent.set(null);
    }
    if (receiver.getPendingResult() != null) {
        data.finish();
    }
}

3.2.2.2 必要时启动新进程

        现在我们回过头来看,在目标进程尚未启动的情况下,是如何完成递送的。刚刚我们已经看到调用startProcessLocked()的句子了,只要不出问题,目标进程成功启动后就会调用AMS的attachApplication()。

        有关attachApplication()的详情,请参考其他关于AMS的文档,此处我们只需知道它里面又会调用attachApplicationLocked()函数。

private final boolean attachApplicationLocked(IApplicationThread thread, int pid)

attachApplicationLocked()内有这么几句:

// Check if a next-broadcast receiver is in this process...
if (!badApp && isPendingBroadcastProcessLocked(pid)) {
    try {
        didSomething = sendPendingBroadcastsLocked(app);
    } catch (Exception e) {
        // If the app died trying to launch the receiver we declare it 'bad'
        badApp = true;
    }
}

它们的意思是,如果新启动的进程就是刚刚mPendingBroadcast所记录的进程的话,此时AMS就会执行sendPendingBroadcastsLocked(app)一句。

 

// The app just attached; send any pending broadcasts that it should receive
boolean sendPendingBroadcastsLocked(ProcessRecord app) {
    boolean didSomething = false;
    for (BroadcastQueue queue : mBroadcastQueues) {
        didSomething |= queue.sendPendingBroadcastsLocked(app);
    }
    return didSomething;
}

BroadcastQueue的sendPendingBroadcastsLocked()函数如下:

public boolean sendPendingBroadcastsLocked(ProcessRecord app) {
    boolean didSomething = false;
    final BroadcastRecord br = mPendingBroadcast;
    if (br != null && br.curApp.pid == app.pid) {
        try {
            mPendingBroadcast = null;
            processCurBroadcastLocked(br, app);
            didSomething = true;
        } catch (Exception e) {
            . . . . . .
        }
    }
    return didSomething;
}

可以看到,既然目标进程已经成功启动了,那么mPendingBroadcast就可以赋值为null了。接着,sendPendingBroadcastsLocked()会调用前文刚刚阐述的processCurBroadcastLocked(),其内再通过app.thread.scheduleReceiver(),将语义发送到用户进程,完成真正的广播递送。这部分在上一小节已有阐述,这里就不多说了。

 

3.2.3 说说有序广播是如何循环起来的?

        我们知道,平行广播的循环很简单,只是在一个while循环里对每个动态receiver执行deliverToRegisteredReceiverLocked()即可。而对有序广播来说,原则上每次processNextBroadcast()只会处理一个BroadcastRecord的一个receiver而已。当然,此时摘下的receiver既有可能是动态注册的,也有可能是静态的。

        对于动态注册的receiver,目标进程处理完广播之后,会间接调用am.finishReceiver()向AMS发出反馈,关于这一步,前文在讲Args.run()时已有涉及,只是当时主要关心的是里面的receiver.onReceive()一句,现在我们要关心其中的receiver.setPendingResult()一句:

final class Args extends BroadcastReceiver.PendingResult implements Runnable 
{
    . . . . . .
    . . . . . .
    public void run() 
    {
        final BroadcastReceiver receiver = mReceiver;
        . . . . . .
        try {
            ClassLoader cl =  mReceiver.getClass().getClassLoader();
            intent.setExtrasClassLoader(cl);
            setExtrasClassLoader(cl);

            // 注意,把自己设到BroadcastReceiver的mPendingResult域里了
            receiver.setPendingResult(this);    
            receiver.onReceive(mContext, intent);  // 回调具体receiver的onReceive()
        } catch (Exception e) {
            . . . . . .
        }
        
        if (receiver.getPendingResult() != null) {
            finish();
        }
        . . . . . .
    }
}

因为receiver通过调用setPendingResult(this)将自己设到mPendingResult域里了,那么在执行完receiver.onReceive()一句后,就会马上走入if (receiver.getPendingResult()!= null)一句,从而执行到finish()函数。

对于动态receiver而言,当初注册它时,已经在对应的ReceiverDispatcher对象里将mRegistered成员记为true了。我们摘一句前文创建ReceiverDispatcher对象的句子,如下:

rd = new LoadedApk.ReceiverDispatcher(
                receiver, context, scheduler, null, true).getIIntentReceiver();

请注意ReceiverDispatcher构造函数的最后那个true值参数,它会给ReceiverDispatcher的mRegistered成员变量赋值。再后来,当ReceiverDispatcher构造Args对象时,就会用到这个mRegistered域。Args的构造函数如下:

public Args(Intent intent, int resultCode, String resultData, Bundle resultExtras,
        boolean ordered, boolean sticky) {
    super(resultCode, resultData, resultExtras,
            mRegistered ? TYPE_REGISTERED : TYPE_UNREGISTERED,
            ordered, sticky, mIIntentReceiver.asBinder());
    mCurIntent = intent;
    mOrdered = ordered;
}

看到那个mRegistered了吗?此时mRegistered为true,所以用到的是TYPE_REGISTERED。这个值会记入Args的mType域(其实是其父类PendingResult的mType域)。另外,当我们要发出有序广播时,从AMS一侧传来的ordered标记最终也会传递到Args的构造函数,即上面那个ordered参数。这个参数会记入Args对象的两个成员里,即mOrdered和mOrderedHint,后者其实是Args父类PendingResult的成员。

我们绕这一小圈是为了说明什么呢?就是为了说明刚刚处理完onReceive()函数后,所调用的finish()函数会有什么行为。

 

finish()函数的代码截选如下:
【frameworks/base/core/java/android/content/BroadcastReceiver.java】

public final void finish() {
    if (mType == TYPE_COMPONENT) {
        . . . . . .
    } else if (mOrderedHint && mType != TYPE_UNREGISTERED) {
        if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
                "Finishing broadcast to " + mToken);
        final IActivityManager mgr = ActivityManagerNative.getDefault();
        sendFinished(mgr);
    }
}

因为此时mType的值为TYPE_REGISTERED,而且mOrderedHint为true,所以会调用到sendFinished(mgr)。

Args继承于BroadcastReceiver.PendingResult,它调用的sendFinished()就是PendingResult的sendFinished():
【frameworks/base/core/java/android/content/BroadcastReceiver.java】

public void sendFinished(IActivityManager am) 
{
    synchronized (this) {
        if (mFinished) {
            throw new IllegalStateException("Broadcast already finished");
        }
        mFinished = true;
    
        try {
            if (mResultExtras != null) {
                mResultExtras.setAllowFds(false);
            }
            if (mOrderedHint) {
                am.finishReceiver(mToken, mResultCode, mResultData, mResultExtras,
                        mAbortBroadcast);
            } else {
                // This broadcast was sent to a component; it is not ordered,
                // but we still need to tell the activity manager we are done.
                am.finishReceiver(mToken, 0, null, null, false);
            }
        } catch (RemoteException ex) {
        }
    }
}

代码中的am.finishReceiver()会通知AMS,表示用户侧receiver已经处理好了,或者至少告一段落了,请AMS进行下一步动作。对于有序广播而言,向AMS传递的语义中携带有mResultCode、mResultData、mResultExtras以及mAbortBroadcast。而对于普通广播而言,此时mOrderedHint为false,是不会把mAbortBroadcast语义传递给AMS的。

 

而对于静态注册的receiver,情况是类似的,最终也是调用am.finishReceiver()向AMS发出回馈的,只不过发起的动作是在ActivityThread的handleReceiver()动作中。还记得前文我们阐述AMS向用户进程发来语义,要求静态receiver处理广播时的情景吗?我们再列一下当时的scheduleReceiver()函数:

public final void scheduleReceiver(Intent intent, ActivityInfo info,
        CompatibilityInfo compatInfo, int resultCode, String data, Bundle extras,
        boolean sync) {
    ReceiverData r = new ReceiverData(intent, resultCode, data, extras,
            sync, false, mAppThread.asBinder());

其中ReceiverData类的定义截选如下:

static final class ReceiverData extends BroadcastReceiver.PendingResult {
public ReceiverData(Intent intent, int resultCode, 
                        String resultData, Bundle resultExtras,
                            boolean ordered, boolean sticky, IBinder token) {
        super(resultCode, resultData, resultExtras, TYPE_COMPONENT, 
               ordered, sticky, token);
        this.intent = intent;
    }

请大家注意两个地方,一个是ordered参数(即scheduleReceiver的sync参数),它会记入ReceiverData的mOrderedHint成员(其实是它的父类BroadcastReceiver.PendingResult的成员)中。另一个是构造ReceiverData时,为其父类构造器传入的TYPE_COMPONENT参数,该参数会记入mType域中。

 

scheduleReceiver()动作最终导致走到用户进程的handleReceiver()处:

【frameworks/base/core/java/android/app/ActivityThread.java】

private void handleReceiver(ReceiverData data) 
{
        . . . . . .
        receiver.setPendingResult(data);
        receiver.onReceive(context.getReceiverRestrictedContext(), data.intent);
        . . . . . .
    if (receiver.getPendingResult() != null) {
        data.finish();
    }
}

此处调用的finish()是PendingResult类的finish():

public final void finish() 
{
    if (mType == TYPE_COMPONENT) {
        final IActivityManager mgr = ActivityManagerNative.getDefault();
        if (QueuedWork.hasPendingWork()) {
            QueuedWork.singleThreadExecutor().execute( new Runnable() {
                @Override public void run() {
                    . . . . . .
                    sendFinished(mgr);
                }
            });
        } else {
            if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
                    "Finishing broadcast to component " + mToken);
            sendFinished(mgr);
        }
} else if (mOrderedHint && mType != TYPE_UNREGISTERED) {
        . . . . . .
    }
}

此时mType为TYPE_COMPONENT,最终会调用到sendFinished(mgr)。此处的sendFinished()内部最终也会调用到am.finishReceiver(),向AMS通告receiver已经处理好了。sendFinished()的代码前面已经列过,此处不再重列。

 

AMS侧在收到finishReceiver语义后,执行:

public void finishReceiver(IBinder who, int resultCode, String resultData,
        Bundle resultExtras, boolean resultAbort) 
{
    . . . . . .
    try {
        boolean doNext = false;
        BroadcastRecord r = null;

        synchronized(this) {
            r = broadcastRecordForReceiverLocked(who);
            if (r != null) {
                doNext = r.queue.finishReceiverLocked(r, resultCode,
                    resultData, resultExtras, resultAbort, true);
            }
        }

        if (doNext) {
            r.queue.processNextBroadcast(false);
        }
        trimApplications();
    } finally {
        Binder.restoreCallingIdentity(origId);
    }
}

可以看到,如有必要,会继续调用processNextBroadcast(),从而完成有序广播的循环处理。

 

3.2.4说说有序广播的拦截处理

对于那些处理有序广播的receivers而言,它们具有阻止其后receivers继续处理广播的权利。在BroadcastReceiver中提供有一个abortBroadcast()函数,只要我们在receiver的onReceive()中调用这个函数,那么其后面的receivers就不会再执行onReceive()了。

abortBroadcast()函数的相关代码如下:
【frameworks/base/core/java/android/content/BroadcastReceiver.java】

public abstract class BroadcastReceiver {
    private PendingResult mPendingResult;
    . . . . . .
    public final void abortBroadcast() {
        checkSynchronousHint();
        mPendingResult.mAbortBroadcast = true;
    }

说起这段代码里的mPendingResult,请大家回忆一下前文阐述调用receiver.onReceive()的地方,是不是在调用之前会有个receiver.setPendingResult()?为了方便阅读,我们以前文所说的Args类为例:

final class Args extends BroadcastReceiver.PendingResult implements Runnable 
{
    . . . . . .
    public void run() 
    {
        final BroadcastReceiver receiver = mReceiver;
        . . . . . .
        try {
            . . . . . .
            // 注意,把自己设到BroadcastReceiver的mPendingResult域里了
            receiver.setPendingResult(this);    
            receiver.onReceive(mContext, intent);  // 回调具体receiver的onReceive()
        } catch (Exception e) {
            . . . . . .
        }
        
        if (receiver.getPendingResult() != null) {
            finish();
        }
        . . . . . .
    }
}

Args把自己设到BroadcastReceiver的mPendingResult域里了,当我们在onReceive()中又调用abortBroadcast()时,事实上就是把Args的mAbortBroadcast设为true了。这个域会在调用finish()时传递给AMS,让AMS去完成拦截动作。finish()调用am.finishReceiver()时,最后一个参数就是mAbortBroadcast。

                am.finishReceiver(mToken, mResultCode, mResultData, mResultExtras,
                        mAbortBroadcast);

前文在讲有序广播是如何循环的时,已经列过AMS一侧的finishReceiver()函数了,我们再摘录几句:

                doNext = r.queue.finishReceiverLocked(r, resultCode,
                    resultData, resultExtras, resultAbort, true);
        . . . . . .
        if (doNext) {
            r.queue.processNextBroadcast(false);
        }

 

finishReceiverLocked()函数的代码截选如下:

【frameworks/base/services/java/com/android/server/am/BroadcastQueue.java】

public boolean finishReceiverLocked(BroadcastRecord r, int resultCode,
        String resultData, Bundle resultExtras, boolean resultAbort,
        boolean explicit) {
    int state = r.state;
    r.state = BroadcastRecord.IDLE;
    . . . . . .
    r.curFilter = null;
    r.curApp = null;
    r.curComponent = null;
    r.curReceiver = null;
    mPendingBroadcast = null;

    r.resultCode = resultCode;
    r.resultData = resultData;
    r.resultExtras = resultExtras;
    r.resultAbort = resultAbort;

    return state == BroadcastRecord.APP_RECEIVE
            || state == BroadcastRecord.CALL_DONE_RECEIVE;
}

主要是清理BroadcastRecord里的成员变量,此时会把“拦截”标志记入BroadcastRecord的resultAbort成员变量。

 

接着程序走入r.queue.processNextBroadcast(),该函数内部会根据resultAbort来判断要不要继续将广播传递给下一个receiver处理。我们只摘录processNextBroadcast()里相关的几句:

【frameworks/base/services/java/com/android/server/am/BroadcastQueue.java】

final void processNextBroadcast(boolean fromMsg) {
    . . . . . .
        do {
            if (mOrderedBroadcasts.size() == 0) {
                // No more broadcasts pending, so all done!
                . . . . . .
                return;
            }
            r = mOrderedBroadcasts.get(0);
            . . . . . .
            if (r.receivers == null || r.nextReceiver >= numReceivers
                    || r.resultAbort || forceReceive) {
                . . . . . .
                addBroadcastToHistoryLocked(r);
                mOrderedBroadcasts.remove(0);
                r = null;
                looped = true;
                continue;
            }
        } while (r == null);
    . . . . . .
}

可以看到,当r.resultAbort为true时,当前处理的BroadcastRecord节点就会从mOrderedBroadcasts里删除了,也就是说,和这个广播相关的后续receiver们都不会被回调了。这就是拦截动作。

 

3.2.5 说说有序广播的timeout处理

        因为AMS很难知道一次广播究竟能不能完全成功递送出去,所以它必须实现一种“时限机制”。前文在阐述broadcastIntentLocked()时,提到过new一个BroadcastRecord节点,并插入一个BroadcastQueue里的“平行列表”或者“有序列表”。不过当时我们没有太细说那个BroadcastQueue,现在我们多加一点儿说明。

        实际上系统中有两个BroadcastQueue,一个叫做“前台广播队列”,另一个叫“后台广播队列”,在AMS里是这样定义的:

BroadcastQueue mFgBroadcastQueue;
BroadcastQueue mBgBroadcastQueue;

为什么要搞出两个队列呢?我认为这是因为系统对“广播时限”的要求不同导致的。对于前台广播队列而言,它里面的每个广播必须在10秒之内把广播递送给receiver,而后台广播队列的时限比较宽,只需60秒之内递送到就可以了。具体时限值请看BroadcastQueue的mTimeoutPeriod域。注意,这个10秒或60秒限制是针对一个receiver而言的。比方说“前台广播队列”的某个BroadcastRecord节点对应了3个receiver,那么在处理这个广播节点时,只要能在30秒(3 x 10)之内搞定就可以了。事实上,AMS系统考虑了更多东西,所以它给一个BroadcastRecord的总时限是其所有receiver时限之和的2倍,在此例中就是60秒(2 x 3 x 10)。

        对于平行receiver而言,时限的作用小一点儿,因为动态receiver是直接递送到目标进程的,它不考虑目标端是什么时候处理完这个广播的。

        然而对于有序receiver来说,时限就比较重要了。因为receiver之间必须是串行处理的,也就是说上一个receiver在没处理完时,系统是不会让下一个receiver进行处理的。从processNextBroadcast()的代码来看,在处理有序receiver时,BroadcastRecord里的nextReceiver域会记录“下一个应该处理的receiver”的标号。只有在BroadcastRecord的所有receiver都处理完后,或者BroadcastRecord的处理时间超过了总时限的情况下,系统才会把这个BroadcastRecord节点从队列里删除。因此我们在processNextBroadcast()里看到的获取当前BroadcastRecord的句子是写死为r = mOrderedBroadcasts.get(0)的。

 

        在拿到当前BroadcastRecord之后,利用nextReceiver值拿到当前该处理的receiver信息:

int recIdx = r.nextReceiver++;
. . . . . .
Object nextReceiver = r.receivers.get(recIdx);

当然,一开始,nextReceiver的值只会是0,表示第一个receiver有待处理,此时会给BroadcastRecord的dispatchTime域赋值。

int recIdx = r.nextReceiver++;
r.receiverTime = SystemClock.uptimeMillis();if (recIdx == 0) {
    r.dispatchTime = r.receiverTime;
    r.dispatchClockTime = System.currentTimeMillis();
    . . . . . .
}

也就是说,dispatchTime的意义是标记实际处理BroadcastRecord的起始时间,那么这个BroadcastRecord所能允许的最大时限值就是:

dispatchTime + 2 * mTimeoutPeriod * 其receiver总数

一旦超过这个时限,而BroadcastRecord又没有处理完,那么就强制结束这个BroadcastRecord节点:

if ((numReceivers > 0) &&
        (now > r.dispatchTime + (2*mTimeoutPeriod*numReceivers))) 
{
    . . . . . .
    broadcastTimeoutLocked(false); // forcibly finish this broadcast
    forceReceive = true;
    r.state = BroadcastRecord.IDLE;
}

此处调用的broadcastTimeoutLocked()的参数是boolean fromMsg,表示这个函数是否是在处理“时限消息”的地方调用的,因为当前是在processNextBroadcast()函数里调用broadcastTimeoutLocked()的,所以这个参数为false。从这个参数也可以看出,另一处判断“处理已经超时”的地方是在消息处理机制里,在那个地方,fromMsg参数应该设为true。

        大体上说,每当processNextBroadcast()准备递送receiver时,会调用setBroadcastTimeoutLocked()设置一个延迟消息:

long timeoutTime = r.receiverTime + mTimeoutPeriod;
. . . . . .
setBroadcastTimeoutLocked(timeoutTime);

setBroadcastTimeoutLocked()的代码如下:

final void setBroadcastTimeoutLocked(long timeoutTime) 
{    if (! mPendingBroadcastTimeoutMessage) 
    {
        Message msg = mHandler.obtainMessage(BROADCAST_TIMEOUT_MSG, this);
        mHandler.sendMessageAtTime(msg, timeoutTime);
        mPendingBroadcastTimeoutMessage = true;
    }
}

只要我们的receiver能及时处理广播,系统就会cancel上面的延迟消息。这也就是说,但凡事件泵的handleMessage()开始处理这个消息,就说明receiver处理超时了。此时,系统会放弃处理这个receiver,并接着尝试处理下一个receiver。

final Handler mHandler = new Handler() 
{
    public void handleMessage(Message msg) {
        switch (msg.what) 
        {
            . . . . . .
            case BROADCAST_TIMEOUT_MSG: 
            {
                synchronized (mService) 
                {
                    broadcastTimeoutLocked(true);
                }
            } break;
        }
    }
};

broadcastTimeoutLocked()的代码截选如下:

final void broadcastTimeoutLocked(boolean fromMsg) 
{
    if (fromMsg) {
        mPendingBroadcastTimeoutMessage = false;
    }
    if (mOrderedBroadcasts.size() == 0) {
        return;
    }
    long now = SystemClock.uptimeMillis();
    BroadcastRecord r = mOrderedBroadcasts.get(0);
    . . . . . .
    . . . . . .
    finishReceiverLocked(r, r.resultCode, r.resultData,
            r.resultExtras, r.resultAbort, true);
    scheduleBroadcastsLocked();
    . . . . . .
}

可以看到,当一个receiver超时后,系统会放弃继续处理它,并再次调用scheduleBroadcastsLocked(),尝试处理下一个receiver。

 

4 尾声

        有关Android的广播机制,我们就先说这么多吧。品一杯红茶,说一段代码,管他云山雾罩,任那琐碎冗长,我自冷眼看安卓,瞧他修短随化。

 

 

如需转载本文内容,请注明出处。

http://my.oschina.net/youranhongcha

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!