Android广播管理一--Broadcast机制介绍

    在Android系统中,广播(Broadcast)是在组件之间传播数据(Intent)的一种机制;这些组件甚至是可以位于不同的进程中,这样它就像Binder机制一样,起到进程间通信的作用。

    在Android系统中,为什么需要广播机制呢?广播机制,本质上它就是一种组件间的通信方式,如果是两个组件位于不同的进程当中,那么可以用Binder机制来实现,如果两个组件是在同一个进程中,那么它们之间可以用来通信的方式就更多了,这样看来,广播机制似乎是多余的。然而,广播机制却是不可替代的,它和Binder机制不一样的地方在于,广播的发送者和接收者事先是不需要知道对方的存在的,这样带来的好处便是,系统的各个组件可以松耦合地组织在一起,这样系统就具有高度的可扩展性,容易与其他系统进行集成。

理解BroadcastReceiver

    BroadcastReceiver是一种很简单的组件,甚至在ActivityThread中都没有管理它的数据结构。因为BroadcastReceiver的本质就是通过Intent来执行应用中的一个方法,在应用中并不需要一个长期存在的对象。因为BroadcastReceiver可以在程序运行时动态向AMS注册,AMS中需要有数据结构来管理动态接收者。

从注册方式分类

    BroadcastReceiver可以分为两类。

    (1).静态接收者:是指在AndroidManifest中通过标签<receiver>指定的接收者(Android8.0后将不再使用);

        <receiver android:name=".DeviceInfo$StatsReportReceiver">
            <intent-filter>
                <action android:name="android.intent.action.STATISTICS_REPORT" />
            </intent-filter>
        </receiver>

    上面的代码就是在AndroidManifest文件中使用<receiver>标签注册的静态接收者,name属性指定BroadcastReceiver的类名;同时
还必须定义<intent-filter>,用来指定接收的Intent的种类;还可以通过属性android:priority来指定接收的优先级。

    (2).动态接收者:是指在代码中通过AMS的registerReceiver()方法注册的接收者,使用比较灵活;如果不需要接收广播了,可以通过unregisterReceiver()方法取消注册

静态和动态接收者的区别

    静态广播接收者和动态广播接收者比较明显的3个区别:

    (1).静态广播接收者在进程没有运行的时候,也可以收到广播,这时候会先启动进程,然后处理广播onReceive方法(静态广播可以拉起进程,但是目前很多厂商为了禁止应用自启动,对于广播拉起应用都有一些限制,被禁止自启动的应用,AMS直接就把应用的静态注册接收者过滤掉了);动态广播因为是在程序中通过代码显式注册的,因此必须要在进程已经运行的时候才能收到广播

    (2).静态广播处理的时候每次都会创建一个新的广播接收器对象,但是动态广播一般都是同一个广播接收器对象。

    (3).针对同一个非order广播,所有动态注册接收者要先于所有的静态接收者收到广播;同一个应用内,先注册的接收器先收到广播

从广播的发送方式分类

        (1).普通广播

        通过Context中的sendBroadcast()或sendBroadcastAsUser()方法发送的广播属于普通广播。

        普通广播的特点是:发送给系统当前的所有注册的广播接收者,广播接收者接收广播的顺序也是不确定的

        (2).有序广播

        通过Context中的sendOrderedBroadcast()或sendOrderedBroadcastAsUser()方法发送的广播属于有序广播(Order广播)。

        和普通广播不同的是:有序广播的发送顺序是按照接收者的优先级来决定的,如果优先级相同,则先注册的接收者会先收到广播,而且动态注册的接收者比静态注册的接收者优先收到广播。

        有序广播的另一个特点是:接收者可以打断广播的发送,如果接收者不希望广播继续传递,可以通过返回值来终止传递。同时接收者还可以篡改广播的内容,正因为如此,设置广播的优先级就有意义了。

    (3).粘性广播

        粘性广播有两种:一种是普通粘性广播,通过sendStickyBroadcast()方法发送;另一种是有序粘性广播,通过sendStickyOrderedBroadcast()方法发送。

        粘性广播的特点是:粘性广播在发送后就一直存在于系统的容器(AMS.mStickyBroadcasts)中,等待对应的广播接收器去处理,如果暂时没有广播接收器处理这个广播,则一直在容器中处于等待状态,粘性广播的Receiver如果被销毁,那么下次重建时会自动接收到广播。

        与前两种广播不同的是:粘性广播能发送给系统中以后注册的接收者,甚至是新安装的应用中的接收者;而前两种广播都只能发给系统已经注册的接收者。

