首页 > 代码库 > 深入分析Intent匹配查询
深入分析Intent匹配查询
尊重原创:http://blog.csdn.net/yuanzeyao/article/details/42243583
在前面的一篇文章中,我们详细分析了PackageManagerService的启动过程(在后面的文章中,为了方便,我会将PackageManagerService简称PMS),PMS在启动的过程中,会去扫描系统app和用户安装的app,然后将这些app的信息保存到一些数据结构中,在这篇文章中,我们会接着前面一篇文章继续分析Intent匹配查询过程,如果对PMS不是很熟悉的同学建议先去阅读前面一篇文章PackageManagerService启动过程分析。
作为一名Android App开发着,我相信你对Intent的使用是再熟悉不过了,例如我想在一个Activity中启动另外一个Activity,会使用如下代码:
Intent intent=new Intent(this,SecondActivity.class); this.startActivity(intent);
以上方式称为显示Intent调用,当然有些时候我们会使用隐式Intent,例如:
Intent intent=new Intent("com.android.demo"); this.startActivity(intent);
由于Intent的使用非常简单,所以在这里我不想再去花太多时间去描述它了,我们这里是要从源码的角度去理解通过Intent是如何匹配Acitivity的(Service,Receiver原理也是差不多的)。
我们直接从startActivity函数开始吧(提示:我使用的是4.1源码,不同版本的源码会有些出入),在这里,先给出一张时序图,然后跟着时序图看源码。
图1-1
根据图1-1,当我们调用Activity的startActivity方法时,其实调用的就是调用ContextImpl的startActivity方法
public void startActivity(Intent intent, Bundle options) { if ((intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) == 0) { throw new AndroidRuntimeException( "Calling startActivity() from outside of an Activity " + " context requires the FLAG_ACTIVITY_NEW_TASK flag." + " Is this really what you want?"); } mMainThread.getInstrumentation().execStartActivity( getOuterContext(), mMainThread.getApplicationThread(), null, (Activity)null, intent, -1, options); }
在ContextImple的startActivity方法中,会调用Instrumentation的execStartActivity方法,这个方法我就不贴出源码了,它里面其实就是调用了ActivityManagerService的startActivity方法,这个方法里面其实就是调用了ActivityStack的startActivityMayWait方法,该方法又调用了自身的resolveActivity方法,最后调用了PMS的resolveIntent方法了,到这里终于见到了PMS了,在resolveIntent方法里面就是调用了自身的queryIntentActivities方法,queryIntentActivities会返回一个ActivityInfo对象,我们知道一个ActivityInfo对象就是一个Activity的档案对象,记录了一个Acitivity的所有的信息。这里给出queryIntentActivities的源码
public List<ResolveInfo> queryIntentActivities(Intent intent, String resolvedType, int flags, int userId) { if (!sUserManager.exists(userId)) return null; 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); 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(); if (pkgName == null) { return mActivities.queryIntent(intent, resolvedType, flags, userId); } final PackageParser.Package pkg = mPackages.get(pkgName); if (pkg != null) { // C return mActivities.queryIntentForPackage(intent, resolvedType, flags, pkg.activities, userId); } return new ArrayList<ResolveInfo>(); } }对于上面的代码,可以看出:
如果Intent 指明了Componet,那么直接通过Componet就可以找到ActivityInfo
如果指定了packagename,那么可以通过packagename找到Package,然后通过Package包含的Activities中进行匹配
如果都不满足,那么需要全系统进行匹配。
写到这里,我们有必要对上一篇文章中的一些重要数据结构进行回忆。
回忆1:PackageManagerService中有两个scanPackageLI,第一个scanPackageLI的第一个参数是File,它的工作就是将指定的文件(apk)的AndroidManifest.xml文件解析成PackageParser.Package对象。我们看看这个对象有哪些字段吧
图1-2
这里我仅仅列出了比较重要的字段,相信大家看了就会明白,Package里面存储了一个apk中的所有信息,其中包括所有的Activity,所有的Service等等。并且在PMS中有一个HashMap保存了所有的Pacakge,其中key就是包名
回忆2:在第二个scanPackageLI中,会将指定Package中的一些信息进行公有化,例如会将activities中的所有Activity加入ActivityIntentResolver类型的变量mActivities变量中。注意这里说的Activity和我们平时用的Activity不是一个类型,它的继承结构如下:
图1-3
回忆3:在scanPackageLI中,通过调用ActivityIntentResolver的addActivity方法,将指定的PackageParser.Activity保存起来,我们看看addActivity做了什么。
/** * @param a * 要被保存的Activity * @param type * "activity" or "recevier" */ public final void addActivity(PackageParser.Activity a, String type) { final boolean systemApp = isSystemApp(a.info.applicationInfo); //保存到一个HashMap中 mActivities.put(a.getComponentName(), a); final int NI = a.intents.size(); //遍历Activity中所有的IntentFilter,然后调用addFilter方法进行保存 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); } }
逻辑比较简单,直接进入addFilter函数,看看做了些什么
public void addFilter(F f) { 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: "); } }
其中mFilters是一个HashSet类型变量,这个方法首先将ActivityIntentInfo类型变量保存到mFilters中,接着调用了register_intent_filter方法
private final int register_intent_filter(F filter, Iterator<String> i, HashMap<String, ArrayList<F>> 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); ArrayList<F> array = dest.get(name); if (array == null) { //Slog.v(TAG, "Creating new array for " + name); array = new ArrayList<F>(); dest.put(name, array); } array.add(filter); } return num; }
在看代码之前,需要熟悉这里的数据结构,filter就相当于一个IntentFilter,i 是一个迭代器,通过它我们可以遍历filter所有的scheme,dest就是一个HashMap,key是filter的scheme,值就是一个ArrayList<F>,其实就是通过遍历一个IntentFilter的所有scheme,根据这个scheme找到对应的ArrayList<F>,然后将这个Filter放入ArrayList<F>,然后返回scheme的个数。
现在回到addFilter方法,接着会带调用register_mime_types方法,同样,看看这个方法做了什么
private final int register_mime_types(F filter, String prefix) { final Iterator<String> i = filter.typesIterator(); if (i == null) { return 0; } int num = 0; while (i.hasNext()) { String name = i.next(); num++; if (localLOGV) Slog.v(TAG, prefix + name); String baseName = name; final int slashpos = name.indexOf(‘/‘); if (slashpos > 0) { baseName = name.substring(0, slashpos).intern(); } else { name = name + "/*"; } ArrayList<F> array = mTypeToFilter.get(name); if (array == null) { //Slog.v(TAG, "Creating new array for " + name); array = new ArrayList<F>(); mTypeToFilter.put(name, array); } array.add(filter); if (slashpos > 0) { array = mBaseTypeToFilter.get(baseName); if (array == null) { //Slog.v(TAG, "Creating new array for " + name); array = new ArrayList<F>(); mBaseTypeToFilter.put(baseName, array); } array.add(filter); } else { array = mWildTypeToFilter.get(baseName); if (array == null) { //Slog.v(TAG, "Creating new array for " + name); array = new ArrayList<F>(); mWildTypeToFilter.put(baseName, array); } array.add(filter); } } return num; }
这个方法的功能和register_intent_filter方法功能是一样的,只不过register_intent_filter是处理scheme的,这里是处理type的,type的逻辑比scheme复杂。scheme只用了一个mSchemeToFilter存储,而type用了三个,他们分别是:
mWildTypeToFilter:用于保存设置了Data类型“image/*”的IntentFilter,但是设置“image/jpeg”的不算在内
mTypeToFilter:包含了mWildTypeToFilter以及指明了Data类型为确定参数的IntentFilter信息,如“image/jpeg”和“image/*”类型
mBaseTypeToFilter:保存MIME中Base类型的IntentFilter,但是不包含Sub type为"*"的IntentFilter
其实上面回忆的3点都是前面一篇文章的内容,下面就开始研究一下queryIntentActivities的逻辑吧。
public List<ResolveInfo> queryIntentActivities(Intent intent, String resolvedType, int flags, int userId) { if (!sUserManager.exists(userId)) return null; 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); 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(); if (pkgName == null) { return mActivities.queryIntent(intent, resolvedType, flags, userId); } final PackageParser.Package pkg = mPackages.get(pkgName); if (pkg != null) { return mActivities.queryIntentForPackage(intent, resolvedType, flags, pkg.activities, userId); } return new ArrayList<ResolveInfo>(); } }
这部分代码逻辑其实也不算复杂,通过Intent拿到ComponetName,如果ComponetName不为null(说明使用的是显示调用),那么通过调用getActivityInfo方法拿到ActivityInfo。getActivityInfo其实就是到mActivities里面根据ComponetName拿到PackageParser.Activity对象,并通过调用PackageParser.generateActivityInfo方法将PackageParser.Activity对象变为ActivityInfo对象。如果ComponetName为null(隐式调用),那么就要分为两种情况:
第一种情况:通过intent拿到包名为Null,那么调用ActivityIntentResolver的queryIntent方法
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); }
代码很少,调用了IntentResolver的queryIntent,直接看queryIntent的源码吧
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 + " of intent " + intent); ArrayList<F> firstTypeCut = null; ArrayList<F> secondTypeCut = null; ArrayList<F> thirdTypeCut = null; ArrayList<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: " + firstTypeCut); secondTypeCut = mWildTypeToFilter.get(baseType); if (debug) Slog.v(TAG, "Second type cut: " + secondTypeCut); } else { // We can match anything with our base type. firstTypeCut = mBaseTypeToFilter.get(baseType); if (debug) Slog.v(TAG, "First type cut: " + firstTypeCut); secondTypeCut = mWildTypeToFilter.get(baseType); if (debug) Slog.v(TAG, "Second type cut: " + 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: " + 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: " + 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: " + 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: " + 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 (R r : finalList) { Slog.v(TAG, " " + r); } } return finalList; }
这个函数看起来很复杂,但是逻辑很简单,我在这里简单的描述一下。
首先如果给定的Intent包含MIME,就到上面(mTypeToFilter,mWildTypeToFilter,mBaseTypeToFilter)里面匹配符合条件的IntentFilter,将结果分别保存到firstTypeCut,secondTypeCut,thirdTypeCut中,然后根据scheme进行匹配,将结果保存到schemeCut,最后调用buildResolveList方法,将action,scheme,categories等因素随后匹配,将结果保存到finalList中去,最后对finalList进行排序。这种情况分析完了。
第二种情况:如果intent中的包名不为Null,根据包名拿到PackageParser.Package对象,调用ActivityIntentResolver的queryIntentForPackage方法即可,此方法中遍历PackageParsr.Package中的mactivities对象,将每个PackageParser.Activity中的所有IntentFilter加入listCut(一个ArrayList)中,然后调用IntentResolve的queryIntentFromList方法,在queryIntentFromList方法中,根据给定的Intent的action,categories,scheme,type等信息匹配listCut中的IntentFilter对象。
好了关于Intent的匹配过程就写到这里。
深入分析Intent匹配查询