广播(Broadcast内部机制讲解)

来源:互联网 发布:俄狄浦斯 知乎 编辑:程序博客网 时间:2024/05/18 01:21

http://my.oschina.net/youranhongcha/blog/226274

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

目录[-]

  • 1 概述
  • 2 两种receiver
  • 2.1 动态receiver
  • 2.2 静态receiver
  • 3 激发广播
  • 3.1 AMS一侧的broadcastIntentLocked()
  • 3.1.1 为intent添加FLAG_EXCLUDE_STOPPED_PACKAGES标记
  • 3.1.2 处理和package相关的广播
  • 3.1.3 处理其他一些系统广播
  • 3.1.4 判断当前是否有权力发出广播
  • 3.1.5 必要时更新一下系统中的sticky广播列表
  • 3.1.6 尝试向并行receivers递送广播
  • 3.1.7 整理两个receiver列表
  • 3.1.8 尝试逐个向串行receivers递送广播
  • 3.2 最重要的processNextBroadcast()
  • 3.2.1 用deliverToRegisteredReceiverLocked()递送到平行动态receiver
  • 3.2.2 静态receiver的递送
  • 3.2.2.1 processCurBroadcastLocked()
  • 3.2.2.2 必要时启动新进程
  • 3.2.3 说说有序广播是如何循环起来的?
  • 3.2.4 说说有序广播的timeout处理
  • 4 尾声
  • 品茗论道说广播(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对象完成:

    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    @Override
    public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) 
    {    
        return registerReceiver(receiver, filter, nullnull);
    }
    @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()代码的截选如下:

    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    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】

    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    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表的定义如下:

    ?
    1
    2
    3
    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(),它最终调用到

    ?
    1
    2
    3
    ActivityManagerNative.getDefault().registerReceiver(
                        mMainThread.getApplicationThread(), mBasePackageName,
                        rd, filter, broadcastPermission);

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

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

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

            ReceiverList的定义截选如下:

    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    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数组列表。

    ?
    1
    2
    3
    4
    5
    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>列表。

            我们举个实际的例子:

    ?
    1
    2
    3
    4
    Intent intent = new Intent(Intent.ACTION_PRE_BOOT_COMPLETED);
    List<ResolveInfo> ris = null;try {
        ris = AppGlobals.getPackageManager().queryIntentReceivers(intent, null00);
    catch (RemoteException e) {}

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

             ResolveInfo的定义截选如下:

    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    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的注册动作,我们就先说这么多。下面我们来看看激发广播时的动作。

     




    0 0