BroadcastReceiver实现

public abstract class BroadcastReceiver {
    private PendingResult mPendingResult;
    public abstract void onReceiver(Context context, Intent intent);
    ......
}

    实现一个广播接收器,只需要从BroadcastReceiver派生一个类,重载onReceiver()方法就可以了。

    上面代码中还有一个PendingResult对象的引用变量mPendingResult,有什么作用呢?有这样一种场景,如果接收到广播后需要很长的时间来处理,而且还需要返回处理结果,该怎么办呢?

    BroadcastReceiver对象的onReceiver()方法如果长时间不返回,会引发ANR,因此如果要执行耗时的操作,必须在其他线程中完成。但是在其他线程中完成处理后,如何把处理的结果传递会AMS呢?这就需要用到mPendingResult了。这时,在继承类的onReceiver()方法中需要先调用goAsync()方法来得到mPendingResult对象,goAsync()方法如下:

    public final PendingResult goAsync() {
        PendingResult res = mPendingResult;
        mPendingResult = null;
        return res;
    }

    goAsync方法将mPendingResult引用的PendingResult对象通过返回值传递出来,同时将mPendingResult的值设置为null。

    这样当onReceiver()方法返回后,AMS如果需要广播的返回结果就会等待。在onReceive()方法结束前,需要将PendingResult对象传递到新的线程中,在新线程处理完成后,必须调用PendingResult对象的finish()方法将结果传递到AMS,AMS得到结果后,才会继续传递消息到下一个接收者。

    finish()方法我们下文会详细介绍,finish()方法中有几处判断条件,但是各条件分支最终都会执行sendFinished()方法。在sendFinished()方法中通过调用finishReceiver()方法将结果传递给AMS。

广播的数据结构

    在AMS中,所有注册的接收者都存放在成员变量mRegisteredReceiver中,定义如下:

    /**
     * Keeps track of all IIntentReceivers that have been registered for broadcasts.用来保存所有已经注册的广播
     * Hash keys are the receiver IBinder, hash value is a ReceiverList.
     */
    final HashMap<IBinder, ReceiverList> mRegisteredReceivers = new HashMap<>();

    mRegisteredReceivers是一个HashMap类型的变量,使用接收者的IBinder作为key,来存储接收者的对象ReceiverList。因为一个接收者可能包含多个IntentFilter,所以接收者的对象是一个数组。ReceiverList定义如下:

/**
 * A receiver object that has registered for one or more broadcasts.
 * The ArrayList holds BroadcastFilter objects.
 */
