深入剖析 Android 广播发送模块
本人掘金号,欢迎点击关注:掘金号地址
本人公众号,欢迎点击关注:公众号地址
一、引言
在 Android 开发中,广播机制是一种非常重要的组件间通信方式。广播发送模块作为广播机制的关键部分,承担着将消息传递给其他组件的重要任务。通过广播,我们可以实现系统与应用之间、应用内部组件之间的高效通信。深入理解广播发送模块的源码和工作原理,对于开发高质量的 Android 应用至关重要。本文将从源码级别深入分析 Android 广播发送模块,带你了解其内部实现细节。
二、广播发送模块概述
2.1 广播的基本概念
广播是 Android 系统中一种广泛使用的组件间通信机制,它允许一个组件向其他组件发送消息。广播可以分为系统广播和自定义广播。系统广播是由 Android 系统发出的,例如屏幕点亮、电量变化等;自定义广播则是由开发者自己定义和发送的。
2.2 广播发送模块的作用
广播发送模块的主要作用是将广播消息发送到系统中,系统会根据广播的类型和注册的接收器,将消息分发给相应的接收器进行处理。广播发送模块可以控制广播的发送方式、优先级等参数,从而实现不同的通信需求。
2.3 广播发送的基本流程
广播发送的基本流程包括创建广播意图(Intent)、设置广播的相关参数、调用发送广播的方法等。以下是一个简单的广播发送示例:
java
// 创建一个广播意图,指定广播的动作
Intent intent = new Intent("com.example.MY_BROADCAST");
// 发送广播
sendBroadcast(intent);
三、广播意图(Intent)的创建
3.1 Intent 的基本概念
Intent 是 Android 中用于在组件之间传递数据和请求的对象。在广播发送中,Intent 用于封装广播的相关信息,如广播的动作、数据等。
3.2 创建 Intent 的方法
可以通过多种方式创建 Intent 对象,以下是一些常见的创建方法:
java
// 方式一:通过构造函数创建,指定广播的动作
Intent intent1 = new Intent("com.example.MY_BROADCAST");
// 方式二:先创建一个空的 Intent 对象,再设置动作
Intent intent2 = new Intent();
intent2.setAction("com.example.MY_BROADCAST");
// 方式三:通过隐式 Intent 创建,指定要启动的组件的包名和类名
Intent intent3 = new Intent();
intent3.setComponent(new ComponentName("com.example.app", "com.example.app.MyReceiver"));
intent3.setAction("com.example.MY_BROADCAST");
3.3 设置 Intent 的参数
Intent 可以携带各种参数,如额外的数据、类别等。以下是设置 Intent 参数的示例:
java
Intent intent = new Intent("com.example.MY_BROADCAST");
// 设置额外的数据
intent.putExtra("key", "value");
// 设置类别
intent.addCategory(Intent.CATEGORY_DEFAULT);
四、广播发送的方法
4.1 sendBroadcast 方法
sendBroadcast 方法用于发送普通广播,普通广播是一种异步广播,所有注册的接收器都会同时接收到广播消息。以下是 sendBroadcast 方法的源码分析:
java
/**
* 发送普通广播
* @param intent 广播意图
*/
public void sendBroadcast(Intent intent) {
// 检查是否有发送广播的权限
mBase.sendBroadcast(intent);
}
在 ContextWrapper 类中,sendBroadcast 方法会调用 mBase 的 sendBroadcast 方法,mBase 是一个 Context 对象,具体的实现会在 ContextImpl 类中。
java
@Override
public void sendBroadcast(Intent intent) {
warnIfCallingFromSystemProcess();
// 调用 ActivityManagerNative.getDefault() 获取 ActivityManagerService 的代理对象
ActivityManager.getService().broadcastIntent(
mMainThread.getApplicationThread(), intent, null,
null, 0, null, null, null, AppOpsManager.OP_NONE, null, false, false,
getUserId());
}
在 ContextImpl 类中,sendBroadcast 方法会调用 ActivityManager.getService().broadcastIntent 方法,将广播意图发送给 ActivityManagerService 进行处理。
4.2 sendOrderedBroadcast 方法
sendOrderedBroadcast 方法用于发送有序广播,有序广播是一种同步广播,所有注册的接收器会按照优先级依次接收到广播消息。以下是 sendOrderedBroadcast 方法的源码分析:
java
/**
* 发送有序广播
* @param intent 广播意图
* @param receiverPermission 接收器的权限
*/
public void sendOrderedBroadcast(Intent intent, String receiverPermission) {
// 检查是否有发送广播的权限
mBase.sendOrderedBroadcast(intent, receiverPermission);
}
同样,ContextWrapper 类中的 sendOrderedBroadcast 方法会调用 mBase 的 sendOrderedBroadcast 方法,具体实现会在 ContextImpl 类中。
java
@Override
public void sendOrderedBroadcast(Intent intent, String receiverPermission) {
warnIfCallingFromSystemProcess();
// 调用 ActivityManagerNative.getDefault() 获取 ActivityManagerService 的代理对象
ActivityManager.getService().broadcastIntent(
mMainThread.getApplicationThread(), intent, null,
null, ActivityManager.BROADCAST_ORDERED, null, null, null,
AppOpsManager.OP_NONE, null, false, false,
getUserId());
}
在 ContextImpl 类中,sendOrderedBroadcast 方法会调用 ActivityManager.getService().broadcastIntent 方法,与 sendBroadcast 方法不同的是,这里会设置 ActivityManager.BROADCAST_ORDERED 标志,表示发送的是有序广播。
4.3 sendStickyBroadcast 方法
sendStickyBroadcast 方法用于发送粘性广播,粘性广播会在发送后一直保留在系统中,新注册的接收器可以接收到之前发送的粘性广播。不过,从 Android 5.0(API 级别 21)开始,sendStickyBroadcast 方法已被弃用,因为它存在安全风险。以下是 sendStickyBroadcast 方法的源码分析:
java
/**
* 发送粘性广播
* @param intent 广播意图
*/
@Deprecated
public void sendStickyBroadcast(Intent intent) {
// 检查是否有发送粘性广播的权限
mBase.sendStickyBroadcast(intent);
}
ContextWrapper 类中的 sendStickyBroadcast 方法会调用 mBase 的 sendStickyBroadcast 方法,具体实现会在 ContextImpl 类中。
java
@Override
@Deprecated
public void sendStickyBroadcast(Intent intent) {
warnIfCallingFromSystemProcess();
// 调用 ActivityManagerNative.getDefault() 获取 ActivityManagerService 的代理对象
ActivityManager.getService().broadcastStickyIntent(
mMainThread.getApplicationThread(), intent, null,
AppOpsManager.OP_NONE, getUserId());
}
在 ContextImpl 类中,sendStickyBroadcast 方法会调用 ActivityManager.getService().broadcastStickyIntent 方法,将广播意图发送给 ActivityManagerService 进行处理。
五、广播发送的权限检查
5.1 权限检查的作用
在发送广播时,系统会进行权限检查,确保发送者有足够的权限发送该广播。权限检查可以提高系统的安全性,防止恶意应用发送非法广播。
5.2 权限检查的源码分析
在 ContextImpl 类的 sendBroadcast 方法中,会调用 warnIfCallingFromSystemProcess 方法进行权限检查:
java
private void warnIfCallingFromSystemProcess() {
if (mPackageInfo != null && mPackageInfo.getApplicationInfo().flags
& ApplicationInfo.FLAG_SYSTEM) {
Log.w(TAG, "Sending broadcast from system "
+ "process requires android.permission.BROADCAST_STICKY permission");
}
}
该方法会检查当前应用是否是系统应用,如果是系统应用,会提示需要 android.permission.BROADCAST_STICKY 权限。
在 ActivityManagerService 的 broadcastIntent 方法中,也会进行权限检查:
java
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;
}
checkBroadcastFromSystem 方法会检查发送者的权限,如果权限不足,会返回相应的错误码。
六、广播发送到 ActivityManagerService 的处理
6.1 ActivityManagerService 的作用
ActivityManagerService(AMS)是 Android 系统中负责管理活动、服务、广播等组件的核心服务。广播发送模块会将广播意图发送给 AMS,由 AMS 进行广播的分发和处理。
6.2 广播意图到达 AMS 后的处理流程
当广播意图到达 AMS 后,AMS 会进行一系列的处理,包括查找匹配的接收器、排序接收器、发送广播等。以下是 ActivityManagerService 的 broadcastIntent 方法的部分源码分析:
java
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);
final int callingPid = Binder.getCallingPid();
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 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) {
// Query one target user at a time, excluding shell-restricted users
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 方法会进行权限检查、查找匹配的接收器、合并静态接收器和动态接收器、处理粘性广播、排序接收器、创建广播队列等操作,最后将广播记录加入到广播队列中进行处理。
七、广播队列的处理
7.1 广播队列的作用
广播队列用于管理待处理的广播记录,AMS 会将广播记录加入到广播队列中,按照一定的规则进行处理。广播队列可以分为并行广播队列和有序广播队列。
7.2 并行广播队列的处理
并行广播队列用于处理普通广播,所有注册的接收器会同时接收到广播消息。以下是并行广播队列的部分源码分析:
java
/**
* 并行广播队列
*/
final BroadcastQueue mParallelBroadcastQueue;
/**
* 将广播记录加入到并行广播队列中
* @param r 广播记录
*/
public void enqueueParallelBroadcastLocked(BroadcastRecord r) {
mParallelBroadcasts.add(r);
r.enqueueClockTime = SystemClock.uptimeMillis();
r.dispatchClockTime = 0;
}
/**
* 调度并行广播的处理
*/
public void scheduleBroadcastsLocked() {
if (mBroadcastsScheduled) {
return;
}
mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, this));
mBroadcastsScheduled = true;
}
enqueueParallelBroadcastLocked 方法会将广播记录加入到 mParallelBroadcasts 列表中,scheduleBroadcastsLocked 方法会发送一个消息给 mHandler,由 mHandler 来处理广播。
7.3 有序广播队列的处理
有序广播队列用于处理有序广播,所有注册的接收器会按照优先级依次接收到广播消息。以下是有序广播队列的部分源码分析:
java
/**
* 有序广播队列
*/
final BroadcastQueue mOrderedBroadcastQueue;
/**
* 将广播记录加入到有序广播队列中
* @param r 广播记录
*/
public void enqueueOrderedBroadcastLocked(BroadcastRecord r) {
mOrderedBroadcasts.add(r);
r.enqueueClockTime = SystemClock.uptimeMillis();
r.dispatchClockTime = 0;
}
/**
* 调度有序广播的处理
*/
public void scheduleBroadcastsLocked() {
if (mBroadcastsScheduled) {
return;
}
mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, this));
mBroadcastsScheduled = true;
}
enqueueOrderedBroadcastLocked 方法会将广播记录加入到 mOrderedBroadcasts 列表中,scheduleBroadcastsLocked 方法会发送一个消息给 mHandler,由 mHandler 来处理广播。
八、广播接收器的查找与匹配
8.1 静态接收器的查找
静态接收器是在 AndroidManifest.xml 文件中注册的接收器。在广播发送时,AMS 会根据广播意图的动作、类别等信息,查找匹配的静态接收器。以下是查找静态接收器的部分源码分析:
java
/**
* 查找匹配的静态接收器
* @param intent 广播意图
* @param resolvedType 解析后的类型
* @param callingUid 调用者的 UID
* @param users 用户 ID
* @return 匹配的静态接收器列表
*/
private List<ResolveInfo> collectReceiverComponents(Intent intent, String resolvedType,
int callingUid, int[] users) {
List<ResolveInfo> receivers = null;
for (int i = 0; i < users.length; i++) {
List<ResolveInfo> r = AppGlobals.getPackageManager().queryBroadcastReceivers(
intent, resolvedType, PackageManager.GET_RESOLVED_FILTER, users[i]);
if (r != null) {
if (receivers == null) {
receivers = new ArrayList<ResolveInfo>();
}
receivers.addAll(r);
}
}
return receivers;
}
collectReceiverComponents 方法会调用 AppGlobals.getPackageManager().queryBroadcastReceivers 方法,根据广播意图查询匹配的静态接收器。
8.2 动态接收器的查找
动态接收器是在代码中通过 registerReceiver 方法注册的接收器。在广播发送时,AMS 会根据广播意图的动作、类别等信息,查找匹配的动态接收器。以下是查找动态接收器的部分源码分析:
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)) {
results.add(filter);
}
}
}
return results;
}
queryIntent 方法会遍历 mFilters 列表,检查每个 BroadcastFilter 是否匹配广播意图,如果匹配则将其加入到结果列表中。
8.3 接收器的匹配规则
接收器的匹配规则主要根据广播意图的动作、类别、数据等信息进行匹配。以下是 BroadcastFilter 的 matchIntent 方法的部分源码分析:
java
/**
* 检查广播意图是否匹配该过滤器
* @param intent 广播意图
* @param resolvedType 解析后的类型
* @return 是否匹配
*/
public boolean matchIntent(Intent intent, String resolvedType) {
// 检查动作是否匹配
String action = intent.getAction();
if (action == null) {
if (mActions.size() > 0) {
return false;
}
} else {
if (!mActions.contains(action)) {
return false;
}
}
// 检查类别是否匹配
String category = intent.getCategory();
if (category != null) {
if (!mCategories.contains(category)) {
return false;
}
}
// 检查数据是否匹配
Uri data = intent.getData();
if (data != null) {
if (!matchData(data, resolvedType)) {
return false;
}
}
return true;
}
matchIntent 方法会依次检查广播意图的动作、类别、数据是否与过滤器匹配,如果都匹配则返回 true,否则返回 false。
九、广播的分发与处理
9.1 广播的分发过程
当广播记录加入到广播队列后,mHandler 会处理广播。对于并行广播,会同时向所有匹配的接收器发送广播消息;对于有序广播,会按照接收器的优先级依次发送广播消息。以下是 BroadcastQueue 的 processNextBroadcast 方法的部分源码分析:
java
/**
* 处理下一个广播
* @param fromMsg 是否从消息处理
*/
final void processNextBroadcast(boolean fromMsg) {
synchronized (mService) {
// 处理并行广播
while (mParallelBroadcasts.size() > 0) {
BroadcastRecord r = mParallelBroadcasts.remove(0);
r.dispatchClockTime = SystemClock.uptimeMillis();
final int N = r.receivers.size();
for (int i = 0; i < N; i++) {
Object target = r.receivers.get(i);
if (target instanceof BroadcastFilter) {
BroadcastFilter filter = (BroadcastFilter) target;
try {
// 调用接收器的 onReceive 方法
filter.receiver.asBinder().transact(
IntentReceiverStub.ON_RECEIVE_TRANSACTION,
r.intent, null, 0);
} catch (RemoteException e) {
Slog.w(TAG, "RemoteException when sending broadcast to "
+ filter.receiver.asBinder());
}
}
}
}
// 处理有序广播
if (mOrderedBroadcasts.size() > 0) {
BroadcastRecord r = mOrderedBroadcasts.get(0);
r.dispatchClockTime = SystemClock.uptimeMillis();
Object target = r.receivers.get(r.nextReceiver++);
if (target instanceof BroadcastFilter) {
BroadcastFilter filter = (BroadcastFilter) target;
try {
// 调用接收器的 onReceive 方法
filter.receiver.asBinder().transact(
IntentReceiverStub.ON_RECEIVE_TRANSACTION,
r.intent, null, 0);
} catch (RemoteException e) {
Slog.w(TAG, "RemoteException when sending broadcast to "
+ filter.receiver.asBinder());
}
}
if (r.nextReceiver >= r.receivers.size()) {
mOrderedBroadcasts.remove(0);
}
}
}
}
processNextBroadcast 方法会先处理并行广播队列中的广播记录,将广播消息发送给所有匹配的接收器;然后处理有序广播队列中的广播记录,按照接收器的优先级依次发送广播消息。
9.2 接收器的处理过程
当接收器接收到广播消息后,会调用其 onReceive 方法进行处理。以下是一个简单的接收器示例:
java
// 自定义广播接收器
public class MyReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// 处理广播消息
String action = intent.getAction();
if ("com.example.MY_BROADCAST".equals(action)) {
// 处理自定义广播
String data = intent.getStringExtra("key");
Log.d("MyReceiver", "Received broadcast: " + data);
}
}
}
在 onReceive 方法中,可以根据广播意图的动作、数据等信息进行相应的处理。
十、广播发送的性能优化
10.1 减少广播的发送频率
频繁发送广播会消耗系统资源,影响应用的性能。因此,应尽量减少广播的发送频率,只在必要时发送广播。例如,在数据发生变化时才发送广播,而不是在每次数据更新时都发送广播。
10.2 优化广播的内容
广播的内容应尽量简洁,避免携带大量的数据。如果需要传递大量的数据,可以考虑使用其他方式,如文件、数据库等。例如,在广播中只传递数据的 ID,然后在接收器中根据 ID 从数据库中获取具体的数据。
10.3 合理设置广播的优先级
对于有序广播,合理设置接收器的优先级可以提高广播的处理效率。将重要的接收器设置较高的优先级,确保其能优先接收到广播消息。
十一、总结与展望
11.1 总结
通过对 Android 广播发送模块的深入分析,我们了解了广播发送的基本流程、广播意图的创建、广播发送的方法、权限检查、ActivityManagerService 的处理、广播队列的处理、接收器的查找与匹配、广播的分发与处理等核心内容。广播发送模块是 Android 广播机制的重要组成部分,它负责将广播消息发送到系统中,由系统进行分发和处理。深入理解广播发送模块的源码和工作原理,对于开发高质量的 Android 应用至关重要。
11.2 展望
随着 Android 系统的不断发展,广播机制也在不断完善和优化。未来,广播发送模块可能会在性能、安全性等方面进行进一步的改进。例如,引入更高效的广播分发算法,提高广播的处理速度;加强广播的权限管理,提高系统的安全性。同时,开发者也可以根据实际需求,结合广播机制开发出更加丰富和实用的应用功能。