跨进程广播的工作原理(3)

通过广播Intent来查找对应广播接收者的具体实现

上一篇我们粗略的走完了一遍广播发送的主流程,但一些详细的具体实现没有仔细研读,所以接下来我们要一点一点的给补回去。这个篇章我们主要研究广播意图和广播接收者的配对查找,看看谷歌究竟是怎么实现的!
上一篇文章我们说到广播接收者查询分为了静态广播查询和动态广播查询,此次我们先分析静态广播接收者查询。


image.png

切入入口就从广播发送的主流程那边开始,首先我们先分析collectReceiverComponents方法的内部实现:

public class ActivityManagerService extends IActivityManager.Stub
        implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
    ...
    private List collectReceiverComponents(Intent intent, String resolvedType,
            int callingUid, int[] users) {
        // TODO: come back and remove this assumption to triage all broadcasts
        int pmFlags = STOCK_PM_FLAGS | MATCH_DEBUG_TRIAGED_MISSING;

        List receivers = null;
        try {
            HashSet singleUserReceivers = null;
            boolean scannedFirstReceivers = false;
            for (int user : users) {//循环获取各用户下的广播接收者
                // Skip users that have Shell restrictions, with exception of always permitted
                //跳过具有Shell限制的用户
                // Shell broadcasts
                if (callingUid == SHELL_UID
                        && mUserController.hasUserRestriction(
                                UserManager.DISALLOW_DEBUGGING_FEATURES, user)
                        && !isPermittedShellBroadcast(intent)) {
                    continue;
                }
                //查找匹配Intent 的广播接收者
                List newReceivers = AppGlobals.getPackageManager()
                        .queryIntentReceivers(intent, resolvedType, pmFlags, user).getList();
                if (user != UserHandle.USER_SYSTEM && newReceivers != null) {
                    // If this is not the system user, we need to check for
                    // any receivers that should be filtered out.
                    //如果不是系统用户,检查过滤广播接收者
                    for (int i=0; i();
                                }
                                singleUserReceivers.add(cn);
                            }
                        }
                    }
                    // Add the new results to the existing results, tracking
                    // and de-dupping single user receivers.
                    //循环遍历新的接收者列表
                    for (int i=0; i();
                            }
                            if (!singleUserReceivers.contains(cn)) {
                                singleUserReceivers.add(cn);
                                receivers.add(ri);
                            }
                        } else {
                            receivers.add(ri);
                        }
                    }
                }
            }
        } catch (RemoteException ex) {
            // pm is in same process, this will never happen.
        }
        return receivers;
    }
    ...
}

沿着AppGlobals.getPackageManager().queryIntentReceivers()走下去,最终你会发现静态广播接收者的查询是在系统包管理服务PackageManagerService 里面实现的。

