一、介绍
广播的类型和区别
普通广播 (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
调用parseMonolithicPackage
,parseMonolithicPackage
内部关键流程
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);
mReceiverResolver
是IntentResolver
的实例,最终还是调用addFilter
函数,将动态注册的Intent fileter信息保存到了AMS的成员变量mReceiverResolver
中,而mRegisteredReceivers
则保存了所有动态注册的广播。
总结,静态注册是在开机时,解析manifest文件,将receiver保存到了PMS的mReceivers
中。动态注册则是通过AMS的registerReceiver
函数,保存到了自身的mRegisteredReceivers
中。
五、发送与接收
广播的详细用法,可以参考这篇文章。
Android中的广播Broadcast详解