相信广播大家都有用过,也知道安卓广播的一些基础知识,如静态广播、动态广播、粘性广播等等,但相信很多人都不知道系统层面是怎样实现这些广播特性的,这篇文章就让我们来聊一聊安卓广播机制的系统实现原理.
静态广播的注册
静态广播是通过PackageManagerService在启动的时候扫描已安装的应用去注册的.
在PackageManagerService的构造方法中,会去扫描应用安装目录,顺序是先扫描系统应用安装目录再扫描第三方应用安装目录.
PackageManagerService.scanDirLI就是用于扫描目录的方法,由于代码比较少,这里我们直接把它贴了上来:
private void scanDirLI(File dir, int flags, int scanMode, long currentTime) {
String[] files = dir.list();
if (files == null) {
return;
}
int i;
for (i=0; i
File file = new File(dir, files[i]);
if (!isPackageFilename(files[i])) {
continue;
}
PackageParser.Package pkg = scanPackageLI(file,
flags|PackageParser.PARSE_MUST_BE_APK, scanMode, currentTime, null);
if (pkg == null && (flags & PackageParser.PARSE_IS_SYSTEM) == 0 &&
mLastScanError == PackageManager.INSTALL_FAILED_INVALID_APK) {
file.delete();
}
}
}
private static final boolean isPackageFilename(String name) {
return name != null && name.endsWith(".apk");
}
可以看到,它通过File.list方法列出目录下的所有后缀为".apk"的文件传给scanPackageLI去处理.
而scanPackageLI(File scanFile,int parseFlags, int scanMode, long currentTime, UserHandle user)内部会调用它的重载方法scanPackageLI(PackageParser.Package pkg,int parseFlags, int scanMode, long currentTime, UserHandle user):
private PackageParser.Package scanPackageLI(File scanFile,int parseFlags, int scanMode, long currentTime, UserHandle user) {
...
final PackageParser.Package pkg = pp.parsePackage(scanFile,scanPath, mMetrics, parseFlags);
...
PackageParser.Package scannedPkg = scanPackageLI(pkg, parseFlags, scanMode | SCAN_UPDATE_SIGNATURE, currentTime, user);
...
}
在这个scanPackageLIl里面会解析Package并且将AndroidManifest.xml中注册的BroadcastReceiver保存下来:
...
N = pkg.receivers.size();
r = null;
for (i=0; i
PackageParser.Activity a = pkg.receivers.get(i);
a.info.processName = fixProcessName(pkg.applicationInfo.processName,
a.info.processName, pkg.applicationInfo.uid);
mReceivers.addActivity(a, "receiver");
...
}
...
所以从上面获取静态广播的流程可以看出来:系统应用的广播先于第三方应用的广播注册,而安装在同一个目录下的应用的静态广播的注册顺序是按照File.list列出来的apk的顺序注册的.他们的注册顺序就决定了它们接收广播的顺序.
通过静态广播的注册流程,我们已经将静态广播注册到了PackageManagerService的mReceivers中,而我们可以使用PackageManagerService.queryIntentReceivers方法查询intent对应的静态广播
public List queryIntentReceivers(Intent intent, String resolvedType, int flags, int userId) {
if (!sUserManager.exists(userId)) return Collections.emptyList();
ComponentName comp = intent.getComponent();
if (comp == null) {
if (intent.getSelector() != null) {
intent = intent.getSelector();
comp = intent.getComponent();
}
}
if (comp != null) {
List list = new ArrayList(1);
ActivityInfo ai = getReceiverInfo(comp, flags, userId);
if (ai != null) {
ResolveInfo