Android 广播内部机制详解(二)

3. 广播的发送

广播的发送,其实也是交给AMS来完成的,首先调用context.sendBroadcast将广播发给AMS的相应函数,AMS再和之前注册的Receiver进行匹配,匹配成功后,就发送给对应的进程。好了,接下来我们通过源码来论证这个结论。

3.1 sendBroadcast

文件:ContextImpl.java

    @Override
    public void sendBroadcast(Intent intent, String receiverPermission) {

        ......

        try {
            //见3.1.1 
            ActivityManagerNative.getDefault().broadcastIntent(
                    mMainThread.getApplicationThread(), intent, resolvedType, null,
                    Activity.RESULT_OK, null, null, receiverPermissions, AppOpsManager.OP_NONE,
                    null, false, false, getUserId());
        }

        ......

    }

很明显,这个是调用了AMS中的broadcastIntent方法

3.1.1 broadcastIntent

文件:ActivityManagerService.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是否能够发送
            intent = verifyBroadcastLocked(intent);

            final ProcessRecord callerApp = getRecordForAppLocked(caller);
            final int callingPid = Binder.getCallingPid();
            final int callingUid = Binder.getCallingUid();
            final long origId = Binder.clearCallingIdentity();
            // 见3.2
            int res = broadcastIntentLocked(callerApp,
                    callerApp != null ? callerApp.info.packageName : null,
                    intent, resolvedType, resultTo, resultCode, resultData, resultExtras,
                    requiredPermissions, appOp, bOptions, serialized, sticky,
                    callingPid, callingUid, userId);
            Binder.restoreCallingIdentity(origId);
            return res;
        }
    }

3.2 broadcastIntentLocked

文件:ActivityManagerService.java
这个方法非常的长,我们分成几个部分来讲:

3.2.1 设置广播flag

        intent = new Intent(intent);
        //设置FLAG_EXCLUDE_STOPPED_PACKAGES
        intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES);

        //没有启动完毕不允许启动新的进程
        if (!mProcessesReady && (intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) == 0) {
            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
        }
        // 检查发送广播时用户的状态
        userId = mUserController.handleIncomingUser(callingPid, callingUid, userId, true,
                ALLOW_NON_FULL, "broadcast", callerPackage);

        if (userId != UserHandle.USER_ALL && !mUserController.isUserRunningLocked(userId, 0)) {
            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;
            }
        }

主要做了三件事:

  • 所有广播默认会添加这个标志,不会发送广播给stop状态的app。stop状态是指下载后重没有使用过的应用,或者被用户强制停止的应用。不过可以通过让intent携带FLAG_INCLUDE_STOPPED_PACKAGES来framework的设置

  • 没有启动完毕不允许启动新的进程,也就是此时只允许发送动态注册的广播

  • 如果不是 UserHandle.USER_ALL广播,且当前用户不是处于Running状态,这时只能响应系统升级和关机广播

3.2.2 受保护广播的处理

if (!isCallerSystem) {
            if (isProtectedBroadcast) {
                String msg = "Permission Denial: not allowed to send broadcast "
                        + action + " from pid="
                        + callingPid + ", uid=" + callingUid;
                Slog.w(TAG, msg);
                throw new SecurityException(msg);

            } else if (AppWidgetManager.ACTION_APPWIDGET_CONFIGURE.equals(action)
                    || AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action)) {
                if (callerPackage == null) {
                    String msg = "Permission Denial: not allowed to send broadcast "
                            + action + " from unknown caller.";
                    Slog.w(TAG, msg);
                    throw new SecurityException(msg);
                } else if (intent.getComponent() != null) {

                    if (!intent.getComponent().getPackageName().equals(
                            callerPackage)) {
                        String msg = "Permission Denial: not allowed to send broadcast "
                                + action + " to "
                                + intent.getComponent().getPackageName() + " from "
                                + callerPackage;
                        Slog.w(TAG, msg);
                        throw new SecurityException(msg);
                    }
                } else {
                    // Limit broadcast to their own package.
                    intent.setPackage(callerPackage);
                }
            }
        }

只允许系统发送手保护的广播(isProtectedBroadcast为true);非系统发送ACTION_APPWIDGET_CONFIGURE或者ACTION_APPWIDGET_UPDATE广播,只能发送给自己本身

3.2.3 处理特定的系统广播

