android Application Component研究之BroadcastReceiver

    本文为原创文章,欢迎转载!转载时请注明出处:http://blog.csdn.net/windskier

    这篇文章开始从framework的角度来研究一下android四大控件最后一个控件BroadcastReceiver 的机制与原理,BroadcastReceiver 字面意思就是广播接收器,它能接收来自于系统的以及其他appliaction的广播消息,是android中非常重要的一个component。 

1. BroadcastReceiver注册

    BroadcastReceiver的注册有两种方式,一种是通过Context.registerReceiver()方法来进行动态注册,或者通过在AndroidManifest.xml中进行静态注册。AMS对两种不同注册方式的Receiver有不同的管理方式。静态注册就不用说了,静态注册的receiver的管理是有PMS负责的,而AMS则需要对动态注册的receiver注册和管理。下面就研究一下AMS对receiver的动态管理过程。

1.1 ReceiverDispatcher

    同Service一样,LoadedApk为receiver提供了一个ReceiverDispatcher类型,类似于Service管理时的ServiceDispatcher类型,对于每个要注册的receiver,LoadedApk会为其创建一个ReceiverDispatcher。从其名称来看,ReceiverDispatcher就是向receiver dispatch广播信息,下面简单介绍一下这个类型存在的价值,广播信息是经由AMS进行分发管理的,因此需要为AMS向receive分发广播消息设计一个接口,通过这个接口可以实现receiver与AMS的IPC通信,BroadcastReceiver 本身并不是一个IBinder接口,因此需要单独设计一个,这个接口就是InnerReceiver,InnerReceiver一方面与AMS进行IPC通信,另一方面还需要与receiver进行交互,为了区分IPC的接口方法和与receiver的交互接口,这里需要定义了另外一个类型ReceiverDispatcher,这就是ReceiverDispatcher产生的原因。
@ContextImpl.java
    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();
                }
                rd = mPackageInfo.getReceiverDispatcher(
                    receiver, context, scheduler,
                    mMainThread.getInstrumentation(), true);
            } else {
                if (scheduler == null) {
                    scheduler = mMainThread.getHandler();
                }
                rd = new LoadedApk.ReceiverDispatcher(
                        receiver, context, scheduler, null, true).getIIntentReceiver();
            }
        }
        try {
            return ActivityManagerNative.getDefault().registerReceiver(
                    mMainThread.getApplicationThread(), mBasePackageName,
                    rd, filter, broadcastPermission);
        } catch (RemoteException e) {
            return null;
        }
    }

1.2 InnerReceiver

    上面一节已经介绍了InnerReceiver存在的意义,它其实是一个IInterface,用于AMS和Receiver之间的IPC通信,InnerReceiver实现了接口IIntentReceiver。
@IIntentReceiver.aidl
oneway interface IIntentReceiver {
    void performReceive(in Intent intent, int resultCode,
            String data, in Bundle extras, boolean ordered, boolean sticky);
}

1.3 sticky broadcast

    有这么一种broadcast,在发送并经过AMS分发给对应的receiver后,这个broadcast并不会被丢弃,而是保存在AMS中,当有新的需要动态注册的receiver请求AMS注册时,如果这个receiver能够接收这个broadcast,那么AMS会将在receiver注册成功之后,马上向receiver发送这个broadcast。这种broadcast我们称之为stickybroadcast
    当receiver注册成功后,AMS会将合适的sticky broadcast作为方法registerReceiver()返回给Component,以便通知Component当前是否有sticky broadcast存在
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter)
    如果有多个符合receiver条件的 sticky  broadcast的话,那么AMS将会只返回一个时间较早的sticky broadcast,并且会将这些所有满足要求的sticky broadcast均发送给reciever,交由receiver去处理。sticky broadcast的发送于取消通过方法sendStickyBroadcast()/sendStickyOrderedBroadcast() 以及removeStickyBroadcast()来控制。