public class PackageManagerService extends IPackageManager.Stub
        implements PackageSender {
    ...
    public @NonNull ParceledListSlice queryIntentReceivers(Intent intent,
            String resolvedType, int flags, int userId) {
        return new ParceledListSlice<>(
                queryIntentReceiversInternal(intent, resolvedType, flags, userId));
    }

    private @NonNull List queryIntentReceiversInternal(Intent intent,
            String resolvedType, int flags, int userId) {
        //用户是否存在校验
        if (!sUserManager.exists(userId)) return Collections.emptyList();
        final int callingUid = Binder.getCallingUid();
        final String instantAppPkgName = getInstantAppPackageName(callingUid);
        flags = updateFlagsForResolve(flags, userId, intent, callingUid,
                false /*includeInstantApps*/);
        //获取广播接收者组件信息,包括包名和类名
        ComponentName comp = intent.getComponent();
        if (comp == null) {
            if (intent.getSelector() != null) {
                intent = intent.getSelector();
                comp = intent.getComponent();
            }
        }
        if (comp != null) {//组件不为空,则说明不用去查找,直接可以组装并返回ResolveInfo
            final List list = new ArrayList(1);
            //获取此次活动的相关信息
            final ActivityInfo ai = getReceiverInfo(comp, flags, userId);
            if (ai != null) {
                // When specifying an explicit component, we prevent the activity from being
                // used when either 1) the calling package is normal and the activity is within
                // an instant application or 2) the calling package is ephemeral and the
                // activity is not visible to instant applications.
                //两个明确的情况下,此次活动将被阻止
                //1、调用包正常,活动在Instant APP中运行
                //2、调用包是临时的,活动对Instant APP不可见。
                final boolean matchInstantApp =
                        (flags & PackageManager.MATCH_INSTANT) != 0;
                final boolean matchVisibleToInstantAppOnly =
                        (flags & PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY) != 0;
                final boolean matchExplicitlyVisibleOnly =
                        (flags & PackageManager.MATCH_EXPLICITLY_VISIBLE_ONLY) != 0;
                final boolean isCallerInstantApp =
                        instantAppPkgName != null;
                final boolean isTargetSameInstantApp =
                        comp.getPackageName().equals(instantAppPkgName);
                final boolean isTargetInstantApp =
                        (ai.applicationInfo.privateFlags
                                & ApplicationInfo.PRIVATE_FLAG_INSTANT) != 0;
                final boolean isTargetVisibleToInstantApp =
                        (ai.flags & ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0;
                final boolean isTargetExplicitlyVisibleToInstantApp =
                        isTargetVisibleToInstantApp
                        && (ai.flags & ActivityInfo.FLAG_IMPLICITLY_VISIBLE_TO_INSTANT_APP) == 0;
                final boolean isTargetHiddenFromInstantApp =
                        !isTargetVisibleToInstantApp
                        || (matchExplicitlyVisibleOnly && !isTargetExplicitlyVisibleToInstantApp);
                final boolean blockResolution =
                        !isTargetSameInstantApp
                        && ((!matchInstantApp && !isCallerInstantApp && isTargetInstantApp)
                                || (matchVisibleToInstantAppOnly && isCallerInstantApp
                                        && isTargetHiddenFromInstantApp));
                if (!blockResolution) {
                    ResolveInfo ri = new ResolveInfo();
                    ri.activityInfo = ai;
                    list.add(ri);
                }
            }
            return applyPostResolutionFilter(list, instantAppPkgName);//过滤临时活动
        }

        // reader
        synchronized (mPackages) {
            String pkgName = intent.getPackage();
            if (pkgName == null) {
                //通过mReceivers查询获取ResolveInfo列表
                final List result =
                        mReceivers.queryIntent(intent, resolvedType, flags, userId);
                return applyPostResolutionFilter(result, instantAppPkgName);
            }
            final PackageParser.Package pkg = mPackages.get(pkgName);
            if (pkg != null) {
                //查询mReceivers获取ResolveInfo列表
                final List result = mReceivers.queryIntentForPackage(
                        intent, resolvedType, flags, pkg.receivers, userId);
                return applyPostResolutionFilter(result, instantAppPkgName);
            }
            return Collections.emptyList();
        }
    }

    private List applyPostResolutionFilter(List resolveInfos,
            String ephemeralPkgName) {
        for (int i = resolveInfos.size() - 1; i >= 0; i--) {
            final ResolveInfo info = resolveInfos.get(i);
            final boolean isEphemeralApp = info.activityInfo.applicationInfo.isInstantApp();
            // TODO: When adding on-demand split support for non-instant apps, remove this check
            // and always apply post filtering
            // allow activities that are defined in the provided package
            if (isEphemeralApp) {
                if (info.activityInfo.splitName != null
                        && !ArrayUtils.contains(info.activityInfo.applicationInfo.splitNames,
                                info.activityInfo.splitName)) {
                    // requested activity is defined in a split that hasn't been installed yet.
                    // add the installer to the resolve list
                    if (DEBUG_EPHEMERAL) {
                        Slog.v(TAG, "Adding ephemeral installer to the ResolveInfo list");
                    }
                    final ResolveInfo installerInfo = new ResolveInfo(mInstantAppInstallerInfo);
                    installerInfo.auxiliaryInfo = new AuxiliaryResolveInfo(
                            info.activityInfo.packageName, info.activityInfo.splitName,
                            info.activityInfo.applicationInfo.versionCode, null /*failureIntent*/);
                    // make sure this resolver is the default
                    installerInfo.isDefault = true;
                    installerInfo.match = IntentFilter.MATCH_CATEGORY_SCHEME_SPECIFIC_PART
                            | IntentFilter.MATCH_ADJUSTMENT_NORMAL;
                    // add a non-generic filter
                    installerInfo.filter = new IntentFilter();
                    // load resources from the correct package
                    installerInfo.resolvePackageName = info.getComponentInfo().packageName;
                    resolveInfos.set(i, installerInfo);
                    continue;
                }
            }
            // caller is a full app, don't need to apply any other filtering
            if (ephemeralPkgName == null) {
                continue;
            } else if (ephemeralPkgName.equals(info.activityInfo.packageName)) {
                // caller is same app; don't need to apply any other filtering
                continue;
            }
            // allow activities that have been explicitly exposed to ephemeral apps
            if (!isEphemeralApp
                    && ((info.activityInfo.flags & ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0)) {
                continue;
            }
            resolveInfos.remove(i);
        }
        return resolveInfos;
    }
  ...
}

从上面的代码逻辑来看,在发送广播时把对应的包名和类名带上,广播发送的效率会有很大的提高,因为其中缺少了最耗时的Intent查找匹配逻辑!而最终的匹配查找是在变量mReceivers中实现的,那么mReceivers究竟做了什么呢?

public class PackageManagerService extends IPackageManager.Stub
        implements PackageSender {
    ...
    // All available receivers, for your resolving pleasure.
    final ActivityIntentResolver mReceivers = new ActivityIntentResolver();

    final class ActivityIntentResolver
            extends IntentResolver {
         ....
        public List queryIntent(Intent intent, String resolvedType, int flags,
                int userId) {
            if (!sUserManager.exists(userId)) return null;
            mFlags = flags;
            return super.queryIntent(intent, resolvedType,
                    (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0,
                    userId);
        }

        public List queryIntentForPackage(Intent intent, String resolvedType,
                int flags, ArrayList packageActivities, int userId) {
            if (!sUserManager.exists(userId)) return null;
            if (packageActivities == null) {
                return null;
            }
            mFlags = flags;
            final boolean defaultOnly = (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0;
            final int N = packageActivities.size();
            ArrayList listCut =
                new ArrayList(N);

            ArrayList intentFilters;
            for (int i = 0; i < N; ++i) {
                intentFilters = packageActivities.get(i).intents;
                if (intentFilters != null && intentFilters.size() > 0) {
                    PackageParser.ActivityIntentInfo[] array =
                            new PackageParser.ActivityIntentInfo[intentFilters.size()];
                    intentFilters.toArray(array);
                    listCut.add(array);
                }
            }
            return super.queryIntentFromList(intent, resolvedType, defaultOnly, listCut, userId);
        }
        ....
    }
    ...
}

没错,事实上的查找逻辑是写在父类IntentResolver里面的:

public abstract class IntentResolver {
    ....
    public List queryIntent(Intent intent, String resolvedType, boolean defaultOnly,
            int userId) {
        String scheme = intent.getScheme();

        ArrayList finalList = new ArrayList();

        final boolean debug = localLOGV ||
                ((intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0);

        if (debug) Slog.v(
            TAG, "Resolving type=" + resolvedType + " scheme=" + scheme
            + " defaultOnly=" + defaultOnly + " userId=" + userId + " of " + intent);

        F[] firstTypeCut = null;
        F[] secondTypeCut = null;
        F[] thirdTypeCut = null;
        F[] schemeCut = null;

        // If the intent includes a MIME type, then we want to collect all of
        // the filters that match that MIME type.
        //通过MIME type来匹配Intent
        if (resolvedType != null) {
            int slashpos = resolvedType.indexOf('/');
            if (slashpos > 0) {
                final String baseType = resolvedType.substring(0, slashpos);
                if (!baseType.equals("*")) {
                    if (resolvedType.length() != slashpos+2
                            || resolvedType.charAt(slashpos+1) != '*') {
                        // Not a wild card, so we can just look for all filters that
                        // completely match or wildcards whose base type matches.
                        firstTypeCut = mTypeToFilter.get(resolvedType);
                        if (debug) Slog.v(TAG, "First type cut: " + Arrays.toString(firstTypeCut));
                        secondTypeCut = mWildTypeToFilter.get(baseType);
                        if (debug) Slog.v(TAG, "Second type cut: "
                                + Arrays.toString(secondTypeCut));
                    } else {
                        // We can match anything with our base type.
                        firstTypeCut = mBaseTypeToFilter.get(baseType);
                        if (debug) Slog.v(TAG, "First type cut: " + Arrays.toString(firstTypeCut));
                        secondTypeCut = mWildTypeToFilter.get(baseType);
                        if (debug) Slog.v(TAG, "Second type cut: "
                                + Arrays.toString(secondTypeCut));
                    }
                    // Any */* types always apply, but we only need to do this
                    // if the intent type was not already */*.
                    thirdTypeCut = mWildTypeToFilter.get("*");
                    if (debug) Slog.v(TAG, "Third type cut: " + Arrays.toString(thirdTypeCut));
                } else if (intent.getAction() != null) {
                    // The intent specified any type ({@literal *}/*).  This
                    // can be a whole heck of a lot of things, so as a first
                    // cut let's use the action instead.
                    firstTypeCut = mTypedActionToFilter.get(intent.getAction());
                    if (debug) Slog.v(TAG, "Typed Action list: " + Arrays.toString(firstTypeCut));
                }
            }
        }

        // If the intent includes a data URI, then we want to collect all of
        // the filters that match its scheme (we will further refine matches
        // on the authority and path by directly matching each resulting filter).
        //通过scheme来匹配
        if (scheme != null) {
            schemeCut = mSchemeToFilter.get(scheme);
            if (debug) Slog.v(TAG, "Scheme list: " + Arrays.toString(schemeCut));
        }

        // If the intent does not specify any data -- either a MIME type or
        // a URI -- then we will only be looking for matches against empty
        // data.
        //通过action来匹配
        if (resolvedType == null && scheme == null && intent.getAction() != null) {
            firstTypeCut = mActionToFilter.get(intent.getAction());
            if (debug) Slog.v(TAG, "Action list: " + Arrays.toString(firstTypeCut));
        }
        //将查找出来的封装成为目标对象列表并返回
        FastImmutableArraySet categories = getFastIntentCategories(intent);
        if (firstTypeCut != null) {
            buildResolveList(intent, categories, debug, defaultOnly, resolvedType,
                    scheme, firstTypeCut, finalList, userId);
        }
        if (secondTypeCut != null) {
            buildResolveList(intent, categories, debug, defaultOnly, resolvedType,
                    scheme, secondTypeCut, finalList, userId);
        }
        if (thirdTypeCut != null) {
            buildResolveList(intent, categories, debug, defaultOnly, resolvedType,
                    scheme, thirdTypeCut, finalList, userId);
        }
        if (schemeCut != null) {
            buildResolveList(intent, categories, debug, defaultOnly, resolvedType,
                    scheme, schemeCut, finalList, userId);
        }
        filterResults(finalList);
        sortResults(finalList);

        if (debug) {
            Slog.v(TAG, "Final result list:");
            for (int i=0; i mFilters = new ArraySet();

    /**
     * All of the MIME types that have been registered, such as "image/jpeg",
     * "image/*", or "{@literal *}/*".
     */
    private final ArrayMap mTypeToFilter = new ArrayMap();

    /**
     * The base names of all of all fully qualified MIME types that have been
     * registered, such as "image" or "*".  Wild card MIME types such as
     * "image/*" will not be here.
     */
    private final ArrayMap mBaseTypeToFilter = new ArrayMap();

    /**
     * The base names of all of the MIME types with a sub-type wildcard that
     * have been registered.  For example, a filter with "image/*" will be
     * included here as "image" but one with "image/jpeg" will not be
     * included here.  This also includes the "*" for the "{@literal *}/*"
     * MIME type.
     */
    private final ArrayMap mWildTypeToFilter = new ArrayMap();

    /**
     * All of the URI schemes (such as http) that have been registered.
     */
    private final ArrayMap mSchemeToFilter = new ArrayMap();

    /**
     * All of the actions that have been registered, but only those that did
     * not specify data.
     */
    private final ArrayMap mActionToFilter = new ArrayMap();

    /**
     * All of the actions that have been registered and specified a MIME type.
     */
    private final ArrayMap mTypedActionToFilter = new ArrayMap();

}

从上面的代码可以看出,查询广播的目标接收者其实是通过Map键值对来查找的,根据不同的专属特征(Action、Scheme、Mime Typed等)来获取对应接收者,逻辑其实并不复杂。代码读到此处,我们难免会有疑问:mActionToFilter、mSchemeToFilter等这些过滤器是什么时候初始化的?静态注册的广播是如何加载到这些Map里面的?别急,代码还需继续看下去:

public abstract class IntentResolver {
      ....
    public void addFilter(F f) {
        if (localLOGV) {
            Slog.v(TAG, "Adding filter: " + f);
            f.dump(new LogPrinter(Log.VERBOSE, TAG, Log.LOG_ID_SYSTEM), "      ");
            Slog.v(TAG, "    Building Lookup Maps:");
        }

        mFilters.add(f);
        int numS = register_intent_filter(f, f.schemesIterator(),
                mSchemeToFilter, "      Scheme: ");
        int numT = register_mime_types(f, "      Type: ");
        if (numS == 0 && numT == 0) {
            register_intent_filter(f, f.actionsIterator(),
                    mActionToFilter, "      Action: ");
        }
        if (numT != 0) {
            register_intent_filter(f, f.actionsIterator(),
                    mTypedActionToFilter, "      TypedAction: ");
        }
    }
    private final int register_intent_filter(F filter, Iterator i,
            ArrayMap dest, String prefix) {
        if (i == null) {
            return 0;
        }

        int num = 0;
        while (i.hasNext()) {
            String name = i.next();
            num++;
            if (localLOGV) Slog.v(TAG, prefix + name);
            addFilter(dest, name, filter);
        }
        return num;
    }
    private final void addFilter(ArrayMap map, String name, F filter) {
        F[] array = map.get(name);
        if (array == null) {
            array = newArray(2);
            map.put(name,  array);
            array[0] = filter;
        } else {
            final int N = array.length;
            int i = N;
            while (i > 0 && array[i-1] == null) {
                i--;
            }
            if (i < N) {
                array[i] = filter;
            } else {
                F[] newa = newArray((N*3)/2);
                System.arraycopy(array, 0, newa, 0, N);
                newa[N] = filter;
                map.put(name, newa);
            }
        }
    }
}

全局查看IntentResolver的代码,你会发现IntentResolver对外开放的过滤器数据添加接口只有一个addFilter(F f),而mReceivers中将这个接口的调用封装到了addActivity(PackageParser.Activity a, String type)方法中,沿着方法被调用的地方查找,你会发现PackageManagerService的构造函数中经过一层层的封装,最终有触发IntentResolver.addActivity()的调用,此处应是用作初始化;同时在PackageHandler mHandler 中也有触发调用,此处应是用作更新!此处代码较多就不一一摘录了。

总结

通过上面的源码分析,我们可以总结出一下结论:
1、静态广播接收者的查找实际上是在包管理服务PackageManagerService 中实现的。
2、广播Intent中指定目标接收者的包名和类名会跳过查找的步骤,极大的提高了广播发送的效率。
3、静态广播接收者会在包管理服务PackageManagerService初始化的时候以键值对的形式被加载到内存当中,广播Intent在查找对应接收者的时候通过其所带的Action、Scheme、MIME TYPE等数据来获取对应的接收者。当然在有新android包安装、升级或卸载的时候,静态广播接收者的数据也会实时更新。

你可能感兴趣的:(跨进程广播的工作原理(3))