Android7.0 PackageManagerService (4) Intent匹配Activity的过程

通过前面的分析,我们知道PKMS负责维护终端全部的Package信息,因此可以想到PKMS具有能力对外提供统一的Package信息查询接口。
我们以查询匹配指定Intent的所有Activity的过程为例,分析一下PKMS提供这类服务对应的流程。

一、PKMS中Activity的信息管理
在前面的博客已经提到,PKMS解析Package信息时,将调用到scanPackageDirtyLI函数。
该函数将会将Package中四大组件等信息,加入到PKMS定义的数据结构中,以便统一管理。

以下代码片段就是为了将Package中的activity相关的信息,加入到PKMS中:

N = pkg.activities.size();
r = null;
for (i=0; i
    PackageParser.Activity a = pkg.activities.get(i);
    a.info.processName = fixProcessName(pkg.applicationInfo.processName,
            a.info.processName, pkg.applicationInfo.uid);
    //Package中的信息被加入到了PKMS的mActivities中
    mActivities.addActivity(a, "activity");
    ..........
}

上述代码的内容比较简单,主要需要关注对应的数据结构。
Android7.0 PackageManagerService (4) Intent匹配Activity的过程_第1张图片
结合代码,我们知道:
1、PKMS中的成员变量mActivities为ActivityIntentResolver类型,用于保存所有与Activity相关的信息。ActivityIntentResolver内部也有一个mActivities变量,它以ComponentName为key,保存PackageParser.Activity对象。
2、从APK文件中解析出来的所有和Activity相关的信息都由PackageParser.Activity来保存。

我们跟进一下ActivityIntentResolver的addActivity函数:

public final void addActivity(PackageParser.Activity a, String type) {
    //将ComponentName和Activity保存到ActivityIntentResolver的mActivities中
    mActivities.put(a.getComponentName(), a);
    ..........
    final int NI = a.intents.size();
    for (int j=0; j<NI; j++) {
        //ActivityIntentInfo存储的是XML中声明的IntentFilter信息
        PackageParser.ActivityIntentInfo intent = a.intents.get(j);
        if ("activity".equals(type)) {
            final PackageSetting ps =
                    mSettings.getDisabledSystemPkgLPr(intent.activity.info.packageName);
            final List<PackageParser.Activity> systemActivities =
                    ps != null && ps.pkg != null ? ps.pkg.activities : null;
            //按照策略,将一些IntentFilter的优先级设置为0
            //例如非系统APK的优先级将被设置为0
            adjustPriority(systemActivities, intent);
        }
        ..........
        //将activity中对应的IntentFilter加入到PKMS中
        addFilter(intent);
    }
}

从上面的代码,我们知道了PKMS除了维护Activity的基本信息外,重点“照顾”了Activity定义的IntentFilter。
跟进addFilter函数:

public void addFilter(F f) {
    .......
    //mFilters保存所有的IntentFilter信息
    mFilters.add(f);

    //除此之外,为了加快匹配工作的速度,PKMS还分类保存了IntentFilter的信息

    //mSchemeToFilter用于保存uri中与scheme相关的IntentFilter
    int numS = register_intent_filter(f, f.schemesIterator(),
                mSchemeToFilter, "      Scheme: ");

    //按照MIME type存储IntentFilter,分别定义了mTypeToFilter、mBaseTypeToFilter和mWildTypeToFilter
    //与MIME的格式有关,代码较为简单,不深入分析
    int numT = register_mime_types(f, "      Type: ");

    if (numS == 0 && numT == 0) {
        //mActionToFilter用于保存仅设置了Action条件的IntentFilter
        register_intent_filter(f, f.actionsIterator(),
                mActionToFilter, "      Action: ");
    }
    if (numT != 0) {
        //mTypedActionToFilter用于保存既设置了Action条件,又设置了Data类型的IntentFilter
        register_intent_filter(f, f.actionsIterator(),
                mTypedActionToFilter, "      TypedAction: ");
    }
}

二、PKMS中Intent与Activity的匹配过程
一般情况下,客户端可以使用ApplicationPackageManager的接口queryIntentActivities发起查询请求:

public List queryIntentActivities(Intent intent, int flags) {
    return queryIntentActivitiesAsUser(intent, flags, mContext.getUserId());
}

public List queryIntentActivitiesAsUser(Intent intent,
        int flags, int userId) {
    try {
        ParceledListSlice parceledList =
                //调用PKMS的接口
                mPM.queryIntentActivities(intent,
                        //如果Intent的Data中包含Uri,那么就需要根据Uri对应的ContentProvider得到Data对应的类型
                        intent.resolveTypeIfNeeded(mContext.getContentResolver()),
                        flags, userId);
        if (parceledList == null) {
            return Collections.emptyList();
        }
        return parceledList.getList();
    } catch (RemoteException e) {
        throw e.rethrowFromSystemServer();
    }
}

我们主要分析PKMS的queryIntentActivities函数:

public @NonNull ParceledListSlice<ResolveInfo> queryIntentActivities(Intent intent,
        String resolvedType, int flags, int userId) {
    try {
        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "queryIntentActivities");

        return new ParceledListSlice<>(
                queryIntentActivitiesInternal(intent, resolvedType, flags, userId));
    } finally {
        Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
    }
}

