Android进阶知识(二十九):BroadcastReceiver的工作过程

Android进阶知识(二十九):BroadcastReceiver的工作过程

  BroadcastReceiver是一种消息型组件,用于在不同的组件乃至不同的应用之间传递消息。BroadcastReceiver的工作过程主要包含两个方面,一个是广播的注册过程,另一个是广播的发送和接收过程。
  至于BroadcastReceiver的使用这里就不介绍了,另外在Android 8.0之后,Google对静态注册的广播有做了一定的使用限制,关于这方面的知识读者可以见笔者的两篇笔记,这里不多介绍:

一、BroadcastReceiver的注册过程

  广播的注册分为静态注册(在AndroidManifest文件中注册)和动态注册(代码注册)。静态注册的广播在应用安装时由系统自动完成注册,具体来说是由PMS(PackageManagerService)完成整个注册过程。除了广播,其他三大组件也都是在应用安装时由PMS解析并注册的
在这里插入图片描述
  这里我们只分析广播的动态注册过程,其具体流程如下图所示。
在这里插入图片描述
  其中,黑色虚线箭头表示调用了重载方法,最终调用箭头指向方法;紫色虚线箭头表示远程调用的IPC过程。对于BrocadcastReceiver的注册过程,需要注意几点。

  1. 注册过程是从ContextWrapper开始

  同Service的启动过程类似,广播的注册方法registerReceiver实际实现是在ContextWrapper中实现的,因此其启动过程也是从ContextWrapper开始的。

  1. ContextImpl的registerReceiverInternal方法

  ContextImpl的registerReceiverInternal方法源码如下。

private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId,
        IntentFilter filter, String broadcastPermission,
        Handler scheduler, Context context, int flags) {
    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 {
        final Intent intent = ActivityManager.getService().registerReceiver(
                mMainThread.getApplicationThread(), mBasePackageName, rd, filter,
                broadcastPermission, userId, flags);
        if (intent != null) {
            intent.setExtrasClassLoader(getClassLoader());
            intent.prepareToEnterProcess();
        }
        return intent;
    } catch (RemoteException e) {
        throw e.rethrowFromSystemServer();
    }
}

  该方法同Service绑定过程的bindServiceCommon工作类似:首先将BroadcastReceiver对象封装成IntentReceiver对象;接着调用AMS的registerReceiver实现注册过程。
  同样的,之所以进行封装原因为:第一,BroadcastReceiver作为Android的一个组件是不能直接跨进程传递的,需要通过IIntentReceiver这个Binder进行中转,具体实现为ReceiverDispatcher.InnerReceiver;第二,ReceiverDispatcher内部同时保存了BroadcastReceiver和InnerReceiver,以方便后续BroadcastReceiver的onReceive方法的调用。
在这里插入图片描述

  1. 注册过程registerReceiver方法只保存,不回调

  广播的注册过程最为不同的一点是,在远程调用AMS的registerReceiver后,该方法只进行InnerReceiver对象以及IntentFilter对象的保存,而无需像Activity和Service流程那样进行远程方法回调。其核心代码如下。

public Intent registerReceiver(IApplicationThread caller, String callerPackage,
        IIntentReceiver receiver, IntentFilter filter, String permission, int userId,
        int flags) {
    // ...
    // 保存IntentReceiver对象
    mRegisteredReceivers.put(receiver.asBinder(), rl);            
    // ...
    // 保存IntentFilter对象
    BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage,
        permission, callingUid, userId, instantApp, visibleToInstantApps);
    if (rl.containsFilter(filter)) {
        Slog.w(TAG, "Receiver with filter " + filter
            + " already registered for pid " + rl.pid
            + ", callerPackage is " + callerPackage);
    } else {
        rl.add(bf);
        if (!bf.debugCheck()) {
            Slog.w(TAG, "==> For Dynamic broadcast");
        }
        mReceiverResolver.addFilter(bf);
    }
    // ...
}

  至此广播的注册过程就分析完了。

二、BroadcastReceiver的发送和接收过程

  对于广播的发送和接收过程,当通过send方法来发送广播时,AMS会查找出匹配的广播接收者并将广播发送给它们处理。广播的发送有几种类型:普通广播、有序广播和粘性广播,即使特性不同当流程都是类似的,因此这里笔者以普通广播为例子进行分析。
在这里插入图片描述
  广播的发送和接收过程需要注意的几点如下。

  1. 广播不会发送给已经停止的应用

  broadcastIntentLocked方法比较复杂,但是代码里面有这么一行。

// By default broadcasts do not go to stopped apps.
intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES);

  从Android 3.1开始(此处用到是Android 10.0)广播有一种特性,即默认情况下广播不会发送给已经停止的应用。在Android 3.1中为Intent添加了两个标记位,分别为FLAG_INCLUDE_STOPPED_PACKAGES和FLAG_EXCLUDE_STOPPED_PACKAGES,用于控制广播是否要对处于停止状态的应用起作用,具体含义如下。

标记位含义
FLAG_INCLUDE_STOPPED_PACKAGES表示包含已经停止的应用,这个时候广播会发送给已经停止的应用
FLAG_EXCLUDE_STOPPED_PACKAGES表示不包含已经停止的应用,这个时候广播不会发送给已经停止的应用

  之所以默认不会发送广播给已经停止的应用,是为了防止广播无意间或者在不必要的时候调起已经停止的应用。如果需要调起已经停止的应用,只需要为广播的Intent添加FLAG_INCLUDE_STOPPED_PACKAGES标记位即可,这两个标记共存时以FLAG_EXCLUDE_STOPPED_PACKAGES为准。

  1. broadcastIntentLocked工作

  在该方法内部会根据intent-filter查找出匹配的广播接收者并经过一系列条件过滤,最终将满足条件的广播接收者添加到BroadcastQueue中,接着调用BroadcastQueue的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.
    if (isCallerSystem) {
        checkBroadcastFromSystem(intent, callerApp, callerPackage, callingUid,
                isProtectedBroadcast, registeredReceivers);
    }
    final BroadcastQueue queue = broadcastQueueForIntent(intent);
    BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
            callerPackage, callingPid, callingUid, callerInstantApp, resolvedType,
            requiredPermissions, appOp, brOptions, registeredReceivers, resultTo,
            resultCode, resultData, resultExtras, ordered, sticky, false, userId,
            allowBackgroundActivityStarts, timeoutExempt);
    if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing parallel broadcast " + r);
    final boolean replaced = replacePending
            && (queue.replaceParallelBroadcastLocked(r) != null); // 查找过滤
    // Note: We assume resultTo is null for non-ordered broadcasts.
    if (!replaced) {
        // 保存普通广播,并发送
        queue.enqueueParallelBroadcastLocked(r);
        queue.scheduleBroadcastsLocked();
    }
    registeredReceivers = null;
    NR = 0;
}

  至此,广播的注册、接收以及发送过程就分析完了。
在这里插入图片描述

参考资料:《Android开发艺术探索》

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值