深入剖析 Android 静态广播接收模块
本人掘金号,欢迎点击关注:掘金号地址
本人公众号,欢迎点击关注:公众号地址
一、引言
在 Android 系统的开发中,广播机制作为一种重要的组件间通信方式,扮演着至关重要的角色。它允许不同的组件(如 Activity、Service、BroadcastReceiver 等)之间进行消息传递和交互。其中,静态广播接收模块是广播机制的一个重要组成部分,它允许应用在未启动的情况下就能接收系统或其他应用发出的广播消息。这种特性使得静态广播在很多场景下都非常有用,比如监听系统开机广播、电量变化广播等。深入理解 Android 静态广播接收模块的源码和工作原理,对于开发者来说是非常有必要的,它可以帮助开发者更好地利用广播机制,提高应用的性能和稳定性。本文将从源码级别深入分析 Android 静态广播接收模块,带你一步步揭开其神秘的面纱。
二、静态广播接收模块概述
2.1 静态广播的基本概念
静态广播是指在 AndroidManifest.xml 文件中注册的广播接收器。与动态广播(在代码中通过 registerReceiver
方法注册)不同,静态广播的注册信息是在应用安装时就被系统读取并保存下来的。这意味着即使应用没有启动,只要系统发出符合静态广播接收器过滤条件的广播,应用就能接收到该广播并做出相应的处理。
2.2 静态广播接收模块的作用
静态广播接收模块主要用于监听系统或其他应用发出的特定广播事件,并在接收到广播时执行相应的业务逻辑。例如,应用可以通过静态广播接收器监听系统开机广播,在开机完成后自动启动一些服务或执行一些初始化操作;还可以监听电量变化广播,根据电量情况调整应用的行为。
2.3 静态广播接收的基本流程
静态广播接收的基本流程可以分为以下几个步骤:
-
注册静态广播接收器:在 AndroidManifest.xml 文件中定义广播接收器的类名和过滤条件。
-
系统读取注册信息:在应用安装时,系统会解析 AndroidManifest.xml 文件,将静态广播接收器的注册信息保存到系统中。
-
广播发送:当系统或其他应用发出广播时,会根据广播的意图信息查找匹配的静态广播接收器。
-
广播匹配:系统会根据广播的动作、类别、数据等信息,与已注册的静态广播接收器的过滤条件进行匹配。
-
广播分发:如果匹配成功,系统会将广播分发给相应的静态广播接收器。
-
接收器处理:静态广播接收器接收到广播后,会调用其
onReceive
方法进行处理。
以下是一个简单的静态广播接收器注册示例:
xml
<!-- AndroidManifest.xml -->
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<!-- 注册静态广播接收器 -->
<receiver
android:name=".MyStaticReceiver"
android:enabled="true"
android:exported="true">
<!-- 定义广播接收器的过滤条件 -->
<intent-filter>
<!-- 监听特定的广播动作 -->
<action android:name="com.example.MY_STATIC_BROADCAST" />
</intent-filter>
</receiver>
</application>
java
// MyStaticReceiver.java
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
// 自定义静态广播接收器类,继承自 BroadcastReceiver
public class MyStaticReceiver extends BroadcastReceiver {
private static final String TAG = "MyStaticReceiver";
@Override
// 当接收到广播时,系统会调用该方法
public void onReceive(Context context, Intent intent) {
// 获取广播的动作
String action = intent.getAction();
if ("com.example.MY_STATIC_BROADCAST".equals(action)) {
// 处理接收到的广播
Log.d(TAG, "Received static broadcast: " + action);
}
}
}
三、静态广播接收器的注册
3.1 AndroidManifest.xml 文件解析
在应用安装过程中,系统会解析 AndroidManifest.xml 文件,提取其中的静态广播接收器注册信息。这一过程主要由 PackageParser
类完成。以下是 PackageParser
类中解析 <receiver>
标签的部分源码分析:
java
// PackageParser.java
/**
* 解析 <receiver> 标签
* @param res XML 资源解析器
* @param parser XML 解析器
* @param flags 解析标志
* @return 解析得到的 Activity 对象,在系统内部,广播接收器被视为一种特殊的 Activity
* @throws XmlPullParserException XML 解析异常
* @throws IOException 输入输出异常
*/
private Activity parseReceiver(Resources res, XmlPullParser parser, int flags)
throws XmlPullParserException, IOException {
// 创建一个 Activity 对象来表示广播接收器
Activity receiver = new Activity();
// 获取 XML 标签的属性集合
AttributeSet attrs = Xml.asAttributeSet(parser);
// 获取资源属性数组
TypedArray sa = res.obtainAttributes(attrs,
com.android.internal.R.styleable.AndroidManifestReceiver);
// 获取广播接收器的类名
receiver.className = sa.getString(
com.android.internal.R.styleable.AndroidManifestReceiver_name);
if (receiver.className == null) {
// 如果类名未指定,抛出异常
throw new XmlPullParserException(parser.getPositionDescription()
+ ": <receiver> does not specify android:name");
}
// 获取广播接收器是否启用的标志
receiver.enabled = sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestReceiver_enabled, true);
// 获取广播接收器是否可导出的标志
receiver.exported = sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestReceiver_exported, false);
// 回收资源属性数组
sa.recycle();
// 解析 <intent-filter> 标签
int type;
while ((type = parser.next()) != XmlPullParser.END_TAG
|| parser.getDepth() > depth) {
if (type != XmlPullParser.START_TAG) {
continue;
}
String tagName = parser.getName();
if ("intent-filter".equals(tagName)) {
// 解析意图过滤器
IntentFilter filter = parseIntentFilter(res, parser, false, false);
if (filter != null) {
// 将意图过滤器添加到广播接收器的过滤器列表中
receiver.intentFilters.add(filter);
}
}
}
return receiver;
}
在上述代码中,parseReceiver
方法会解析 <receiver>
标签的属性,包括类名、是否启用、是否可导出等。同时,会递归解析 <intent-filter>
标签,将解析得到的意图过滤器添加到广播接收器的 intentFilters
列表中。
3.2 注册信息的保存
解析得到的静态广播接收器注册信息会被保存到 PackageParser.Package
对象中。PackageParser.Package
对象表示一个应用的包信息,其中包含了该应用的所有组件信息,包括静态广播接收器。以下是相关源码分析:
java
// PackageParser.java
/**
* 解析应用的 AndroidManifest.xml 文件
* @param packageFile 应用的 APK 文件
* @param flags 解析标志
* @return 解析得到的应用包信息
* @throws PackageParserException 包解析异常
*/
public Package parsePackage(File packageFile, int flags)
throws PackageParserException {
// 创建一个 Package 对象来表示应用包
Package pkg = new Package();
// 解析 APK 文件中的 AndroidManifest.xml 文件
XmlResourceParser parser = null;
try {
parser = loadXmlMetaData(packageFile, ANDROID_MANIFEST_FILENAME);
if (parser == null) {
// 如果无法读取 AndroidManifest.xml 文件,抛出异常
throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
"Failed to read AndroidManifest.xml of " + packageFile);
}
int type;
while ((type = parser.next()) != XmlPullParser.START_TAG
&& type != XmlPullParser.END_DOCUMENT) {
// 跳过非起始标签
}
if (type != XmlPullParser.START_TAG) {
// 如果没有找到起始标签,抛出异常
throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
"No start tag found in " + packageFile);
}
if (!"manifest".equals(parser.getName())) {
// 如果起始标签不是 <manifest>,抛出异常
throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
"AndroidManifest.xml does not start with <manifest> tag");
}
// 解析 <manifest> 标签
parseBaseApkElements(parser, pkg, flags);
// 解析 <application> 标签
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
if (type == XmlPullParser.START_TAG) {
String tagName = parser.getName();
if ("application".equals(tagName)) {
// 解析应用标签
parseApplication(parser, pkg, flags);
}
}
}
} catch (XmlPullParserException e) {
// 处理 XML 解析异常
throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
"Failed to parse AndroidManifest.xml of " + packageFile, e);
} catch (IOException e) {
// 处理输入输出异常
throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
"Failed to read AndroidManifest.xml of " + packageFile, e);
} finally {
if (parser != null) {
// 关闭解析器
parser.close();
}
}
return pkg;
}
/**
* 解析 <application> 标签
* @param parser XML 资源解析器
* @param pkg 应用包信息
* @param flags 解析标志
* @throws XmlPullParserException XML 解析异常
* @throws IOException 输入输出异常
*/
private void parseApplication(XmlPullParser parser, Package pkg, int flags)
throws XmlPullParserException, IOException {
int depth = parser.getDepth();
int type;
while ((type = parser.next()) != XmlPullParser.END_TAG
|| parser.getDepth() > depth) {
if (type != XmlPullParser.START_TAG) {
continue;
}
String tagName = parser.getName();
if ("receiver".equals(tagName)) {
// 解析 <receiver> 标签
Activity receiver = parseReceiver(pkg.resources, parser, flags);
if (receiver != null) {
// 将解析得到的广播接收器添加到应用包的 receivers 列表中
pkg.receivers.add(receiver);
}
}
}
}
在上述代码中,parsePackage
方法会解析应用的 AndroidManifest.xml 文件,将解析得到的应用包信息保存到 PackageParser.Package
对象中。parseApplication
方法会解析 <application>
标签,当遇到 <receiver>
标签时,会调用 parseReceiver
方法进行解析,并将解析得到的广播接收器添加到 pkg.receivers
列表中。
3.3 系统对注册信息的管理
系统会将所有应用的静态广播接收器注册信息保存到 PackageManagerService
中。PackageManagerService
是 Android 系统中负责管理应用包信息的核心服务。以下是 PackageManagerService
中保存和管理静态广播接收器注册信息的部分源码分析:
java
// PackageManagerService.java
/**
* 安装应用包
* @param packageFile 应用的 APK 文件
* @param installFlags 安装标志
* @param installerPackageName 安装器应用的包名
* @param userId 用户 ID
* @return 安装结果
*/
private int installPackageLI(File packageFile, int installFlags,
String installerPackageName, int userId) {
// 创建一个 PackageParser 对象用于解析 APK 文件
PackageParser pp = new PackageParser();
// 解析 APK 文件,得到应用包信息
PackageParser.Package pkg = pp.parsePackage(packageFile, installFlags);
if (pkg == null) {
// 如果解析失败,返回解析失败的结果
return INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
}
// 保存应用的包信息
synchronized (mPackages) {
// 将应用包信息添加到 mPackages 映射中
mPackages.put(pkg.packageName, pkg);
}
// 处理静态广播接收器的注册信息
for (Activity receiver : pkg.receivers) {
// 创建一个 BroadcastFilter 对象来表示广播过滤器
BroadcastFilter filter = new BroadcastFilter(receiver.intentFilters,
receiver.packageName, userId);
// 将广播过滤器添加到接收器解析器中
mReceiverResolver.addFilter(filter);
}
return INSTALL_SUCCEEDED;
}
在上述代码中,installPackageLI
方法会解析应用的 APK 文件,将解析得到的应用包信息保存到 mPackages
中。同时,会遍历应用的静态广播接收器列表,为每个接收器创建一个 BroadcastFilter
对象,并将其添加到 mReceiverResolver
中。mReceiverResolver
是一个 IntentResolver
对象,用于管理和查找匹配的广播过滤器。
四、广播的匹配机制
4.1 广播意图的解析
当系统或其他应用发出广播时,会创建一个 Intent
对象来封装广播的相关信息,如广播的动作、类别、数据等。系统会对这个 Intent
对象进行解析,以便后续进行匹配操作。以下是 Intent
类中部分关键属性和方法的源码分析:
java
// Intent.java
// 广播的动作
private String mAction;
// 广播的类别集合
private ArrayList<String> mCategories;
// 广播的数据 URI
private Uri mData;
// 广播的数据类型
private String mType;
/**
* 设置广播的动作
* @param action 广播的动作
* @return 当前 Intent 对象
*/
public Intent setAction(String action) {
mAction = action;
return this;
}
/**
* 获取广播的动作
* @return 广播的动作
*/
public String getAction() {
return mAction;
}
/**
* 添加广播的类别
* @param category 广播的类别
* @return 当前 Intent 对象
*/
public Intent addCategory(String category) {
if (mCategories == null) {
// 如果类别集合为空,创建一个新的集合
mCategories = new ArrayList<String>();
}
if (!mCategories.contains(category)) {
// 如果集合中不包含该类别,添加到集合中
mCategories.add(category);
}
return this;
}
/**
* 获取广播的类别集合
* @return 广播的类别集合
*/
public Set<String> getCategories() {
if (mCategories == null) {
return null;
}
// 返回类别集合的副本
return new HashSet<String>(mCategories);
}
/**
* 设置广播的数据 URI
* @param data 广播的数据 URI
* @return 当前 Intent 对象
*/
public Intent setData(Uri data) {
mData = data;
mType = null;
return this;
}
/**
* 获取广播的数据 URI
* @return 广播的数据 URI
*/
public Uri getData() {
return mData;
}
/**
* 设置广播的数据类型
* @param type 广播的数据类型
* @return 当前 Intent 对象
*/
public Intent setType(String type) {
mType = type;
mData = null;
return this;
}
/**
* 获取广播的数据类型
* @return 广播的数据类型
*/
public String getType() {
return mType;
}
在上述代码中,Intent
类提供了一系列方法来设置和获取广播的动作、类别、数据 URI 和数据类型等信息。这些信息将用于后续的广播匹配操作。
4.2 静态广播接收器的过滤条件
静态广播接收器的过滤条件是通过 <intent-filter>
标签定义的。<intent-filter>
标签可以包含一个或多个 <action>
、<category>
和 <data>
子标签,分别用于指定广播的动作、类别和数据过滤条件。以下是 IntentFilter
类中部分关键属性和方法的源码分析:
java
// IntentFilter.java
// 广播的动作集合
private ArrayList<String> mActions;
// 广播的类别集合
private ArrayList<String> mCategories;
// 广播的数据 URI 过滤规则集合
private ArrayList<PatternMatcher> mDataSchemes;
private ArrayList<PatternMatcher> mDataHosts;
private ArrayList<PatternMatcher> mDataPaths;
// 广播的数据类型过滤规则集合
private ArrayList<String> mDataTypes;
/**
* 添加广播的动作
* @param action 广播的动作
*/
public void addAction(String action) {
if (mActions == null) {
// 如果动作集合为空,创建一个新的集合
mActions = new ArrayList<String>();
}
if (!mActions.contains(action)) {
// 如果集合中不包含该动作,添加到集合中
mActions.add(action);
}
}
/**
* 添加广播的类别
* @param category 广播的类别
*/
public void addCategory(String category) {
if (mCategories == null) {
// 如果类别集合为空,创建一个新的集合
mCategories = new ArrayList<String>();
}
if (!mCategories.contains(category)) {
// 如果集合中不包含该类别,添加到集合中
mCategories.add(category);
}
}
/**
* 添加广播的数据 URI 过滤规则
* @param scheme 数据 URI 的协议
*/
public void addDataScheme(String scheme) {
if (mDataSchemes == null) {
// 如果数据 URI 协议过滤规则集合为空,创建一个新的集合
mDataSchemes = new ArrayList<PatternMatcher>();
}
// 创建一个 PatternMatcher 对象表示协议过滤规则,并添加到集合中
mDataSchemes.add(new PatternMatcher(scheme, PatternMatcher.PATTERN_LITERAL));
}
/**
* 添加广播的数据类型过滤规则
* @param type 数据类型
* @throws IntentFilter.MalformedMimeTypeException 数据类型格式错误异常
*/
public void addDataType(String type) throws MalformedMimeTypeException {
if (mDataTypes == null) {
// 如果数据类型过滤规则集合为空,创建一个新的集合
mDataTypes = new ArrayList<String>();
}
if (!mDataTypes.contains(type)) {
// 如果集合中不包含该数据类型,添加到集合中
mDataTypes.add(type);
}
}
/**
* 检查广播意图是否匹配该过滤器
* @param intent 广播意图
* @param resolvedType 解析后的广播数据类型
* @return 是否匹配
*/
public boolean matchIntent(Intent intent, String resolvedType) {
// 检查广播的动作是否匹配
String action = intent.getAction();
if (action == null) {
if (mActions != null && mActions.size() > 0) {
// 如果广播没有动作,而过滤器有动作要求,则不匹配
return false;
}
} else {
if (mActions == null ||!mActions.contains(action)) {
// 如果广播动作不在过滤器的动作列表中,则不匹配
return false;
}
}
// 检查广播的类别是否匹配
Set<String> categories = intent.getCategories();
if (categories != null) {
for (String category : categories) {
if (mCategories == null ||!mCategories.contains(category)) {
// 如果广播的某个类别不在过滤器的类别列表中,则不匹配
return false;
}
}
}
// 检查广播的数据 URI 和数据类型是否匹配
Uri data = intent.getData();
if (data != null) {
if (!matchData(data, resolvedType)) {
// 如果数据 URI 和数据类型不匹配过滤器的规则,则不匹配
return false;
}
}
return true;
}
/**
* 检查广播的数据 URI 和数据类型是否匹配
* @param data 广播的数据 URI
* @param resolvedType 解析后的广播数据类型
* @return 是否匹配
*/
private boolean matchData(Uri data, String resolvedType) {
// 检查数据 URI 的协议是否匹配
if (mDataSchemes != null) {
String scheme = data.getScheme();
boolean found = false;
for (PatternMatcher matcher : mDataSchemes) {
if (matcher.match(scheme)) {
// 如果数据 URI 的协议匹配过滤器的协议规则,则标记为找到匹配
found = true;
break;
}
}
if (!found) {
// 如果没有找到匹配的协议规则,则不匹配
return false;
}
}
// 检查数据 URI 的主机名是否匹配
if (mDataHosts != null) {
String host = data.getHost();
boolean found = false;
for (PatternMatcher matcher : mDataHosts) {
if (matcher.match(host)) {
// 如果数据 URI 的主机名匹配过滤器的主机名规则,则标记为找到匹配
found = true;
break;
}
}
if (!found) {
// 如果没有找到匹配的主机名规则,则不匹配
return false;
}
}
// 检查数据 URI 的路径是否匹配
if (mDataPaths != null) {
String path = data.getPath();
boolean found = false;
for (PatternMatcher matcher : mDataPaths) {
if (matcher.match(path)) {
// 如果数据 URI 的路径匹配过滤器的路径规则,则标记为找到匹配
found = true;
break;
}
}
if (!found) {
// 如果没有找到匹配的路径规则,则不匹配
return false;
}
}
// 检查数据类型是否匹配
if (mDataTypes != null) {
if (resolvedType == null) {
// 如果解析后的广播数据类型为空,则不匹配
return false;
}
boolean found = false;
for (String type : mDataTypes) {
if (MimeTypeUtils.hasWildcard(type)
? MimeTypeUtils.mimeTypeAgainstFilter(type, resolvedType)
: type.equals(resolvedType)) {
// 如果数据类型匹配过滤器的数据类型规则,则标记为找到匹配
found = true;
break;
}
}
if (!found) {
// 如果没有找到匹配的数据类型规则,则不匹配
return false;
}
}
return true;
}
在上述代码中,IntentFilter
类提供了一系列方法来添加广播的动作、类别、数据 URI 和数据类型过滤规则。matchIntent
方法用于检查广播意图是否匹配该过滤器,它会依次检查广播的动作、类别、数据 URI 和数据类型是否匹配。matchData
方法用于检查广播的数据 URI 和数据类型是否匹配,它会分别检查数据 URI 的协议、主机名、路径和数据类型是否符合过滤规则。
4.3 广播匹配的源码分析
在 PackageManagerService
中,mReceiverResolver
负责管理和查找匹配的广播过滤器。当系统发出广播时,会调用 mReceiverResolver
的 queryIntent
方法来查找匹配的静态广播接收器。以下是 IntentResolver
类中 queryIntent
方法的源码分析:
java
// IntentResolver.java
/**
* 查找匹配的广播过滤器
* @param intent 广播意图
* @param resolvedType 解析后的广播数据类型
* @param defaultOnly 是否只查找默认过滤器
* @param userId 用户 ID
* @return 匹配的广播过滤器列表
*/
public List<BroadcastFilter> queryIntent(Intent intent, String resolvedType,
boolean defaultOnly, int userId) {
// 创建一个列表用于存储匹配的广播过滤器
ArrayList<BroadcastFilter> results = new ArrayList<BroadcastFilter>();
synchronized (this) {
int N = mFilters.size();
for (int i = 0; i < N; i++) {
// 获取当前的广播过滤器
BroadcastFilter filter = mFilters.get(i);
if (filter.getUserId() == userId && filter.matchIntent(intent, resolvedType)) {
// 如果过滤器的用户 ID 匹配且广播意图匹配该过滤器,则将其添加到结果列表中
results.add(filter);
}
}
}
return results;
}
在上述代码中,queryIntent
方法会遍历 mFilters
列表,检查每个 BroadcastFilter
的用户 ID 是否匹配,以及广播意图是否匹配该过滤器。如果匹配成功,则将该过滤器添加到结果列表中。
五、广播的分发与处理
5.1 广播的分发过程
当系统找到匹配的静态广播接收器后,会将广播分发给这些接收器进行处理。广播的分发过程主要由 ActivityManagerService
负责。以下是 ActivityManagerService
中处理广播分发的部分源码分析:
java
// ActivityManagerService.java
/**
* 处理广播意图
* @param caller 调用者的应用线程
* @param intent 广播意图
* @param resolvedType 解析后的广播数据类型
* @param resultTo 结果接收器
* @param resultCode 结果代码
* @param resultData 结果数据
* @param resultExtras 结果额外数据
* @param requiredPermissions 所需的权限
* @param appOp 应用操作
* @param bOptions 广播选项
* @param serialized 是否序列化
* @param sticky 是否为粘性广播
* @param userId 用户 ID
* @return 广播处理结果
*/
public final int broadcastIntent(IApplicationThread caller,
Intent intent, String resolvedType, IIntentReceiver resultTo,
int resultCode, String resultData, Bundle resultExtras,
String[] requiredPermissions, int appOp, Bundle bOptions,
boolean serialized, boolean sticky, int userId) {
// 检查调用者是否为隔离进程
enforceNotIsolatedCaller("broadcastIntent");
synchronized(this) {
// 验证广播意图
intent = verifyBroadcastLocked(intent);
// 获取调用者的应用进程记录
final ProcessRecord callerApp = getRecordForAppLocked(caller);
// 获取调用者的进程 ID
final int callingPid = Binder.getCallingPid();
// 获取调用者的用户 ID
final int callingUid = Binder.getCallingUid();
// 清除调用者的身份标识
final long origId = Binder.clearCallingIdentity();
try {
// 检查广播的权限
int res = checkBroadcastFromSystem(intent, callerApp, callerPackage, callingUid,
isProtectedBroadcast(intent), receiverPermission);
if (res != PackageManager.PERMISSION_GRANTED) {
if (res == PackageManager.PERMISSION_DENIED) {
// 权限被拒绝,记录日志
Slog.w(TAG, "Permission Denial: broadcasting Intent " + intent
+ " from " + callerApp + " (pid=" + Binder.getCallingPid()
+ ", uid=" + Binder.getCallingUid() + ") requires "
+ receiverPermission);
return ActivityManager.BROADCAST_FAILED;
}
// 其他权限问题,记录日志
Slog.w(TAG, "Error broadcasting Intent " + intent + " from " + callerApp
+ ": " + res);
return ActivityManager.BROADCAST_FAILED;
}
// 查找匹配的静态广播接收器
List<ResolveInfo> receivers = null;
List<BroadcastFilter> registeredReceivers = null;
if ((intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY)
== 0) {
// 收集匹配的组件
receivers = collectReceiverComponents(intent, resolvedType, callingUid, users);
}
if (intent.getComponent() == null) {
if (userId == UserHandle.USER_ALL && callingUid == Process.SHELL_UID) {
// 逐个目标用户查询,排除受限用户
for (int i = 0; i < mUserController.getMaxUsers(); i++) {
if (!mUserController.hasUserRestriction(
UserManager.DISALLOW_DEBUGGING_FEATURES, i)) {
receivers = collectReceiverComponents(intent, resolvedType, callingUid, i);
if (receivers != null && receivers.size() > 0) {
break;
}
}
}
} else {
// 查询注册的广播过滤器
registeredReceivers = mReceiverResolver.queryIntent(intent,
resolvedType, false /*defaultOnly*/, userId);
}
}
// 合并静态接收器和动态接收器
int NR = registeredReceivers != null? registeredReceivers.size() : 0;
if (!ordered && NR > 0) {
if (receivers == null) {
receivers = new ArrayList();
}
receivers.addAll(registeredReceivers);
}
// 处理粘性广播
if (sticky) {
if (receivers == null) {
receivers = new ArrayList();
}
ArrayMap stickyIntents = mStickyBroadcasts.get(intent.getAction());
if (stickyIntents != null) {
receivers.add(stickyIntents.get(intent.getComponent()));
}
}
// 排序接收器
if (receivers != null) {
if (ordered) {
// 对接收器进行排序
Collections.sort(receivers, new ReceiverComparator());
}
}
// 创建广播队列
BroadcastQueue queue = broadcastQueueForIntent(intent);
// 创建广播记录
BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
callerPackage, callingPid, callingUid, resolvedType,
requiredPermissions, appOp, brOptions, receivers, resultTo,
resultCode, resultData, resultExtras, ordered, sticky, false, userId);
// 处理广播
if (receiverList != null) {
// 将广播记录加入并行广播队列
queue.enqueueParallelBroadcastLocked(r);
// 调度广播处理
queue.scheduleBroadcastsLocked();
} else {
// 将广播记录加入有序广播队列
queue.enqueueOrderedBroadcastLocked(r);
// 调度广播处理
queue.scheduleBroadcastsLocked();
}
return ActivityManager.BROADCAST_SUCCESS;
} finally {
// 恢复调用者的身份标识
Binder.restoreCallingIdentity(origId);
}
}
}
在上述代码中,broadcastIntent
方法会进行权限检查、查找匹配的静态广播接收器、合并静态接收器和动态接收器、处理粘性广播、排序接收器、创建广播队列等操作,最后将广播记录加入到广播队列中进行处理。
5.2 静态广播接收器的处理过程
当静态广播接收器接收到广播后,会调用其 onReceive
方法进行处理。onReceive
方法是 BroadcastReceiver
类的抽象方法,需要开发者在自定义的广播接收器类中实现。以下是 BroadcastReceiver
类的部分源码分析:
java
// BroadcastReceiver.java
/**
* 当接收到广播时调用的方法
* @param context 上下文对象
* @param intent 广播意图
*/
public abstract void onReceive(Context context, Intent intent);
在自定义的广播接收器类中,需要实现 onReceive
方法,在该方法中编写具体的广播处理逻辑。以下是一个简单的示例:
java
// MyStaticReceiver.java
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
// 自定义静态广播接收器类,继承自 BroadcastReceiver
public class MyStaticReceiver extends BroadcastReceiver {
private static final String TAG = "MyStaticReceiver";
@Override
// 当接收到广播时,系统会调用该方法
public void onReceive(Context context, Intent intent) {
// 获取广播的动作
String action = intent.getAction();
if ("com.example.MY_STATIC_BROADCAST".equals(action)) {
// 处理接收到的广播
Log.d(TAG, "Received static broadcast: " + action);
// 可以在这里添加更多的处理逻辑,如启动服务、更新 UI 等
}
}
}
在上述代码中,MyStaticReceiver
类继承自 BroadcastReceiver
类,并实现了 onReceive
方法。当接收到 com.example.MY_STATIC_BROADCAST
广播时,会在日志中输出相应的信息,并可以添加更多的处理逻辑。
5.3 广播处理的线程问题
静态广播接收器的 onReceive
方法是在主线程中执行的。因此,在 onReceive
方法中不应该执行耗时的操作,否则会导致应用的 UI 卡顿。如果需要执行耗时的操作,可以在 onReceive
方法中启动一个服务或线程来处理。以下是一个在 onReceive
方法中启动服务的示例:
java
// MyStaticReceiver.java
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
// 自定义静态广播接收器类,继承自 BroadcastReceiver
public class MyStaticReceiver extends BroadcastReceiver {
private static final String TAG = "MyStaticReceiver";
@Override
// 当接收到广播时,系统会调用该方法
public void onReceive(Context context, Intent intent) {
// 获取广播的动作
String action = intent.getAction();
if ("com.example.MY_STATIC_BROADCAST".equals(action)) {
Log.d(TAG, "Received static broadcast: " + action);
// 启动一个服务来处理耗时操作
Intent serviceIntent = new Intent(context, MyService.class);
context.startService(serviceIntent);
}
}
}
java
// MyService.java
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;
// 自定义服务类,继承自 Service
public class MyService extends Service {
private static final String TAG = "MyService";
@Override
// 当服务启动时调用的方法
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "Service started");
// 执行耗时操作,如网络请求、文件读写等
new Thread(new Runnable() {
@Override
public void run() {
//
六、静态广播接收模块与 Android 系统启动流程的关联
6.1 系统启动时静态广播接收器注册信息的加载
在 Android 系统启动过程中,PackageManagerService
的启动至关重要,它负责加载所有应用的组件注册信息,其中就包括静态广播接收器。在 PackageManagerService
的初始化过程中,会调用 readLPw
方法来读取系统中已安装应用的持久化数据,其中包含静态广播接收器的注册信息。
java
// PackageManagerService.java
private void readLPw() {
// 从文件中读取系统包管理器的状态数据
File packageListFile = new File(mPackagesDir, "packages.list");
FileInputStream str = null;
try {
str = new FileInputStream(packageListFile);
BufferedReader br = new BufferedReader(new InputStreamReader(str, "utf-8"));
String line;
while ((line = br.readLine()) != null) {
if (line.length() == 0 || line.charAt(0) == '#') {
continue;
}
String[] fields = line.split("\s");
int i = 0;
String pkgName = fields[i++];
// 解析包名、用户 ID 等信息
int userId = UserHandle.parseUserId(Integer.parseInt(fields[i++]));
// 后续处理包信息,包括加载静态广播接收器相关数据
PackageParser.Package pkg = readPackageLPw(pkgName, userId, false, false);
if (pkg != null) {
synchronized (mPackages) {
mPackages.put(pkgName, pkg);
}
// 处理静态广播接收器
for (Activity receiver : pkg.receivers) {
BroadcastFilter filter = new BroadcastFilter(receiver.intentFilters,
receiver.packageName, userId);
mReceiverResolver.addFilter(filter);
}
}
}
br.close();
} catch (IOException e) {
Slog.w(TAG, "Problem reading packages list file " + packageListFile, e);
} finally {
if (str != null) {
try {
str.close();
} catch (IOException e) {
}
}
}
}
在上述代码中,readLPw
方法逐行读取 packages.list
文件,该文件记录了系统中已安装应用的基本信息。对于每个应用包,通过 readPackageLPw
方法解析其详细信息,包括静态广播接收器。解析后为每个静态广播接收器创建 BroadcastFilter
对象,并添加到 mReceiverResolver
中,从而完成系统启动时静态广播接收器注册信息的加载,使得系统在启动后就能对广播进行匹配和分发。
6.2 系统开机广播的处理机制
系统开机完成后会发送 ACTION_BOOT_COMPLETED
广播,这是一个典型的由系统发出且常被应用通过静态广播接收器监听的广播。在 ActivityManagerService
中,当系统启动流程接近尾声时,会发送该广播。
java
// ActivityManagerService.java
private void systemReady() {
// 系统准备完成后的一系列操作
//...
Intent intent = new Intent(Intent.ACTION_BOOT_COMPLETED);
intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
sendBroadcastAsUser(intent, UserHandle.ALL);
//...
}
private void sendBroadcastAsUser(Intent intent, UserHandle user) {
enforceNotIsolatedCaller("sendBroadcast");
// 获取调用者相关信息
final IApplicationThread caller = Binder.getCallingPid() == MY_PID? null
: ActivityThread.currentApplicationThread();
// 调用广播处理方法
broadcastIntent(caller, intent, null, null, 0, null, null, null,
AppOpsManager.OP_NONE, null, false, false, user.getIdentifier());
}
在 systemReady
方法中创建了 ACTION_BOOT_COMPLETED
广播意图,并设置相关标志,然后通过 sendBroadcastAsUser
方法调用 broadcastIntent
进行广播发送。在广播匹配阶段,系统会根据之前加载的静态广播接收器注册信息,使用 IntentResolver
的 queryIntent
方法查找匹配的静态广播接收器。若某个应用在 AndroidManifest.xml 中注册了监听 ACTION_BOOT_COMPLETED
的静态广播接收器,且过滤条件匹配,该接收器就会接收到此广播,进而在 onReceive
方法中执行如启动后台服务、进行系统初始化配置等相关操作。
七、静态广播接收模块的安全与隐私问题
7.1 权限滥用风险
静态广播接收器如果注册了过多敏感权限的广播监听,可能会导致权限滥用问题。例如,监听 ACTION_PACKAGE_ADDED
、ACTION_PACKAGE_REMOVED
等广播,若应用在没有合理用途的情况下获取这些信息,可能会对用户隐私造成威胁。在 PackageManagerService
中,对广播权限的检查在 checkBroadcastFromSystem
方法中进行。
java
// PackageManagerService.java
int checkBroadcastFromSystem(Intent intent, ProcessRecord callerApp,
String callerPackage, int callingUid, boolean isProtectedBroadcast,
String receiverPermission) {
// 检查调用者是否有发送广播的权限
if (callerApp != null) {
PackageParser.Package pkg = callerApp.info;
if (pkg != null) {
// 检查应用是否声明了相应权限
if (receiverPermission != null) {
int perm = checkUidPermission(receiverPermission, callingUid);
if (perm != PackageManager.PERMISSION_GRANTED) {
return perm;
}
}
// 对受保护广播的特殊权限检查
if (isProtectedBroadcast) {
if (!pkg.requestsCleartextTraffic() &&!callerApp.isPersistent()) {
return PackageManager.PERMISSION_DENIED;
}
}
}
}
return PackageManager.PERMISSION_GRANTED;
}
上述代码中,checkBroadcastFromSystem
方法会检查发送广播的应用是否具有相应权限。若应用没有声明对应广播所需的权限,或在处理受保护广播时不符合条件,都会导致权限检查不通过,广播发送失败。然而,部分恶意应用可能会通过欺骗用户授予权限的方式,利用静态广播接收器获取敏感信息,因此开发者在使用静态广播接收器时应谨慎声明权限,并确保权限使用的合理性。
7.2 组件导出风险
当静态广播接收器的 android:exported
属性设置为 true
时,意味着该接收器可以被其他应用调用和触发。如果开发者没有正确评估风险,可能会导致组件导出风险。例如,外部恶意应用可能会构造特定的广播意图,触发该静态广播接收器,执行一些开发者未预期的操作,如启动敏感服务、修改数据等。在 PackageParser
解析 <receiver>
标签时,会获取该属性值。
java
// PackageParser.java
private Activity parseReceiver(Resources res, XmlPullParser parser, int flags)
throws XmlPullParserException, IOException {
Activity receiver = new Activity();
AttributeSet attrs = Xml.asAttributeSet(parser);
TypedArray sa = res.obtainAttributes(attrs,
com.android.internal.R.styleable.AndroidManifestReceiver);
//...
receiver.exported = sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestReceiver_exported, false);
//...
return receiver;
}
系统在处理广播分发时,也会考虑该属性。若 android:exported
为 true
,且满足其他匹配条件,外部应用发送的广播就可能触发该接收器。因此,开发者应根据实际需求谨慎设置该属性,对于不需要外部调用的静态广播接收器,应将其设置为 false
,以降低安全风险。
八、静态广播接收模块的性能优化策略
8.1 减少不必要的静态广播注册
过多不必要的静态广播接收器注册会增加系统解析 AndroidManifest.xml 文件的负担,占用更多内存空间,同时也会在广播匹配阶段消耗更多时间。开发者应仔细评估业务需求,仅注册真正需要的静态广播接收器。例如,对于一些仅在应用运行时才需要监听的广播事件,可采用动态广播注册的方式,避免静态注册带来的资源浪费。在项目开发过程中,可以通过代码审查和需求分析,梳理出哪些广播事件是必须通过静态广播接收器监听,哪些可以采用更灵活的动态注册方式,从而优化静态广播接收器的注册数量。
8.2 优化过滤条件的精准度
复杂且宽泛的过滤条件会增加广播匹配的计算量。在定义静态广播接收器的过滤条件时,应尽可能精准。例如,避免使用过多的通配符或不明确的匹配规则。在 IntentFilter
的 matchIntent
和 matchData
方法中,过滤条件越多、越复杂,匹配过程所需的计算时间就越长。
java
// IntentFilter.java
public boolean matchIntent(Intent intent, String resolvedType) {
// 检查动作、类别、数据等匹配情况
//...
Uri data = intent.getData();
if (data != null) {
if (!matchData(data, resolvedType)) {
return false;
}
}
return true;
}
private boolean matchData(Uri data, String resolvedType) {
// 检查数据 URI 的协议、主机名、路径和数据类型匹配情况
//...
if (mDataTypes != null) {
if (resolvedType == null) {
return false;
}
boolean found = false;
for (String type : mDataTypes) {
if (MimeTypeUtils.hasWildcard(type)
? MimeTypeUtils.mimeTypeAgainstFilter(type, resolvedType)
: type.equals(resolvedType)) {
found = true;
break;
}
}
if (!found) {
return false;
}
}
return true;
}
从上述代码可以看出,每一项过滤条件的检查都需要进行一定的计算和判断。因此,优化过滤条件,使其更精准地匹配实际需要监听的广播,能够显著提高广播匹配效率,减少系统资源消耗。
8.3 避免在 onReceive 方法中执行耗时操作
由于 onReceive
方法在主线程执行,若其中包含耗时操作,如网络请求、大量数据读写等,会导致主线程阻塞,影响应用的 UI 响应和整体性能。一种优化方式是在 onReceive
方法中启动 IntentService
来处理耗时任务。IntentService
是 Service
的子类,它会在后台线程中处理任务,处理完成后自动停止。
java
// MyStaticReceiver.java
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
public class MyStaticReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// 启动 IntentService 处理任务
Intent serviceIntent = new Intent(context, MyIntentService.class);
context.startService(serviceIntent);
}
}
// MyIntentService.java
import android.app.IntentService;
import android.content.Intent;
import android.util.Log;
public class MyIntentService extends IntentService {
public MyIntentService() {
super("MyIntentService");
}
@Override
protected void onHandleIntent(Intent intent) {
// 执行耗时操作,如网络请求、数据处理等
Log.d("MyIntentService", "Performing time-consuming task");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
通过这种方式,将耗时操作从主线程分离到后台线程,保证了应用的 UI 流畅性和整体性能。同时,也避免了因主线程阻塞导致的 ANR(应用无响应)问题。
九、总结与展望
9.1 总结
本文从源码级别深入分析了 Android 静态广播接收模块。首先介绍了静态广播的基本概念、作用和接收流程,接着详细阐述了静态广播接收器的注册过程,包括 AndroidManifest.xml 文件解析、注册信息保存以及系统对注册信息的管理。在广播匹配机制方面,深入分析了广播意图解析、静态广播接收器过滤条件以及广播匹配的源码实现。对于广播的分发与处理,讲解了 ActivityManagerService
在广播分发中的作用、静态广播接收器处理过程以及线程问题。此外,还探讨了静态广播接收模块与系统启动流程的关联、安全与隐私问题以及性能优化策略。通过对这些内容的分析,我们全面了解了 Android 静态广播接收模块的工作原理和实现细节,这有助于开发者在实际项目中更好地运用静态广播机制,提高应用的开发质量。
9.2 展望
随着 Android 系统的不断发展和安全要求的日益提高,静态广播接收模块可能会在以下几个方面进行改进和发展。在安全性方面,系统可能会加强对静态广播接收器权限和组件导出的管理,引入更严格的权限检查机制和安全审计功能,防止恶意应用滥用静态广播。在性能优化上,未来可能会对广播匹配算法进行优化,进一步提高广播匹配效率,减少资源消耗。同时,随着 Android 系统对隐私保护的重视,静态广播接收模块也可能会在隐私保护方面进行更多的改进,例如限制某些敏感广播的监听范围,保护用户的隐私数据。此外,随着新技术的不断涌现,如 Android Jetpack 等架构组件的发展,静态广播接收模块可能会与这些新技术更好地融合,为开发者提供更便捷、高效的开发方式 。