registerReceiver()@ActivityManagerService.java
            // Enqueue broadcasts for all existing stickies that match
            // this filter.
            if (allSticky != null) {
                ArrayList receivers = new ArrayList();
                receivers.add(bf);

                int N = allSticky.size();
                for (int i=0; i

1.4 动态注册receiver的注册管理

    receiver 的动态注册过程就需要掌握以上几个知识点就可以了,真正的注册名其曰注册,其实质就是将其交由 AMS 去管理,因此下面我们来分析研究一下 AMS 是通过哪些数据结构来管理 receiver 的。掌握了这些数据结构的含义以及它们之间的相互关系之后,我们就明白了整个需注册 receiver 的注册过程了。
    

1.4.1 BroadcastFilter  

     对于每个注册过程,由两部分组成,一部分是是BroadcastReceiver,另一部分是IntentFilter,BroadcastReceiver通过IntentFilter来指定要接收的Intent BroadCast。而BroadcastFilter则是继承自IntentFilter,它记录了当前需注册的Receiver的IntentFilter信息。因此每一组BroadcastReceiver和IntentFilter对应一次注册过程,每个注册过程即是一个BroadcastReceiver向AMS指示一个Intent Broadcast的需求,如果有多个Intent Broadcast需求,BroadcastReceiver需要向AMS进行多次注册。

1.4.2 ReceiverList

    ReceiverList继承自Arraylist,并且该Arraylist是以BroadcastFilter为元素,同时我们知道BroadcastReceiver的注册过程的二要素是BroadcastReceiver和IntentFilter,对于同一个BroadcastReceiver可能会接收多个Intent Broadcast,因此每个IIntentReceiver可能会对应多个BroadcastFilter,这也是设计ReceiverList这个Arraylist的主要目的。

1.4.3 mReceiverResolver

     mReceiverResolver的定义如下:

    final IntentResolver mReceiverResolver
            = new IntentResolver()

    mReceiverResolver是一个IntentResolver类型的对象,通过它AMS可以将BroadcastFilter按照不同的元素,如action、data、category,进行管理,这样的话在AMS收到broadcast之后,会根据这个broadcast的Intent的元素去query mReceiverResolver,获取对应的BroadcastFilter。

    既然mReceiverResolver实现了对BroadcastFilter的管理,那么前面的ReceiverList类型存在的意义是什么呢? mReceiverResolver和ReceiverList的关系其实并不冲突,AMS可以通过mReceiverResolver来获得可以接收发来的Broadcast的所有的BroadcastFilter。通过上面的类型关系图,我们可以看出BroadcastFilter中反向链接到自己所处的ReceiverList,看见两者之间的关系并不是对立的,而是适用于AMS接收分发过程的不同阶段。


1.4.4 BroadcastRecord

    就向前面介绍的android中的其他几大component一样,BroadReceiver在被处理时同样是通过一个BroadcastRecord的Record类型来进行管理的,BroadcastRecord只有在AMS在处理分发Broadcast时才去创建,它记录着每个Broadcast的信息,在Receiver的过程并不涉及Broadcast。

2. Broadcast的发送过程

    上面第一节介绍了Receiver的动态注册过程,那么接下来我们分析一下Broadcast的发送处理。

2.1 Broadcast的发送方式  

    Broadcast根据不同的需求有多种不同的发送方式,下面来分析一下具体有几种发送方式。下面的表格列出了application几种不同的发送方式以及不同方式对应的方法。

不同的发送方式

对应的方法

普通模式

sendBroadcast(Intent intent)

Broadcast sender 指定了receiver的permission,只有符合这个permission的receiver才能接收这个Broadcast

sendBroadcast(Intent intent, String receiverPermission)

要求符合要求的所有receiver (包括动态注册和静态注册的receiver)按照优先级顺序来接收这个Broadcast

sendOrderedBroadcast(Intent intent, String receiverPermission)

某些情况下,Broadcast sender在发出Broadcast之后,向AMS指定一个resultReceiver,期望所有的receiver接收并处理完Broadcast之后,将在各个receiver间传递的resultExtras返回给resultReceiver。这个resultExtras既可以由sender指定,也可以由某一个receiver指定,一般情况下只要receiver需要向sender返回数据,则可以通过这个resultExtras

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

Sticky Broadcast。前面已描述

sendStickyBroadcast(Intent intent);

endStickyOrderedBroadcast(Intent intent,BroadcastReceiver resultReceiver,Handler scheduler, int initialCode, String initialData,Bundle initialExtras)

2.2 Package manager Broadcast

    AMS在处理收到的Broadcast时,需要对一些特殊的Broadcast做特殊处理,比如Package manager发来的关于Package移除、增加、修改等操作的,由于android系统允许user在package内部component在运行的情况下进行如上所述的操作,因此每当发生这些操作时,PM则会通过发送Broadcast的方式通知AMS进行属于AMS范畴的一些操作,如当PM移除某个Package的时候,要求AMS停止这个Package相关的所有Component等。

    同样的,关于timezone的一些事件也是通过Broadcast的方式通知AMS,这里就不再分析。

2.3 Protected Broadcast

2.3.1 Protected Broadcast作用

    从其名称看即是受保护的Broadcast,它声明在AndroidManifest.xml的一级节点中,对应的节点名称为,一般情况下,protected broadcast定义的是系统级别的broadcast,通常只能被系统服务或者appliaction发送,下面我们分析那些情况下才能发送protected broadcast。

    首先看一下protectedbroadcast的定义,它的主要元素就是一个action name,这些action只能被系统发送,我们可以想象一下,系统级别的broadcast可以不受限制的被所有的应用发送的后果,那将是非常严重的,不可想象的。因此从安全性的角度出发,android为了保证这些系统级别的broadcast不被第三方的应用滥发,提出了这个protected broadcast的概念。

 

Android规定了一些可以发送protected broadcast的进程,

●  android:sharedUserId="android.uid.system"

●  android:sharedUserId="android.uid.phone"

●  android:sharedUserId="android.uid.shell"

●  设置android:persistent的appliaction。

对于其他不符合上述四条的进程发送broadcast时,AMS回去检查该是否是protected broadcast规定的action,如果是,那么将不允许这个broadcast发送。具体代码如下:

broadcastIntentLocked()@ActivityManagerService.java

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

2.3.2 Protected Broadcast定义

    上面介绍了Protected Broadcast的作用,知道这类Broadcast一般是系统发送的,那么我们再来分析一下哪些AndroidManifest.xml中可以定义Protected Broadcast。我们可容易的想到,Protected Broadcast也不是随随便便定义在一个application的AndroidManifest.xml。

     只有系统appliaction才能在其AndroidManifest.xml中定义Protected Broadcast,系统appliaction包括/system/framework、/system/app、vendor/app下的package,因此设备中安装的第三方apk中如果定义了Protected Broadcast,那么这个Protected Broadcast将不生效。下面代码是对属性的解析,可以看出解析之后会检查当前的package是否为系统package,如果是则提取这个ProtectedBroadcast。

parsePackage ()@PackageParser.java

          } else if (tagName.equals("protected-broadcast")) {
                sa = res.obtainAttributes(attrs,
                        com.android.internal.R.styleable.AndroidManifestProtectedBroadcast);

                // Note: don't allow this value to be a reference to a resource
                // that may change.
                String name = sa.getNonResourceString(
                       com.android.internal.R.styleable.AndroidManifestProtectedBroadcast_name);

                sa.recycle();

                if (name != null && (flags&PARSE_IS_SYSTEM) != 0) {
                    if (pkg.protectedBroadcasts == null) {
                        pkg.protectedBroadcasts = new ArrayList();
                    }
                    if (!pkg.protectedBroadcasts.contains(name)) {
                        pkg.protectedBroadcasts.add(name.intern());
                    }
                }

                XmlUtils.skipCurrentTag(parser);
                
            }

2.4 查找receiver

    这一节来分析一下AMS在收到Broadcast之后,查找匹配的receiver来向其分发这个Broadcast。前面说过receiver有两种,一种是动态注册的,另一种则是预先定义静态注册的。不同类型的receiver有不同的管理方式,动态注册的receiver从上面章节中介绍的IntentResolver mReceiverResolver中获取;静态注册的receiver则是从PM中获取。

2.5 Broadcast分发模式

    2.1中介绍了几种android中常用的Broadcast发送方式,sticky模式上面的文章中已经做了介绍,下面主要介绍一些ordered模式和非ordered模式下,不同的分发模式。

2.5.1 非ordered模式

    默认情况下,Broadcast发送方式是非order模式的,这里所说的ordered和非ordered模式是针对动态注册的receiver来说的,而对于静态注册的receiver则一律是ordered模式。非ordered模式下,AMS对Broadcast的分发是一种非同步的模式,也就是说,不待一个receiver处理完Broadcast,就会将Broadcast分发给下一个receiver。

2.5.2 ordered模式

    ordered模式下,动态注册的receiver将会采取和静态注册的receiver相同的处理模式,类似于一种同步模式,AMS会等待一个reciever处理完这个Broadcast,才会将Broadcast分发给下一个receiver。这种模式下,receiver的处理有了先后顺序,因此必须考虑如何布置这个先后顺序,这里按照优先级的顺序来给receiver的处理顺序进行排序。

    对于动态注册的receiver,可以在其IntentFilter类型中定义它的优先级;而对于静态注册的receiver,同样的我们可以在其属性中定义它的优先级。根据receiver优先级从高到低进行排序,如果动态的注册的和静态注册的receive处在相同的优先级,那么将优先处理动态注册的receiver。

broadcastIntentLocked()@ActivityManagerService.java

           int NT = receivers != null ? receivers.size() : 0;
            int it = 0;
            ResolveInfo curt = null;
            BroadcastFilter curr = null;
            while (it < NT && ir < NR) {
                if (curt == null) {
                    curt = (ResolveInfo)receivers.get(it);
                }
                if (curr == null) {
                    curr = registeredReceivers.get(ir);
                }
                if (curr.getPriority() >= curt.priority) {
                    // Insert this broadcast record into the final list.
                    receivers.add(it, curr);
                    ir++;
                    curr = null;
                    it++;
                    NT++;
                } else {
                    // Skip to the next ResolveInfo in the final list.
                    it++;
                    curt = null;
                }
            }
        }
        while (ir < NR) {
            if (receivers == null) {
                receivers = new ArrayList();
            }
            receivers.add(registeredReceivers.get(ir));
            ir++;
        }

