Android广播之发送广播的源码分析

本文详细剖析了Android广播发送的步骤,从广播发送者到AMS,再到BroadcastQueue和接收者进程,涉及无序和有序广播的调度与处理,揭示了广播发送与接收的异步特性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前面文章介绍了Android注册广播的过程,这篇介绍下广播的发送过程。

广播的发送过程比广播的注册过程复杂的多,主要有以下几个步骤:

1.广播的发送者将一个特定类型的广播发送给ActivityManagerService。

2.AMS接收到这个广播后,首先找到与这个广播对应的广播接收者,然后将它们添加到一个广播调度队列中,再将这个调度队列传递给BroadcastQueue,最后向BroadcastQueue的消息队列发送一个类型为BROADCAST_INTENT_MSG的消息,此时对于广播发送者来说,一个广播的发送就完成了。

3.当消息队列中的BROADCAST_INTENT_MSG消息被处理时,BroadcastQueue就会从广播调度队列中找对需要接收广播的接收者,并且将对应的广播发送给它们所运行在的应用程序进程。

4.广播接收者所运行在的应用程序进程接收到广播后,并不是直接将接收到的广播分发给各个广播接收者来处理,而是将接收到的广播封装成一个消息,并且发送到主线程的消息队列中。当这个消息被处理时,应用程序进程才会将它所描述的广播发送给相应的广播接收者处理。

惯例先看时序图,由于发送广播的过程有点复杂,所以时序图分开画的。sendBroadcast、sendOrderedBroadcast和sendStickyBroadcast方法在调用broadcastIntent方法之前的流程是一样的,这里只画出sendBroadcast方法的时序图:

说明:一个广播是使用一个Intent对象来描述的,而这个Intent对象的action名称就是用来描述它所对应的广播类型。

广播的发送也是从ContextWrapper类开始的:

@Override
public void sendBroadcast(Intent intent) {
    mBase.sendBroadcast(intent);
}


ContextImpl类的sendBroadcast方法:

@Override
public void sendBroadcast(Intent intent) {
    // 如果调用者是系统进程的话打印log
    warnIfCallingFromSystemProcess();
    // Intent的MIME数据类型,没有设置则为null
    String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
    try {
        // 准备离开应用程序进程,进入AMS进程
        intent.prepareToLeaveProcess();
        // 调用ActivityManagerProxy类的broadcastIntent方法来实现广播的发送
        // ActivityManagerProxy是一个Binder对象的远程接口,而这个Binder对象就是ActivityManagerService
        ActivityManagerNative.getDefault().broadcastIntent(
                mMainThread.getApplicationThread(), intent, resolvedType, null,
                Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, false,
                getUserId());
    } catch (RemoteException e) {
        throw new RuntimeException("Failure from system", e);
    }
}


ActivityManagerService类的broadcastIntent方法:

public final int broadcastIntent(IApplicationThread caller,
        Intent intent, String resolvedType, IIntentReceiver resultTo,
        int resultCode, String resultData, Bundle resultExtras,
        String[] requiredPermissions, int appOp, Bundle options,
        boolean serialized, boolean sticky, int userId) {
    // 执行调用者不是独立进程的判断
    enforceNotIsolatedCaller("broadcastIntent");
    synchronized(this) {
        intent = verifyBroadcastLocked(intent);

        // 通过ApplicationThread对象从成员变量mLruProcesses列表中查找调用者的ProcessRecord对象
        final ProcessRecord callerApp = getRecordForAppLocked(caller);
        final int callingPid = Binder.getCallingPid();
        final int callingUid = Binder.getCallingUid();
        final long origId = Binder.clearCallingIdentity();
        // 调用broadcastIntentLocked方法处理 intent 所描述的广播
        int res = broadcastIntentLocked(callerApp,
                callerApp != null ? callerApp.info.packageName : null,
                intent, resolvedType, resultTo, resultCode, resultData, resultExtras,
                requiredPermissions, appOp, null, serialized, sticky,
                callingPid, callingUid, userId);
        Binder.restoreCallingIdentity(origId);
        return res;
    }
}


再看后续时序图:

broadcastIntentLocked方法中主要是用来查找目标广播接收者的:

/**
 * State of all active sticky broadcasts per user.  Keys are the action of the
 * sticky Intent, values are an ArrayList of all broadcasted intents with
 * that action (which should usually be one).  The SparseArray is keyed
 * by the user ID the sticky is for, and can include UserHandle.USER_ALL
 * for stickies that are sent to all users.
 */
final SparseArray<ArrayMap<String, ArrayList<Intent>>> mStickyBroadcasts =
        new SparseArray<ArrayMap<String, ArrayList<Intent>>>();