final class ReceiverList extends ArrayList<BroadcastFilter>
        implements IBinder.DeathRecipient {

    在ReceiverList类声明中也指出了,一个广播接收者对象已经注册过一个或多个广播,ReceiverList继承于BroadcastFilter类型的数组。

    上面的BroadcastFilter继承IntentFilter,源码如下:

final class BroadcastFilter extends IntentFilter {
    // Back-pointer to the list this filter is in.
    final ReceiverList receiverList;//所属receiver的引用
    final String packageName;//所在应用的包名
    final String requiredPermission;//所需权限字符串
    final int owningUid;//所在应用的UID
    final int owningUserId;//所在应用的UserID

    requiredPermission保存的是向receiver发送广播时需要申请的权限,owningUserId是receiver注册时的用户id。ReceiverList的定义如下:

/**
 * A receiver object that has registered for one or more broadcasts.
 * The ArrayList holds BroadcastFilter objects.
 */
final class ReceiverList extends ArrayList<BroadcastFilter>
        implements IBinder.DeathRecipient {
    final ActivityManagerService owner;
    public final IIntentReceiver receiver;//用户进程中定义的IntentReceiver
    public final ProcessRecord app;//所属用户进程的ProcessRecord对象
    public final int pid;//所属用户进程的pid
    public final int uid;
    public final int userId;
    BroadcastRecord curBroadcast = null;
    boolean linkedToDeath = false;//是否注册了“死亡通知”

    ReceiverList实现了IBinder.DeathRecipient,如果注册了receiver的应用发生了崩溃,AMS中的ReceiverList对象就会收到通知,去除这个进程的receiver。
    发送广播时,AMS中收到的广播消息首先保存在mBroadcastQueues对象中,然后在发给用户进程中的接收者。mBroadcastQueues是一个只有两个元素的数组,定义如下:

    BroadcastQueue mFgBroadcastQueue;
    BroadcastQueue mBgBroadcastQueue;
    // Convenient for easy iteration over the queues. Foreground is first在这个队列上迭代是非常简单的,
    // so that dispatch of foreground broadcasts gets precedence.首先分发前台广播
    final BroadcastQueue[] mBroadcastQueues = new BroadcastQueue[2];

    AMS初始化时将mFgBroadcastQueue赋予了mBroadcastQueues[0],将mBgBroadcastQueue赋予了mBroadcastQueues[1]。mFgBroadcastQueue用来保存带有FLAG_RECEIVER_FOREGROUND标志的广播,它要求接收者进程以forground的优先级运行,这样执行更快。如果不特别指定,一般的广播是不带这个标记的。

    BroadcastQueue的主要成员变量如下:

/**
 * BROADCASTS
 *
 * We keep two broadcast queues and associated bookkeeping, one for those at
 * foreground priority, and one for normal (background-priority) broadcasts.
 */
public final class BroadcastQueue {
......
    /**
     * Lists of all active broadcasts that are to be executed immediately该列表中的所有active广播需要立即执行(不必等待其他广播是否结束)
     * (without waiting for another broadcast to finish).  Currently this only
     * contains broadcasts to registered receivers, to avoid spinning up只包含已经注册的广播,避免IntentReceiver。
     * 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.该列表中的active广播一次只能执行一个。
     * 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<>();

    /**
     * Historical data of past broadcasts, for debugging.  This is a ring buffer
     * whose last element is at mHistoryNext.
     */
    final BroadcastRecord[] mBroadcastHistory = new BroadcastRecord[MAX_BROADCAST_HISTORY];
......

    mParallelBroadcasts用来保存所有的普通广播;mOrderedBroadcasts用来保存所有有序广播;mBroadcastHistory用来保存所有发送过的广播,主要用于调试。

    系统中所有的粘性广播都保存在AMS的成员变量mStickyBroadcast中,如下:

    /**
     * State of all active sticky broadcasts per user.  Keys are the action of the每个用户的所有active粘性广播的状态
     * sticky Intent, values are an ArrayList of all broadcasted intents with;key是action,values是action对应的广播列表;
     * that action (which should usually be one).  The SparseArray is keyed
     * by the user ID the sticky is for, and can include UserHandle.USER_ALL
     * for stickies that are sent to all users.
     */
    final SparseArray<ArrayMap<String, ArrayList<Intent>>> mStickyBroadcasts =
            new SparseArray<ArrayMap<String, ArrayList<Intent>>>();

    mStickyBroadcasts是一个SparseArray类型的数组,使用用户id做索引,保存的是ArrayMap对象,这个存储的是某个用户发送的所有粘性广播;每条记录以Intent的action字符串做索引,保存的内容是一个ArrayList对象,其中保存了包含该action的所有Intent。

    接下来,我们看一下广播的注册过程:
Android广播管理二–广播注册(registerReceiver)流程分析

    原文作者:Kitty_London
    原文地址: https://blog.csdn.net/kitty_landon/article/details/79009889
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