2.6 Broadcast分发过程

    Broadcast分发过程是一个异步过程,AMS在收到Broadcast之后,会根据不同类型的receiver,按照Broadcast发送发指定的发送模式进行分发,现在我们来分析一下Broadcast分发过程。

    AMS向自己的消息队列发送消息BROADCAST_INTENT_MSG对所有的receiver进行分发操作,所有的分发操作由方法processNextBroadcast()来实现,因此这一部分我们着重分析一下这个方法中的实现过程。

    


2.6.1 非ordered分发

    上面已经对ordered模式和非ordered模式进行了简单介绍,这里不再多介绍了。对于非ordered模式的分发过程,AMS则会将Broadcast直接分发给符合要求的receiver的进程的消息队列,这种分发的方式可以视作一种异步方式,也可以看作是将该Broadcast同时分发给符合要求的动态注册的receiver,上面章节中我们分析过只有动态注册的receiver才会有ordered和非ordered模式之分,而静态注册的reciever则无此之分,一概视为ordered模式。


    

2.6.2 ordered分发

    满足ordered模式的receiver,AMS对其进行Broadcast分发时是一种同步的过程,只有前一个receiver处理完该Broadcast之后,并且receiver通知AMS该Broadcast已经处理结束,此时AMS才能将Broadcast分发给下一个receiver去处理,如下图所示:


    

    我们知道需要ordered分发的receiver既包括动态注册的receiver,又包括静态注册的receiver,但是AMS向两种不同的注册方式的receiver分发Broadcast的方式有不尽相同,但是最基本的发送及处理过程是按照上面的图示进行的,两种注册方式具体的分发和处理内容如下图所示:

    