private final int broadcastIntentLocked(ProcessRecord callerApp,
        String callerPackage, Intent intent, String resolvedType,
        IIntentReceiver resultTo, int resultCode, String resultData,
        Bundle resultExtras, String[] requiredPermissions, int appOp, Bundle options,
        boolean ordered, boolean sticky, int callingPid, int callingUid, int userId) {
    intent = new Intent(intent);

    // By default broadcasts do not go to stopped apps.
    // 设置这个flag后,intent将不会去匹配这个package中当前停止运行的组件。
    intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES);

    // If we have not finished booting, don't allow this to launch new processes.
    // FLAG_RECEIVER_BOOT_UPGRADE标志是广播用于系统升级的,如果设置了该标记,允许系统在启动完成前发送广播
    if (!mProcessesReady && (intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) == 0) {
        // 追加FLAG_RECEIVER_REGISTERED_ONLY标志后,只有动态注册的广播接收者能收到广播
        // 这是因为在系统启动过程中,PackageManagerService可能还未启动,此时AMS是无法获得静态注册的广播接收者的
        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
    }

    if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST,
            (sticky ? "Broadcast sticky: ": "Broadcast: ") + intent
            + " ordered=" + ordered + " userid=" + userId);
    if ((resultTo != null) && !ordered) {
        Slog.w(TAG, "Broadcast " + intent + " not ordered but result callback requested!");
    }

    // 处理调用者uid
    userId = handleIncomingUser(callingPid, callingUid, userId,
            true, ALLOW_NON_FULL, "broadcast", callerPackage);

    // Make sure that the user who is receiving this broadcast is running.
    // If not, we will just skip it. Make an exception for shutdown broadcasts
    // and upgrade steps.

    if (userId != UserHandle.USER_ALL && !isUserRunningLocked(userId, false)) {
        if ((callingUid != Process.SYSTEM_UID
                || (intent.getFlags() & Intent.FLAG_RECEIVER_BOOT_UPGRADE) == 0)
                && !Intent.ACTION_SHUTDOWN.equals(intent.getAction())) {
            Slog.w(TAG, "Skipping broadcast of " + intent
                    + ": user " + userId + " is stopped");
            return ActivityManager.BROADCAST_FAILED_USER_STOPPED;
        }
    }

    BroadcastOptions brOptions = null;
    if (options != null) {
        brOptions = new BroadcastOptions(options);
        if (brOptions.getTemporaryAppWhitelistDuration() > 0) {
            // See if the caller is allowed to do this.  Note we are checking against
            // the actual real caller (not whoever provided the operation as say a
            // PendingIntent), because that who is actually supplied the arguments.
            if (checkComponentPermission(
                    android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST,
                    Binder.getCallingPid(), Binder.getCallingUid(), -1, true)
                    != PackageManager.PERMISSION_GRANTED) {
                String msg = "Permission Denial: " + intent.getAction()
                        + " broadcast from " + callerPackage + " (pid=" + callingPid
                        + ", uid=" + callingUid + ")"
                        + " requires "
                        + android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST;
                Slog.w(TAG, msg);
                throw new SecurityException(msg);
            }
        }
    }

    /*
     * Prevent non-system code (defined here to be non-persistent
     * processes) from sending protected broadcasts.
     * 防止非系统代码(这里定义为非持久性进程)发送受保护的广播
     */
    int callingAppId = UserHandle.getAppId(callingUid);
    if (callingAppId == Process.SYSTEM_UID || callingAppId == Process.PHONE_UID
        || callingAppId == Process.SHELL_UID || callingAppId == Process.BLUETOOTH_UID
        || callingAppId == Process.NFC_UID || callingUid == 0) {
        // Always okay.
    } else if (callerApp == null || !callerApp.persistent) {// 调用者为null或者调用者不是持久性进程
        try {
            // 非系统应用不能发送受保护的广播
            if (AppGlobals.getPackageManager().isProtectedBroadcast(
                    intent.getAction())) {
                String msg = "Permission Denial: not allowed to send broadcast "
                        + intent.getAction() + " from pid="
                        + callingPid + ", uid=" + callingUid;
                Slog.w(TAG, msg);
                throw new SecurityException(msg);
            } else if (AppWidgetManager.ACTION_APPWIDGET_CONFIGURE.equals(intent.getAction())) {
                // Special case for compatibility(兼容性): we don't want apps to send this,
                // but historically it has not been protected and apps may be using it
                // to poke(干涉) their own app widget.  So, instead of making it protected,
                // just limit it to the caller.
                if (callerApp == null) {
                    String msg = "Permission Denial: not allowed to send broadcast "
                            + intent.getAction() + " from unknown caller.";
                    Slog.w(TAG, msg);
                    throw new SecurityException(msg);
                // 接收目标组件不为null
                } else if (intent.getComponent() != null) {
                    // They are good enough to send to an explicit component...  verify
                    // it is being sent to the calling app.
                    if (!intent.getComponent().getPackageName().equals(
                            callerApp.info.packageName)) {
                        String msg = "Permission Denial: not allowed to send broadcast "
                                + intent.getAction() + " to "
                                + intent.getComponent().getPackageName() + " from "
                                + callerApp.info.packageName;
                        Slog.w(TAG, msg);
                        throw new SecurityException(msg);
                    }
                } else {
                    // 发送者不为null,接收者组件为null时,设置接收者只能是发送者
                    // Limit broadcast to their own package.
                    intent.setPackage(callerApp.info.packageName);
                }
            }
        } catch (RemoteException e) {
            Slog.w(TAG, "Remote exception", e);
            return ActivityManager.BROADCAST_SUCCESS;
        }
    }

    final String action = intent.getAction();
    if (action != null) {
        // 特殊的Action有不同的处理方式
        switch (action) {
            case Intent.ACTION_UID_REMOVED:
            case Intent.ACTION_PACKAGE_REMOVED:
            case Intent.ACTION_PACKAGE_CHANGED:
            case Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE:
            case Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE:
                // Handle special intents: if this broadcast is from the package
                // manager about a package being removed, we need to remove all of
                // its activities from the history stack.
                if (checkComponentPermission(
                        android.Manifest.permission.BROADCAST_PACKAGE_REMOVED,
                        callingPid, callingUid, -1, true)
                        != PackageManager.PERMISSION_GRANTED) {
                    String msg = "Permission Denial: " + intent.getAction()
                            + " broadcast from " + callerPackage + " (pid=" + callingPid
                            + ", uid=" + callingUid + ")"
                            + " requires "
                            + android.Manifest.permission.BROADCAST_PACKAGE_REMOVED;
                    Slog.w(TAG, msg);
                    throw new SecurityException(msg);
                }
                switch (action) {
                    case Intent.ACTION_UID_REMOVED:
                        final Bundle intentExtras = intent.getExtras();
                        final int uid = intentExtras != null
                                ? intentExtras.getInt(Intent.EXTRA_UID) : -1;
                        if (uid >= 0) {
                            mBatteryStatsService.removeUid(uid);
                            mAppOpsService.uidRemoved(uid);
                        }
                        break;
                    // app正在移动到SD卡中,发出的广播
                    case Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE:
                        // If resources are unavailable just force stop all those packages
                        // and flush the attribute cache as well.
                        String list[] =
                                intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
                        if (list != null && list.length > 0) {
                            for (int i = 0; i < list.length; i++) {
                                forceStopPackageLocked(list[i], -1, false, true, true,
                                        false, false, userId, "storage unmount");
                            }
                            // 清空app的任务栈
                            mRecentTasks.cleanupLocked(UserHandle.USER_ALL);
                            // 发送app不可用的广播
                            sendPackageBroadcastLocked(
                                    IApplicationThread.EXTERNAL_STORAGE_UNAVAILABLE, list,
                                    userId);
                        }
                        break;
                    // app完成移动到SD的操作,发出的广播
                    case Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE:
                        mRecentTasks.cleanupLocked(UserHandle.USER_ALL);
                        break;
                    case Intent.ACTION_PACKAGE_REMOVED:
                    case Intent.ACTION_PACKAGE_CHANGED:
                        Uri data = intent.getData();
                        String ssp;
                        if (data != null && (ssp=data.getSchemeSpecificPart()) != null) {
                            boolean removed = Intent.ACTION_PACKAGE_REMOVED.equals(action);
                            boolean fullUninstall = removed &&
                                    !intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
                            final boolean killProcess =
                                    !intent.getBooleanExtra(Intent.EXTRA_DONT_KILL_APP, false);
                            if (killProcess) {
                                forceStopPackageLocked(ssp, UserHandle.getAppId(
                                        intent.getIntExtra(Intent.EXTRA_UID, -1)),
                                        false, true, true, false, fullUninstall, userId,
                                        removed ? "pkg removed" : "pkg changed");
                            }
                            if (removed) {
                                sendPackageBroadcastLocked(IApplicationThread.PACKAGE_REMOVED,
                                        new String[] {ssp}, userId);
                                if (fullUninstall) {
                                    mAppOpsService.packageRemoved(
                                            intent.getIntExtra(Intent.EXTRA_UID, -1), ssp);

                                    // Remove all permissions granted from/to this package
                                    removeUriPermissionsForPackageLocked(ssp, userId, true);

                                    removeTasksByPackageNameLocked(ssp, userId);
                                    mBatteryStatsService.notePackageUninstalled(ssp);
                                }
                            } else {
                                cleanupDisabledPackageComponentsLocked(ssp, userId, killProcess,
                                        intent.getStringArrayExtra(
                                                Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST));
                            }
                        }
                        break;
                }
                break;
            case Intent.ACTION_PACKAGE_ADDED:
                // Special case for adding a package: by default turn on compatibility mode.
                Uri data = intent.getData();
                String ssp;
                if (data != null && (ssp = data.getSchemeSpecificPart()) != null) {
                    final boolean replacing =
                            intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
                    mCompatModePackages.handlePackageAddedLocked(ssp, replacing);

                    try {
                        ApplicationInfo ai = AppGlobals.getPackageManager().
                                getApplicationInfo(ssp, 0, 0);
                        mBatteryStatsService.notePackageInstalled(ssp,
                         
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值