PackageManagerService Intent匹配查询
来源:互联网 发布:js音乐的获取方式 编辑:程序博客网 时间:2024/06/05 16:10
PKMS除了负责应用的安装、更新、卸载之外,同时也对外提供信息的查询。例如查询系统中匹配某Intent的Activities、Services等。本文就以Activity为例,分析PKMS如何通过Intent查询Activity。
本文分为三个部分:
1. Activity信息的保存
2. Intent查询Activity的规则
3. PKMS通过Intent查询Activity的过程
一、Activity信息的保存
Activity信息的保存是在安装应用时完成的,在scanPackageDirtyLI()方法最后会把Activity信息保存在PKMS的变量mActivities中。mActivities为ActivityIntenResolver类型,为PKMS的内部类。主要处理Intent的查询操作,其父类为IntentResolver<F extends IntentFilter, R extends Object>,为泛型编程,因为还有ServiceIntentResolver等。
final class ActivityIntentResolver extends IntentResolver<PackageParser.ActivityIntentInfo, ResolveInfo> { public List<ResolveInfo> queryIntent(Intent intent, String resolvedType, boolean defaultOnly, int userId) { if (!sUserManager.exists(userId)) return null; mFlags = defaultOnly ? PackageManager.MATCH_DEFAULT_ONLY : 0; return super.queryIntent(intent, resolvedType, defaultOnly, userId); } public List<ResolveInfo> 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<ResolveInfo> queryIntentForPackage(Intent intent, String resolvedType, int flags, ArrayList<PackageParser.Activity> 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<PackageParser.ActivityIntentInfo[]> listCut = new ArrayList<PackageParser.ActivityIntentInfo[]>(N); ArrayList<PackageParser.ActivityIntentInfo> 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); } public final void addActivity(PackageParser.Activity a, String type) { final boolean systemApp = a.info.applicationInfo.isSystemApp(); mActivities.put(a.getComponentName(), a); if (DEBUG_SHOW_INFO) Log.v( TAG, " " + type + " " + (a.info.nonLocalizedLabel != null ? a.info.nonLocalizedLabel : a.info.name) + ":"); if (DEBUG_SHOW_INFO) Log.v(TAG, " Class=" + a.info.name); final int NI = a.intents.size(); for (int j=0; j<NI; j++) { PackageParser.ActivityIntentInfo intent = a.intents.get(j); if (!systemApp && intent.getPriority() > 0 && "activity".equals(type)) { intent.setPriority(0); Log.w(TAG, "Package " + a.info.applicationInfo.packageName + " has activity " + a.className + " with priority > 0, forcing to 0"); } if (DEBUG_SHOW_INFO) { Log.v(TAG, " IntentFilter:"); intent.dump(new LogPrinter(Log.VERBOSE, TAG), " "); } if (!intent.debugCheck()) { Log.w(TAG, "==> For Activity " + a.info.name); } addFilter(intent); } } public final void removeActivity(PackageParser.Activity a, String type) { mActivities.remove(a.getComponentName()); if (DEBUG_SHOW_INFO) { Log.v(TAG, " " + type + " " + (a.info.nonLocalizedLabel != null ? a.info.nonLocalizedLabel : a.info.name) + ":"); Log.v(TAG, " Class=" + a.info.name); } final int NI = a.intents.size(); for (int j=0; j<NI; j++) { PackageParser.ActivityIntentInfo intent = a.intents.get(j); if (DEBUG_SHOW_INFO) { Log.v(TAG, " IntentFilter:"); intent.dump(new LogPrinter(Log.VERBOSE, TAG), " "); } removeFilter(intent); } } @Override protected boolean allowFilterResult( PackageParser.ActivityIntentInfo filter, List<ResolveInfo> dest) { ActivityInfo filterAi = filter.activity.info; for (int i=dest.size()-1; i>=0; i--) { ActivityInfo destAi = dest.get(i).activityInfo; if (destAi.name == filterAi.name && destAi.packageName == filterAi.packageName) { return false; } } return true; } @Override protected ActivityIntentInfo[] newArray(int size) { return new ActivityIntentInfo[size]; } @Override protected boolean isFilterStopped(PackageParser.ActivityIntentInfo filter, int userId) { if (!sUserManager.exists(userId)) return true; PackageParser.Package p = filter.activity.owner; if (p != null) { PackageSetting ps = (PackageSetting)p.mExtras; if (ps != null) { // System apps are never considered stopped for purposes of // filtering, because there may be no way for the user to // actually re-launch them. return (ps.pkgFlags&ApplicationInfo.FLAG_SYSTEM) == 0 && ps.getStopped(userId); } } return false; } @Override protected boolean isPackageForFilter(String packageName, PackageParser.ActivityIntentInfo info) { return packageName.equals(info.activity.owner.packageName); } @Override protected ResolveInfo newResult(PackageParser.ActivityIntentInfo info, int match, int userId) { if (!sUserManager.exists(userId)) return null; if (!mSettings.isEnabledLPr(info.activity.info, mFlags, userId)) { return null; } final PackageParser.Activity activity = info.activity; if (mSafeMode && (activity.info.applicationInfo.flags &ApplicationInfo.FLAG_SYSTEM) == 0) { return null; } PackageSetting ps = (PackageSetting) activity.owner.mExtras; if (ps == null) { return null; } ActivityInfo ai = PackageParser.generateActivityInfo(activity, mFlags, ps.readUserState(userId), userId); if (ai == null) { return null; } final ResolveInfo res = new ResolveInfo(); res.activityInfo = ai; if ((mFlags&PackageManager.GET_RESOLVED_FILTER) != 0) { res.filter = info; } if (info != null) { res.handleAllWebDataURI = info.handleAllWebDataURI(); } res.priority = info.getPriority(); res.preferredOrder = activity.owner.mPreferredOrder; //System.out.println("Result: " + res.activityInfo.className + // " = " + res.priority); res.match = match; res.isDefault = info.hasDefault; res.labelRes = info.labelRes; res.nonLocalizedLabel = info.nonLocalizedLabel; if (userNeedsBadging(userId)) { res.noResourceId = true; } else { res.icon = info.icon; } res.iconResourceId = info.icon; res.system = res.activityInfo.applicationInfo.isSystemApp(); return res; } @Override protected void sortResults(List<ResolveInfo> results) { Collections.sort(results, mResolvePrioritySorter); } @Override protected void dumpFilter(PrintWriter out, String prefix, PackageParser.ActivityIntentInfo filter) { out.print(prefix); out.print( Integer.toHexString(System.identityHashCode(filter.activity))); out.print(' '); filter.activity.printComponentShortName(out); out.print(" filter "); out.println(Integer.toHexString(System.identityHashCode(filter))); } @Override protected Object filterToLabel(PackageParser.ActivityIntentInfo filter) { return filter.activity; } protected void dumpFilterLabel(PrintWriter out, String prefix, Object label, int count) { PackageParser.Activity activity = (PackageParser.Activity)label; out.print(prefix); out.print( Integer.toHexString(System.identityHashCode(activity))); out.print(' '); activity.printComponentShortName(out); if (count > 1) { out.print(" ("); out.print(count); out.print(" filters)"); } out.println(); }// List<ResolveInfo> filterEnabled(List<ResolveInfo> resolveInfoList) {// final Iterator<ResolveInfo> i = resolveInfoList.iterator();// final List<ResolveInfo> retList = Lists.newArrayList();// while (i.hasNext()) {// final ResolveInfo resolveInfo = i.next();// if (isEnabledLP(resolveInfo.activityInfo)) {// retList.add(resolveInfo);// }// }// return retList;// } // Keys are String (activity class name), values are Activity. private final ArrayMap<ComponentName, PackageParser.Activity> mActivities = new ArrayMap<ComponentName, PackageParser.Activity>(); private int mFlags; }
private PackageParser.Package scanPackageDirtyLI(PackageParser.Package pkg, int parseFlags, int scanFlags, long currentTime, UserHandle user) throws PackageManagerException {...... N = pkg.activities.size(); r = null; for (i=0; i<N; i++) { PackageParser.Activity a = pkg.activities.get(i); a.info.processName = fixProcessName(pkg.applicationInfo.processName, a.info.processName, pkg.applicationInfo.uid); mActivities.addActivity(a, "activity"); if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) { if (r == null) { r = new StringBuilder(256); } else { r.append(' '); } r.append(a.info.name); } }......}
1.2 .addActivity()首先以Component为键,Packageparser.Activity对象为值保存Activity信息在mActivities的内部变量mActivities。然后调用其父类的addFilter保存Activity的intent信息(用PackageParser.ActivityIntentInfo表示)
public final void addActivity(PackageParser.Activity a, String type) { final boolean systemApp = a.info.applicationInfo.isSystemApp(); mActivities.put(a.getComponentName(), a); if (DEBUG_SHOW_INFO) Log.v( TAG, " " + type + " " + (a.info.nonLocalizedLabel != null ? a.info.nonLocalizedLabel : a.info.name) + ":"); if (DEBUG_SHOW_INFO) Log.v(TAG, " Class=" + a.info.name); final int NI = a.intents.size(); for (int j=0; j<NI; j++) { PackageParser.ActivityIntentInfo intent = a.intents.get(j); if (!systemApp && intent.getPriority() > 0 && "activity".equals(type)) { intent.setPriority(0); Log.w(TAG, "Package " + a.info.applicationInfo.packageName + " has activity " + a.className + " with priority > 0, forcing to 0"); } if (DEBUG_SHOW_INFO) { Log.v(TAG, " IntentFilter:"); intent.dump(new LogPrinter(Log.VERBOSE, TAG), " "); } if (!intent.debugCheck()) { Log.w(TAG, "==> For Activity " + a.info.name); } addFilter(intent); } }
1.3 在为了加快匹配速度,创建了较多种类的成员变量:ArraySet<F>mFilters : 保存了所有的IntentFilter
ArrayMap<String,F[]> mTypeToFilter : 保存所有MIME types类型
ArrayMap<String,F[]> mBaseTypeToFilter : 包含MIME中Base类型的IntentFilter信息,但不包括Sub type为“*”的IntentFilter
ArrayMap<String,F[]> mWildTypeToFilter : 用于保存设置了Data类型类似“image/*”的IntentFilter
ArrayMap<String,F[]> mSchemeToFilter : 保存与scheme相关的IntentFilter信息
ArrayMap<String,F[]> mActionToFilter : 保存仅设置Action条件的IntentFilter信息
ArrayMap<String,F[]> mTypedActionToFilter : 保存了既设置了Action又设置了Data的MIME类型的IntentFilter信息
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: "); } }
二、Intent查询Activity的规则
这部分对于开发APP也是需要掌握的知识,这里简单介绍下匹配规则。
2.1指定ComponentName
不需要使用<intent-filter.../>元素进行配置。直接根据Component启动相应的组件。
2.2 通过Action、category匹配
Intent的Action、Category属性的值都是一个普通的字符串,其中Action代表该Intent所要完成的一个抽象“动作”,而Category则用于为Action增加额外的附加类别信息,通常Action属性会与Category属性结合使用。
Action:Intent中的Action必须能够和Activity过滤规则中的Action匹配.(这里的匹配是完全相等).一个过滤规则中有多个action,那么只要Intent中的action能够和Activity过滤规则中的任何一个action相同即可匹配成功。
Category: 如果Intent中的存在category那么所有的category都必须和Activity过滤规则中的category相同。才能和这个Activity匹配。Intent中的category数量可能少于Activity中配置的category数量,但是Intent中的这category必须和Activity中配置的category相同才能匹配。
2.3 通过Type、Data匹配
Data属性通常用于向Action属性提供操作的数据。Data属性接受一个Uri对象,一个Uri对象通常通过如下形式的字符串来表示:
content://com.android.contacts:8080/contacts/1
例如上面给出的content://com.android.contacts/contacts/1,其中content是scheme部分,com.android.contacts是host部分,port为8080,/contacts/1是path部分。
Type属性用于指定该Data属性所指定Uri对应的MIME类型,这种MIME类型可以是任何自定义的MIME类型,只要符合abc/xyz格式的字符串即可。
Type:Intent的Type属性也用于指定该Intent的要求,对应组件中<intent-filter.../>元素的<data.../>子元素的mimeType属性必须与此相同,才能启动该组件。
Data:
· 如果目标组件的<data.../>子元素只指定了android:scheme属性,那么只要Intent的Data属性的scheme部分与android:scheme属性值相同,即可启动该组件。
· 如果目标组件的<data../>子元素只指定了android:scheme、android:host属性,那么只要Intent的Data属性的scheme、host部分与android:scheme、android:host属性值相同,即可启动该组件。
· 如果目标组件的<data../>子元素只指定了android:scheme、android:host、android:port属性,那么只要Intent的Data属性的scheme、host、port部分与android:scheme、android:host、android:port属性值相同,即可启动该组件。(注意:如果<data.../>子元素只有android:port属性,没有指定android:host属性,那么android:port属性将不会起作用)。
· 如果目标组件的<data../>子元素只指定了android:scheme、android:host、android:path属性,那么只要Intent的Data属性的scheme、host、path部分与android:scheme、android:host、android:path属性值相同,即可启动该组件。(注意:如果<data.../>子元素只有android:path属性,没有指定android:host属性,那么android:path属性将不会起作用)。
· 如果目标组件的<data../>子元素只指定了android:scheme、android:host、android:port、android:path属性,那么只要Intent的Data属性的scheme、host、port、path部分与android:scheme、android:host、android:port、android:path属性值相同,即可启动该组件。
三 、PKMS通过Intent查询Activity的过程
当要打开一个Activity时,客户端通过ApplicationPackageManager输出的queryIntentActivities方法向PKMS发起送查询请求,这会调用到PKMS的queryIntentActivities方法。查询Activity分为三种情况
1. 指定了ComponentName
2. 指定了Package
3. 既没指定Component也没指定package
@Override public List<ResolveInfo> queryIntentActivities(Intent intent, String resolvedType, int flags, int userId) { if (!sUserManager.exists(userId)) return Collections.emptyList(); enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false, "query intent activities"); ComponentName comp = intent.getComponent(); if (comp == null) { if (intent.getSelector() != null) { intent = intent.getSelector(); comp = intent.getComponent(); } } //指定了Component if (comp != null) { final List<ResolveInfo> list = new ArrayList<ResolveInfo>(1); final ActivityInfo ai = getActivityInfo(comp, flags, userId); if (ai != null) { final ResolveInfo ri = new ResolveInfo(); ri.activityInfo = ai; list.add(ri); } return list; } // reader synchronized (mPackages) { final String pkgName = intent.getPackage();//既没有指定packgae也没有指定Component。 if (pkgName == null) { List<CrossProfileIntentFilter> matchingFilters = getMatchingCrossProfileIntentFilters(intent, resolvedType, userId); // Check for results that need to skip the current profile. ResolveInfo xpResolveInfo = querySkipCurrentProfileIntents(matchingFilters, intent, resolvedType, flags, userId); if (xpResolveInfo != null && isUserEnabled(xpResolveInfo.targetUserId)) { List<ResolveInfo> result = new ArrayList<ResolveInfo>(1); result.add(xpResolveInfo); return filterIfNotPrimaryUser(result, userId); } // Check for results in the current profile. List<ResolveInfo> result = mActivities.queryIntent( intent, resolvedType, flags, userId); // Check for cross profile results. xpResolveInfo = queryCrossProfileIntents( matchingFilters, intent, resolvedType, flags, userId); if (xpResolveInfo != null && isUserEnabled(xpResolveInfo.targetUserId)) { result.add(xpResolveInfo); Collections.sort(result, mResolvePrioritySorter); } result = filterIfNotPrimaryUser(result, userId); if (hasWebURI(intent)) { CrossProfileDomainInfo xpDomainInfo = null; final UserInfo parent = getProfileParent(userId); if (parent != null) { xpDomainInfo = getCrossProfileDomainPreferredLpr(intent, resolvedType, flags, userId, parent.id); } if (xpDomainInfo != null) { if (xpResolveInfo != null) { // If we didn't remove it, the cross-profile ResolveInfo would be twice // in the result. result.remove(xpResolveInfo); } if (result.size() == 0) { result.add(xpDomainInfo.resolveInfo); return result; } } else if (result.size() <= 1) { return result; } result = filterCandidatesWithDomainPreferredActivitiesLPr(intent, flags, result, xpDomainInfo, userId); Collections.sort(result, mResolvePrioritySorter); } return result; } final PackageParser.Package pkg = mPackages.get(pkgName);//指定了Package if (pkg != null) { return filterIfNotPrimaryUser( mActivities.queryIntentForPackage( intent, resolvedType, flags, pkg.activities, userId), userId); } return new ArrayList<ResolveInfo>(); } }
3.1 指定Component
指定ComponentName获取ActivityInfo是最简单的,根据前面第一部分保存Activity信息可知,Activity信息被保存在PKMS的变量mActivities的内部变量mActivities中,其结构为HashMap<ComponentName ,PackageParser.Activity>,通过Component即可获取Activity信息。
@Override public ActivityInfo getActivityInfo(ComponentName component, int flags, int userId) { if (!sUserManager.exists(userId)) return null; enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false, "get activity info"); synchronized (mPackages) { PackageParser.Activity a = mActivities.mActivities.get(component); if (DEBUG_PACKAGE_INFO) Log.v(TAG, "getActivityInfo " + component + ": " + a); if (a != null && mSettings.isEnabledLPr(a.info, flags, userId)) { PackageSetting ps = mSettings.mPackages.get(component.getPackageName()); if (ps == null) return null; return PackageParser.generateActivityInfo(a, flags, ps.readUserState(userId), userId); } if (mResolveComponentName.equals(component)) { return PackageParser.generateActivityInfo(mResolveActivity, flags, new PackageUserState(), userId); } } return null; }
3.2 指定Package
指定Package的情况下,PKMS调用queryIntentForPackage进行查询。流程如下:
1. 获取Package下所有Activity的intent信息,保存在ArrayList<PackageParser.ActivityIntentInfo[]> listCut中,PackageParser.ActivityIntentInfo[]数组保存了一个Activity包含的IntentFilter信息。然后调用父类的queryIntentFromList方法。
public List<ResolveInfo> queryIntentForPackage(Intent intent, String resolvedType, int flags, ArrayList<PackageParser.Activity> 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<PackageParser.ActivityIntentInfo[]> listCut = new ArrayList<PackageParser.ActivityIntentInfo[]>(N); ArrayList<PackageParser.ActivityIntentInfo> 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); }
2. queryIntentFromList方法中遍历listCut列表,即逐一取出每个Activiy的intentFilter信息,然后调用buildResolveList方法来和intent进行匹配
public List<R> queryIntentFromList(Intent intent, String resolvedType, boolean defaultOnly, ArrayList<F[]> listCut, int userId) { ArrayList<R> resultList = new ArrayList<R>(); final boolean debug = localLOGV || ((intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0); FastImmutableArraySet<String> categories = getFastIntentCategories(intent); final String scheme = intent.getScheme(); int N = listCut.size(); for (int i = 0; i < N; ++i) { buildResolveList(intent, categories, debug, defaultOnly, resolvedType, scheme, listCut.get(i), resultList, userId); } sortResults(resultList); return resultList; }
3. buildResolveList中,首先获取intent的action、category、type、data信息,然后遍历IntentFilter,调用其match方法进行匹配。匹配规则如第二部分所描述的,具体代码可以参考IntentFilter类的match方法。
private void buildResolveList(Intent intent, FastImmutableArraySet<String> categories, boolean debug, boolean defaultOnly, String resolvedType, String scheme, F[] src, List<R> dest, int userId) { final String action = intent.getAction(); final Uri data = intent.getData(); final String packageName = intent.getPackage(); final boolean excludingStopped = intent.isExcludingStopped(); final Printer logPrinter; final PrintWriter logPrintWriter; if (debug) { logPrinter = new LogPrinter(Log.VERBOSE, TAG, Log.LOG_ID_SYSTEM); logPrintWriter = new FastPrintWriter(logPrinter); } else { logPrinter = null; logPrintWriter = null; } final int N = src != null ? src.length : 0; boolean hasNonDefaults = false; int i; F filter; for (i=0; i<N && (filter=src[i]) != null; i++) { int match; if (debug) Slog.v(TAG, "Matching against filter " + filter); if (excludingStopped && isFilterStopped(filter, userId)) { if (debug) { Slog.v(TAG, " Filter's target is stopped; skipping"); } continue; } // Is delivery being limited to filters owned by a particular package? if (packageName != null && !isPackageForFilter(packageName, filter)) { if (debug) { Slog.v(TAG, " Filter is not from package " + packageName + "; skipping"); } continue; } // Are we verified ? if (filter.getAutoVerify()) { if (localVerificationLOGV || debug) { Slog.v(TAG, " Filter verified: " + isFilterVerified(filter)); int authorities = filter.countDataAuthorities(); for (int z = 0; z < authorities; z++) { Slog.v(TAG, " " + filter.getDataAuthority(z).getHost()); } } } // Do we already have this one? if (!allowFilterResult(filter, dest)) { if (debug) { Slog.v(TAG, " Filter's target already added"); } continue; } match = filter.match(action, resolvedType, scheme, data, categories, TAG); if (match >= 0) { if (debug) Slog.v(TAG, " Filter matched! match=0x" + Integer.toHexString(match) + " hasDefault=" + filter.hasCategory(Intent.CATEGORY_DEFAULT)); if (!defaultOnly || filter.hasCategory(Intent.CATEGORY_DEFAULT)) { final R oneResult = newResult(filter, match, userId); if (oneResult != null) { dest.add(oneResult); if (debug) { dumpFilter(logPrintWriter, " ", filter); logPrintWriter.flush(); filter.dump(logPrinter, " "); } } } else { hasNonDefaults = true; } } else { if (debug) { String reason; switch (match) { case IntentFilter.NO_MATCH_ACTION: reason = "action"; break; case IntentFilter.NO_MATCH_CATEGORY: reason = "category"; break; case IntentFilter.NO_MATCH_DATA: reason = "data"; break; case IntentFilter.NO_MATCH_TYPE: reason = "type"; break; default: reason = "unknown reason"; break; } Slog.v(TAG, " Filter did not match: " + reason); } } } if (hasNonDefaults) { if (dest.size() == 0) { Slog.w(TAG, "resolveIntent failed: found match, but none with CATEGORY_DEFAULT"); } else if (dest.size() > 1) { Slog.w(TAG, "resolveIntent: multiple matches, only some with CATEGORY_DEFAULT"); } } }
4. 若匹配成功则把IntentFilter转化为ResolveInfo对象,添加到结果List<ResolveInfo>中。
三、 全局搜索Intent
调用了IntentResolver的queryIntent。为了加快Intent全局搜索,保存IntentFilter时就保存了多个成员变量,如mTypeToFilter、mSchemeToFilter等,见第一部分分析,queryIntent的主要逻辑就是根据请求的Intent信息,去相应的成员变量中获取IntentFilter,并保存在F[]变量中,然后调用buildResolveList进行匹配,buildResolveList上面已经介绍过了。
例如,Intent中信息为type为abc/xyz scheme为content。那么,首先会查看mTypeToFilter中键为“abc/xyz”的IntentFilter值,并保存在firstTypeCut中,因为mTypeToFilter是保存了所有Type值,然后继续查找mWildTypeToFilter中包含“abc”的IntentFilter,并保存在secondTypeCut中,然后在调用buildResolveList进行逐个匹配。
public List<R> queryIntent(Intent intent, String resolvedType, boolean defaultOnly, int userId) { String scheme = intent.getScheme(); ArrayList<R> finalList = new ArrayList<R>(); 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. 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). 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. if (resolvedType == null && scheme == null && intent.getAction() != null) { firstTypeCut = mActionToFilter.get(intent.getAction()); if (debug) Slog.v(TAG, "Action list: " + Arrays.toString(firstTypeCut)); } FastImmutableArraySet<String> 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); } sortResults(finalList); if (debug) { Slog.v(TAG, "Final result list:"); for (int i=0; i<finalList.size(); i++) { Slog.v(TAG, " " + finalList.get(i)); } } return finalList; }
至此,PKMS查询Intent已介绍完成。
- PackageManagerService Intent匹配查询
- Android7.0 PackageManagerService (4) Intent匹配Activity的过程
- 深入分析Intent匹配查询
- Intent匹配
- PackageManagerService
- Intent Filter匹配
- Intent Filter匹配
- Intent Filter匹配
- 转:Intent Filter匹配
- Intent Filter匹配
- Intent Filter匹配
- Intent Filter匹配
- Intent 匹配规则
- Intent Filter匹配规则
- Intent匹配原理
- Android Intent匹配解析
- Intent Filter匹配规则
- Intent匹配和解析
- luogu1091【2004提高】合唱队形(dp)
- NYOJ 42-一笔画问题(判断欧拉回路)
- python 字符串转为时间戳
- 事件嵌套导致的bug
- Java面试知识点第一天
- PackageManagerService Intent匹配查询
- javaweb服务调优
- Lua备忘
- python 匿名函数
- 纯数组,实现简单模拟酒店管理系统
- memcached 与 redis的不同及其实现
- php第三天-整数,str,class 等各种类型
- 关于sleep()、yield()、wait()三种让线程暂停方法的区别
- 0与1