2.7 Broadcast处理超时

    AMS向各个BroadcastReceiver分发Broadcast过程以及BroadcastReceiver处理Broadcast的过程并不是无限期的,为了保证系统的响应性和稳定性,android给这个过程设置了固定期限,有Broadcast相关的有两种固定期限,这两种期限均是针对Ordered模式的Receiver,而对于非Ordered模式的Receiver则不需要考虑这个时限,下面来一一介绍一下。

2.7.1 BroadcastRecord处理总时长

    针对每个BroadcastRecord的处理,均会设置一个总时长,这个总时长是指所有符合BroadcastRecord条件的receiver处理完这个Broadcast的总时长的上限。也就是说如果满足条件的所有的Receiver均已经接收并处理完了这个Broadcast的时刻,不能超过规定的总时长,总时长跟满足条件的Receiver总个数有关系,如下面代码所示:

processNextBroadcast ()@ActivityManagerService.java

int numReceivers = (r.receivers != null) ? r.receivers.size() : 0;
if (mProcessesReady && r.dispatchTime > 0) {
    long now = SystemClock.uptimeMillis();
    if ((numReceivers > 0) &&
            (now > r.dispatchTime + (2*BROADCAST_TIMEOUT*numReceivers))) {
        Slog.w(TAG, "Hung broadcast discarded after timeout failure:"
                + " now=" + now
                + " dispatchTime=" + r.dispatchTime
                + " startTime=" + r.receiverTime
                + " intent=" + r.intent
                + " numReceivers=" + numReceivers
                + " nextReceiver=" + r.nextReceiver
                + " state=" + r.state);
        broadcastTimeoutLocked(false); // forcibly finish this broadcast
        forceReceive = true;
        r.state = BroadcastRecord.IDLE;
    }
}

2.7.2 BroadcastReceiver处理时长  

    除了设置针对一个BroadcastRecord的处理的总时长外,android针对每个Receiver的处理时长也做了一个时限设定,保证每一个的Receiver的处理时长不超过规定时长,具体如下面代码所示:

processNextBroadcast ()@ActivityManagerService.java

if (! mPendingBroadcastTimeoutMessage) {
    long timeoutTime = r.receiverTime + BROADCAST_TIMEOUT;
    if (DEBUG_BROADCAST) Slog.v(TAG,
            "Submitting BROADCAST_TIMEOUT_MSG for " + r + " at " + timeoutTime);
    setBroadcastTimeoutLocked(timeoutTime);
}

    如果上述2种时长存在超时的话,AMS将会抛出ANR, 向user报告当前的Receiver在处理Broadcast时存在无响应状况或者响应时间过长等状况。

broadcastTimeoutLocked()@ActivityManagerService.java

if (anrMessage != null) {
    // Post the ANR to the handler since we do not want to process ANRs while
    // potentially holding our lock.
    mHandler.post(new AppNotResponding(app, anrMessage));
}

你可能感兴趣的:(android Application Component研究之BroadcastReceiver)