深入剖析 Android 广播发送模块(一)

236 阅读13分钟

深入剖析 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 系统的不断发展,广播机制也在不断完善和优化。未来,广播发送模块可能会在性能、安全性等方面进行进一步的改进。例如,引入更高效的广播分发算法,提高广播的处理速度;加强广播的权限管理,提高系统的安全性。同时,开发者也可以根据实际需求,结合广播机制开发出更加丰富和实用的应用功能。