if (action != null) {
            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:
                case Intent.ACTION_PACKAGES_SUSPENDED:
                case Intent.ACTION_PACKAGES_UNSUSPENDED:

处理一些特殊的广播,对于一些package manager的变化,会通知AMS对特定应用进行处理

3.2.4 处理sticky广播

if (sticky) {
            if (checkPermission(android.Manifest.permission.BROADCAST_STICKY,
                    callingPid, callingUid)
                    != PackageManager.PERMISSION_GRANTED) {
                // 要在AndroidManifest.xml里面声明android.permission.BROADCAST_STICKY权限
                String msg = "Permission Denial: broadcastIntent() requesting a sticky broadcast from pid="
                        + callingPid + ", uid=" + callingUid
                        + " requires " + android.Manifest.permission.BROADCAST_STICKY;
                Slog.w(TAG, msg);
                throw new SecurityException(msg);
            }
            if (requiredPermissions != null && requiredPermissions.length > 0) {
                // 不能设置接收权限
                Slog.w(TAG, "Can't broadcast sticky intent " + intent
                        + " and enforce permissions " + Arrays.toString(requiredPermissions));
                return ActivityManager.BROADCAST_STICKY_CANT_HAVE_PERMISSION;
            }
            if (intent.getComponent() != null) {
                // 不能指定具体接收者
                throw new SecurityException(
                        "Sticky broadcasts can't target a specific component");
            }
            if (userId != UserHandle.USER_ALL) {
                // 如果广播不是发送给USER_ALL的,则该广播不能和USER_ALL的广播冲突
                ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(
                        UserHandle.USER_ALL);
                if (stickies != null) {
                    ArrayList<Intent> list = stickies.get(intent.getAction());
                    if (list != null) {
                        int N = list.size();
                        int i;
                        for (i=0; i<N; i++) {
                            if (intent.filterEquals(list.get(i))) {
                                throw new IllegalArgumentException(
                                        "Sticky broadcast " + intent + " for user "
                                        + userId + " conflicts with existing global broadcast");
                            }
                        }
                    }
                }
            }

这部分主要是说:
- 发送sticky广播需要在AndroidManifest.xml里面声明权限

  • 发送sticky广播不能设置发送权限和具体接收对象

  • 如果不是发送给USER_ALL,则该广播不能和USER_ALL里已有的广播冲突

ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(userId);
            if (stickies == null) {
                stickies = new ArrayMap<>();
                mStickyBroadcasts.put(userId, stickies);
            }
            ArrayList<Intent> list = stickies.get(intent.getAction());
            if (list == null) {
                list = new ArrayList<>();
                stickies.put(intent.getAction(), list);
            }
            final int stickiesCount = list.size();
            int i;
            for (i = 0; i < stickiesCount; i++) {
                if (intent.filterEquals(list.get(i))) {
                    // 替换掉已经存在的sticky广播
                    list.set(i, new Intent(intent));
                    break;
                }
            }
            if (i >= stickiesCount) {
                list.add(new Intent(intent));
            }

这部分主要是:

  • 通过userId作为key取出mStickyBroadcasts里面保存的所有Sticky广播

  • 如果该sticky广播和mStickyBroadcasts着哦功能保存的一致则替换,不一致则添加到末尾

3.2.5 指明接收用户

        int[] users;
        if (userId == UserHandle.USER_ALL) {
            // 所有当前已经启动的用户
            users = mUserController.getStartedUserArrayLocked();
        } else {
            // 发送给userId这个用户
            users = new int[] {userId};
        }

3.2.6 指明receivers和registeredReceivers内容

其中receivers存储的是静态注册的接收者;registeredReceivers是动态注册的接收者

        List receivers = null;
        List<BroadcastFilter> registeredReceivers = null;
        // 没有指定FLAG_RECEIVER_REGISTERED_ONLY标记,允许静态注册
        if ((intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY)
                 == 0) {
            // 查询所有满足条件的静态注册的接收者,见3.2.6.1
            receivers = collectReceiverComponents(intent, resolvedType, callingUid, users);
        }
        if (intent.getComponent() == null) {
            if (userId == UserHandle.USER_ALL && callingUid == Process.SHELL_UID) {
                for (int i = 0; i < users.length; i++) {
                    // shell用户是否开启允许debug功能
                    if (mUserController.hasUserRestriction(
                            UserManager.DISALLOW_DEBUGGING_FEATURES, users[i])) {
                        continue;
                    }
                    List<BroadcastFilter> registeredReceiversForUser =
                            mReceiverResolver.queryIntent(intent,
                                    resolvedType, false, users[i]);
                    // 查询动态注册的广播
                    if (registeredReceivers == null) {
                        registeredReceivers = registeredReceiversForUser;
                    } else if (registeredReceiversForUser != null) {
                        registeredReceivers.addAll(registeredReceiversForUser);
                    }
                }
            } else {
                // 查询动态注册的广播
                registeredReceivers = mReceiverResolver.queryIntent(intent,
                        resolvedType, false, userId);
            }
        }
        // 是否可以用新的Intent替换旧的Intent
        final boolean replacePending =
                (intent.getFlags()&Intent.FLAG_RECEIVER_REPLACE_PENDING) != 0;

        // 动态注册广播的数量
        int NR = registeredReceivers != null ? registeredReceivers.size() : 0;
        // 处理并行广播
        if (!ordered && NR > 0) {
            if (isCallerSystem) {
                checkBroadcastFromSystem(intent, callerApp, callerPackage, callingUid,
                        isProtectedBroadcast, registeredReceivers);
            }
            // 找到合适的BroadcastQueue ,见3.2.6.2
            final BroadcastQueue queue = broadcastQueueForIntent(intent);
            BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
                    callerPackage, callingPid, callingUid, resolvedType, requiredPermissions,
                    appOp, brOptions, registeredReceivers, resultTo, resultCode, resultData,
                    resultExtras, ordered, sticky, false, userId);
            // queue中等待发送的BroadcastRecord是否有和r一致的
            final boolean replaced = replacePending && queue.replaceParallelBroadcastLocked(r);
            if (!replaced) {
                // 将r放到BroadcastQueue.mParallelBroadcasts中
                queue.enqueueParallelBroadcastLocked(r);
                // 处理广播
                queue.scheduleBroadcastsLocked();
            }
            registeredReceivers = null;
            NR = 0;
        }

这部分主要将动态注册的广播接收器和静态注册的广播接收器都找出来,分别放入registeredReceivers和receivers中,然后开始处理非排序的动态广播

3.2.6.1 collectReceiverComponents

文件:ActivityManagerService.java

private List<ResolveInfo> collectReceiverComponents(Intent intent, String resolvedType,
            int callingUid, int[] users) {

            ......

            List<ResolveInfo> newReceivers = AppGlobals.getPackageManager()
                        .queryIntentReceivers(intent, resolvedType, pmFlags, user).getList();

            ......

这个函数最重要的是getPackageManager,PKMS调用queryIntentReceivers,把AndroidManifest.xml里所有的接收者信息都提取出来

3.2.6.2 broadcastQueueForIntent

文件:ActivityManagerService.java

    BroadcastQueue broadcastQueueForIntent(Intent intent) {
        final boolean isFg = (intent.getFlags() & Intent.FLAG_RECEIVER_FOREGROUND) != 0;
        return (isFg) ? mFgBroadcastQueue : mBgBroadcastQueue;
    }

如果Intent中的flag设置FLAG_RECEIVER_FOREGROUND则位前台广播,否则为后台广播

3.2.7 处理有序广播

        int ir = 0;
        if (receivers != null) {
            String skipPackages[] = null;
            // 不允许自己本身接收自己的ACTION_PACKAGE_ADDED,Intent.ACTION_PACKAGE_RESTARTED和
            // Intent.ACTION_PACKAGE_DATA_CLEARED广播
            if (Intent.ACTION_PACKAGE_ADDED.equals(intent.getAction())
                    || Intent.ACTION_PACKAGE_RESTARTED.equals(intent.getAction())
                    || Intent.ACTION_PACKAGE_DATA_CLEARED.equals(intent.getAction())) {
                Uri data = intent.getData();
                if (data != null) {
                    String pkgName = data.getSchemeSpecificPart();
                    if (pkgName != null) {
                        skipPackages = new String[] { pkgName };
                    }
                }
            } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(intent.getAction())) {
                skipPackages = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
            }
            // 将静态接收者中将当前被add的应用排除掉
            if (skipPackages != null && (skipPackages.length > 0)) {
                for (String skipPackage : skipPackages) {
                    if (skipPackage != null) {
                        int NT = receivers.size();
                        for (int it=0; it<NT; it++) {
                            ResolveInfo curt = (ResolveInfo)receivers.get(it);
                            if (curt.activityInfo.packageName.equals(skipPackage)) {
                                receivers.remove(it);
                                it--;
                                NT--;
                            }
                        }
                    }
                }
            }

不允许应用接收ACTION_PACKAGE_ADDED,Intent.ACTION_PACKAGE_RESTARTED和Intent.ACTION_PACKAGE_DATA_CLEARED广播,就算是静态注册了也不行,需要在receivers中将他们删掉

            int NT = receivers != null ? receivers.size() : 0;
            int it = 0;
            ResolveInfo curt = null;
            BroadcastFilter curr = null;
            while (it < NT && ir < NR) {
                if (curt == null) {
                    curt = (ResolveInfo)receivers.get(it);
                }
                if (curr == null) {
                    curr = registeredReceivers.get(ir);
                }
                if (curr.getPriority() >= curt.priority) {
                    // Insert this broadcast record into the final list.
                    receivers.add(it, curr);
                    ir++;
                    curr = null;
                    it++;
                    NT++;
                } else {
                    // Skip to the next ResolveInfo in the final list.
                    it++;
                    curt = null;
                }
            }
        }
        while (ir < NR) {
            if (receivers == null) {
                receivers = new ArrayList();
            }
            receivers.add(registeredReceivers.get(ir));
            ir++;
        }

将有序的动态注册接收者和静态接收者按照优先级合并到receivers里;合并以后,在receivers链表里,静态接收者对应的是ResolveInfo对象,动态接收者对应的是BroadcastFilter对象

        if ((receivers != null && receivers.size() > 0)
                || resultTo != null) {
            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);

            boolean replaced = replacePending && queue.replaceOrderedBroadcastLocked(r);
            if (!replaced) {
                queue.enqueueOrderedBroadcastLocked(r);
                //处理广播,见4.1
                queue.scheduleBroadcastsLocked();
            }
        } 

如果receivers有接收器,则进行串行广播的发送

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值