Android broadcast receivers详细介绍

一、介绍

广播的类型和区别

普通广播Context.sendBroadcast发送的广播)
普通广播也是无序广播,它是完全异步的。它的接收者是没有顺序的,并且几乎同时的接收到广播。这种方式是高效的,但也意味者你不能给广播增加result或者调用abort APIs

有序广播Context.sendOrderedBroadcast发送的广播)
一次传递给一个广播接收者。每个广播接收者处理过程,可以携带一个result给下个接收者,或者完全阻止广播传递给其它接收者。广播接收者的接收顺序可以由android:priority属性控制;相同优先级的接收者,接收顺序是随机的。

值得注意的是,无序广播的接收者可能无法同时收到广播。例如,当接收广播的进程需要创建,一次只会创建一个进程而不是全部创建,这样可以避免系统过载。但即使这样,你依然不能携带result或阻止广播的传递。

有序和无序广播都可以带有粘滞的特性(Context.sendStickyBroadcast , Context.sendStickyOrderedBroadcast。粘滞广播在5.0的版本中由于安全问题加上了@deprecated标签,详细特性可以参考谷歌文档。

如果你的注册和广播发送都在一个进程中,最佳实践是使用本地广播LocalBroadcastManager。相对于普通广播,它有以下有点:

  • 因为广播是在app内部传播的,所以你不必担心敏感数据的泄漏。
  • 其它app是无法给你发送这些广播的,因此没有必要考虑由于广播引起的安全漏洞。
  • 本地广播相对于系统广播,是更加高效的。

二、常用系统广播

常用系统广播


三、注册方式

动态注册Context.registerReceiver
静态注册,AndroidManifest.xml中使用<receiver>标签。


四、注册的系统流程

部分参考 外链引用
静态receiver的注册是由PackageManagerService开机的时候负责初始化(后面简称PMS)。
PMS在开机的时候会对系统一些目录逐个扫描,解析apk文件。静态广播接收器就是在PMS做这件事情的时候顺便处理的。
PMS会解析apk的manifest文件,查找这里注册的receiver,然后加载到内存中。

1.开机,PMS初始化,扫描app所在目录。
源码位置:frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
PMS构造函数

// Collected privileged system packages.
final File privilegedAppDir = new File(Environment.getRootDirectory(), "priv-app");
scanDirLI(privilegedAppDir, PackageParser.PARSE_IS_SYSTEM
          | PackageParser.PARSE_IS_SYSTEM_DIR
                  | PackageParser.PARSE_IS_PRIVILEGED, scanFlags, 0);

// Collect ordinary system packages.
final File systemAppDir = new File(Environment.getRootDirectory(), "app");
scanDirLI(systemAppDir, PackageParser.PARSE_IS_SYSTEM
          | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);
// 
// 扫描其它目录

扫描的目录顺序为/vendor/overlay、system/framework、priv-app、app、/vendor/app、${oemDictionary}/app。

scanDirLI流程

final File[] files = dir.listFiles();
// ...
for (File file : files) {
    scanPackageLI(file, parseFlags | PackageParser.PARSE_MUST_BE_APK,
                        scanFlags, currentTime, null);
}

scanPackageLI流程

PackageParser pp = new PackageParser();
// ...
final PackageParser.Package pkg;
try {
      pkg = pp.parsePackage(scanFile, parseFlags);
} catch (PackageParserException e) {
      throw PackageManagerException.from(e);
}

到这里,PackageParser开始负责解析Apk。

2.PackageParser解析Apk
parsePackage调用parseMonolithicPackageparseMonolithicPackage内部关键流程

final Package pkg = parseBaseApk(apkFile, assets, flags);
pkg.codePath = apkFile.getAbsolutePath();
return pkg;

之后就是Xml解析并调用各种parse函数操作。最终在parseActivity中解析receiver标签,流程如下

Activity a = new Activity(mParseActivityArgs, new ActivityInfo());
// ...
if (receiver) {
// 填充a的info属性
}

此Activity也并非彼Activity,它是PackageParser的内部类,包含ActivityInfo属性。

public final static class Activity extends Component<ActivityIntentInfo> {
    public final ActivityInfo info;

    public Activity(final ParseComponentArgs args, final ActivityInfo _info) {
        super(args, _info);
        info = _info;
        info.applicationInfo = args.owner.applicationInfo;
    }

    public void setPackageName(String packageName) {
        super.setPackageName(packageName);
        info.packageName = packageName;
    }
}

到这里,我们已经得到了包含完整manifest信息的PackageParser.Package的实例pkg。
再然后的调用scanPackageLI,继续调用scanPackageDirtyLI,在这个函数里,系统保存了广播接收者的信息。

3.PMS保存静态注册广播信息
scanPackageDirtyLI流程

for (i=0; i<N; i++) {
    PackageParser.Activity a = pkg.receivers.get(i);
    mReceivers.addActivity(a, "receiver");
}

原来,PMS的成员变量mReceivers保存了所有静态注册的广播接收者。

addActivity流程

public final void addActivity(PackageParser.Activity a, String type) {
    mActivities.put(a.getComponentName(), a);
    final int NI = a.intents.size();
    for (int j=0; j<NI; j++) {
        PackageParser.ActivityIntentInfo intent = a.intents.get(j);
        addFilter(intent);
    }
}

addFilter位于frameworks/base/services/core/java/com/android/server/IntentResolver.java
中,流程如下

public void addFilter(F f) {
    mFilters.add(f);
    int numS = register_intent_filter(f, f.schemesIterator(),
            mSchemeToFilter, "      Scheme: ");
    int numT = register_mime_types(f, "      Type: ");
    if (numS == 0 && numT == 0) {
        register_intent_filter(f, f.actionsIterator(),
                mActionToFilter, "      Action: ");
    }
    if (numT != 0) {
        register_intent_filter(f, f.actionsIterator(),
                mTypedActionToFilter, "      TypedAction: ");
    }
}

可以看到,关于广播的intent filter信息,已经保存到了mActionToFilter等成员变量中了。


动态注册比较简单,最终调用的是ActivityManagerService(后面简称AMS)中的registerReceiver,关键流程如下

ReceiverList rl
        = (ReceiverList)mRegisteredReceivers.get(receiver.asBinder());
if (rl == null) {
    rl = new ReceiverList(this, callerApp, callingPid, callingUid,
            userId, receiver);
    if (rl.app != null) {
        rl.app.receivers.add(rl);
    } else {
        try {
            receiver.asBinder().linkToDeath(rl, 0);
        } catch (RemoteException e) {
            return sticky;
        }
        rl.linkedToDeath = true;
    }
    mRegisteredReceivers.put(receiver.asBinder(), rl);
} else if (rl.uid != callingUid) {
    // 异常处理
}
BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage,
        permission, callingUid, userId);
rl.add(bf);
mReceiverResolver.addFilter(bf);

mReceiverResolverIntentResolver的实例,最终还是调用addFilter函数,将动态注册的Intent fileter信息保存到了AMS的成员变量mReceiverResolver中,而mRegisteredReceivers则保存了所有动态注册的广播。

总结,静态注册是在开机时,解析manifest文件,将receiver保存到了PMS的mReceivers中。动态注册则是通过AMS的registerReceiver函数,保存到了自身的mRegisteredReceivers中。


五、发送与接收

广播的详细用法,可以参考这篇文章。
Android中的广播Broadcast详解


六、广播的生命周期

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值