private @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent,
        String resolvedType, int flags, int userId) {
    if (!sUserManager.exists(userId)) return Collections.emptyList();
    .........
    ComponentName comp = intent.getComponent();
    .........
    if (comp == null) {
        if (intent.getSelector() != null) {
            intent = intent.getSelector();
            comp = intent.getComponent();
        }
    }

    //显示匹配
    if (comp != null) {
        final List<ResolveInfo> list = new ArrayList<ResolveInfo>(1);
        //利用getActivityInfo获取指定的ActivityInfo即可
        final ActivityInfo ai = getActivityInfo(comp, flags, userId);
        if (ai != null) {
            final ResolveInfo ri = new ResolveInfo();
            ri.activityInfo = ai;
            list.add(ri);
        }
        return list;
    }

    synchronized (mPackages) {
        final String pkgName = intent.getPackage();
        //Intent未指定Package名,需要在整个系统范围内进行匹配查询
        if (pkgName == null) {
            //处理隐式Intent,此处引入了CrossProfile和Current Profile的概念,不是很懂
            //但不论如何,最终调用的是ActivityIntentResolver的queryIntent函数来得到实际的结果
            ..............
        }

        //Intent指定了Package名
        final PackageParser.Package pkg = mPackages.get(pkgName);
        if (pkg != null) {
            return filterIfNotSystemUser(
                    //在指定Package的Activity中,进行匹配
                    mActivities.queryIntentForPackage(
                            intent, resolvedType, flags, pkg.activities, userId),
                            userId);
        }
        return new ArrayList<ResolveInfo>();
    }
}

上面的代码明显的将Intent分为3类进行处理:
如果Intent指明了Component,则直接查询该Component对应的ActivityInfo,这也是我们常说的显示Intent;
如果Intent仅指明的Package名,则根据Package名找出对应的Package,然后再从中Package中找出匹配的ActivityInfo;
如果上述条件均不满足,也就是遇到常说的隐式Intent,则需要在全局系统中查找ActivityInfo。

这里我们重点分析一下,ActivityIntentResolver的queryIntent函数流程:

public List queryIntent(Intent intent, String resolvedType,
        boolean defaultOnly, int userId) {
    if (!sUserManager.exists(userId)) return null;
    mFlags = defaultOnly ? PackageManager.MATCH_DEFAULT_ONLY : 0;
    //调用IntentResolver的queryIntent函数
    return super.queryIntent(intent, resolvedType, defaultOnly, userId);
}

跟进IntentResolver的queryIntent函数:

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

    //用于保存最终结果
    ArrayList finalList = new ArrayList();
    ...........

    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.
    if (resolvedType != null) {
        //从PKMS中的mTypeToFilter、mWildTypeToFilter、mBaseTypeToFilter和mTypedActionToFilter中
        //取出匹配类型的ActivityInfo填充到firstTypeCut、secondTypeCut和thirdTypeCut中
        ..................
    }

    // 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).
    if (scheme != null) {
        schemeCut = mSchemeToFilter.get(scheme);
        ..............
    }

    // 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.
    if (resolvedType == null && scheme == null && intent.getAction() != null) {
        firstTypeCut = mActionToFilter.get(intent.getAction());
        ..............
    }

    FastImmutableArraySet categories = getFastIntentCategories(intent);

    //以下开始整合所有信息,进行一轮轮地筛选
    //每次都将符合全部要求的ActivityInfo加入到finalList中

    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);
    }
    sortResults(finalList);
    ............
    return finalList;
}

上述代码的主要目的比较清晰,最终就是通过Intent携带的信息,从PKMS中取出完全与之要求相匹配的ActivityInfo。
按照逻辑来讲,queryIntent函数初始时,应该就是从不同的维度,将满足该维度的ActivityInfo存入对应的Cut数组中。
那么最终的结果,应该就是四个Cut数组的交集。

为了验证这一点,我们看一下buildResolveList函数:

//src依次为每个Cut数组,dest为finalList
private void buildResolveList(Intent intent, FastImmutableArraySet categories,
        boolean debug, boolean defaultOnly,
        String resolvedType, String scheme, F[] src, List dest, int userId) {
    ...........
    for (i=0; inull; i++) {
        ............
        // Do we already have this one?
        //这里的allowFilterResult就相当于求交集的作用
        //处理一个Cut数组时,若某个filter之前已经匹配过,那么就可以跳过
        if (!allowFilterResult(filter, dest)) {
            if (debug) {
                Slog.v(TAG, "  Filter's target already added");
            }
            continue;
        }

        //进行一次全条件匹配
        match = filter.match(action, resolvedType, scheme, data, categories, TAG);
        //匹配成功后,将ActivityInfo加入到dest中
        ...........
    }
    ........
}

我们看看allowFilterResult函数,实际上该函数由IntentResolver的子类实现,此处需要跟进ActivityInfoResolver中的实现:

protected boolean allowFilterResult(
        PackageParser.ActivityIntentInfo filter, List dest) {
    ActivityInfo filterAi = filter.activity.info;
    for (int i=dest.size()-1; i>=0; i--) {
        ActivityInfo destAi = dest.get(i).activityInfo;
        //匹配时返回false,将会跳过match检查
        if (destAi.name == filterAi.name
                && destAi.packageName == filterAi.packageName) {
            return false;
        }
    }
    return true;
}

三、总结
从整个代码来看,PKMS中查找与指定Intent匹配的Activity的过程,主要的思想很清晰,但其中涉及的数据结构和细节还是相当琐碎的。

可以看出queryIntentActivities函数在处理隐式Intent时,主要通过IntentResolver来完成实际的查找工作。考虑到PKMS中四大组件的继承结构,我们大体可以推测出Provider和Service的Intent匹配过程,应该和Activity有许多相似之处。

你可能感兴趣的:(Android源码学习笔记)