从Android源码剖析Intent查询匹配的实现

这篇文章主要介绍了从Android源码剖析Intent查询匹配的实现,Intent部分的源码为Java代码,需要的朋友可以参考下

前言
    这篇文章主要是介绍一下Android Intent,并且从Android源码的角度对Intent查询匹配过程进行分析。

Intent介绍
    Intent的中文是“意图”的意思,而意图是一个非常抽象的概念,那么在Android的编码设计中,如何实例化意图呢?因此Android系统明确指定一个Intent可由两方面属性来衡量。

    主要属性:包括Action和Data。其中Action用于表示该Intent所表达的动作意图,Data用于表示该Action所操作的数据。
    次要属性:包括Category、Type、Component和Extras。其中Category表示类别,Type表示数据的MIME类型,Component可用于指定特定的Intent的响应者(例如指定intent为某个包下的某个class类),Extras用于承载其他的信息。

    Android系统中主要有两种类型的Intent,显示Intent(Explicit Intent)和隐式Intent(Implicit Intent)。

    Explicit Intent:这类Intent明确指明了要找哪个Component。在代码中可以通过setClassName或者setComponent来锁定目标对象。
    Implicit Intent:这类Intent不明确指明要启动哪个Component,而是设置Action、Data、Category让系统来筛选出合适的Component。

    接下来,写两个代码示例,来介绍一下Explicit Intent和Implict Inent。首先是Explicit Intent:

   

 private void startExplicitIntentWithComponent() { Intent intent = new Intent(); ComponentName component = new ComponentName("com.example.photocrop", "com.example.photocrop.MainActivity"); intent.setComponent(component); startActivity(intent); } private void startExplicitIntentWithClassName() { Intent intent = new Intent(); intent.setClassName("com.example.photocrop", "com.example.photocrop.MainActivity"); startActivity(intent); } 

    但是,从源码里面去看,发现setClassName也是借助了ComponentName实现了Explicit Intent。源码如下:

 public Intent setClassName(String packageName, String className) { mComponent = new ComponentName(packageName, className); return this; } 

    然后,在给出一个Implict Intent的代码示例。我这里用一个Activity标注一些Intent Filter为例,然后在写一个Intent用于启动它。

   

    在当前应用的AndroidManifest.xml中,给SendIntentType类增加了intent-filter,action的名字为“justtest”,category的名字为“justcategory”。启动该Activity的代码如下:

 private void startImplictIntent() { Intent intent = new Intent(); intent.setAction("justaction"); intent.addCategory("justcategory"); startActivity(intent); } 

    系统在匹配Implict Intent的过程中,将以Intent Filter列出的3项内容为参考标准,具体步骤如下:

  •     首先匹配IntentFilter的Action,如果Intent设置的action不满足IntentFilter的Action,则匹配失败。如果IntentFilter未设定Action或者设定的Action相同,则匹配成功。
  •     然后检查IntentFilter的Category,匹配方法同Action的匹配相同,唯一例外的是当Category为CATEGORY_DEFAULT的情况。
  •     最后检查Data。


Activityi信息的管理
    从上面的分析可以看出,系统的匹配Intent的过程中,首先需要管理当前系统中所有Activity信息。Activity的信息是PackageManagerService在扫描APK的时候进行收集和管理的。相关源码如下:

 // 处理该package的activity信息 N = pkg.activities.size(); r = null; for (i = 0; i 

    上面代码中,有两个比较重要的数据结构,如下图所示。

2015730164435391.png-600 (841×306)

结合代码和上图的数据结构,可知:

    mAcitivitys为ActivityIntentResolver类型,是PKMS的成员变量,用于保存系统中所有与Activity相关的信息。此数据结构内部也有一个mActivities变量,它以ComponentName为key,保存PackageParser.Activity对象。
    从APK中解析得到的所有和Acitivity相关的信息(包括XML中声明的IntentFilter标签)都由PackageParser.Activity来保存。

    前面代码中调用addActivity函数完成了私有信息的公有化。addActivity函数的代码如下:

    

 public final void addActivity(PackageParser.Activity a, String type) { final boolean systemApp = isSystemApp(a.info.applicationInfo); mActivities.put(a.getComponentName(), a); final int NI = a.intents.size(); for (int j = 0; j  0 && "activity".equals(type)) { // 非系统APK的priority必须为0 intent.setPriority(0); } addFilter(intent); } } 

    接下来看一下addFilter函数。函数源码如下:

  

 public void addFilter(F f) { // mFilters保存所有IntentFilter信息 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: "); } } 

    这里又出现了几种数据结构,它们的类似都是ArrayMap,其中F为模板参数。

  •     mSchemeToFilter:用于保存uri中与scheme相关的IntentFilter信息。
  •     mActionToFilter:用于保存仅设置Action条件的IntentFilter信息。
  •     mTypedActionToFilter:用于保存既设置了Action又设置了Data的MIME类型的IntentFilter信息。

    了解了大概的数据结构之后,我们来看一下register_intent_filter的函数实现:

  

 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++; addFilter(dest, name, filter); } return num; } 

    然后又是一个addFilter函数,明显是一个函数重载,我们来看一下这个addFilter的实现:

    

 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 

    其实代码还是很简单的,如果F数组存在,则判断容量,不够则扩容,够的话就找到位置插入。如果F数组不存在,则创建一个容量为2的数组,将0号元素赋值为该filter。

Intent匹配查询分析
    客户端通过ApplicationPackageManager输出的queryIntentActivities函数向PackageManagerService发起一次查询请求,代码如下:

  

 @Override public List queryIntentActivities(Intent intent, int flags) { return queryIntentActivitiesAsUser(intent, flags, mContext.getUserId()); } /** @hide Same as above but for a specific user */ @Override public List queryIntentActivitiesAsUser(Intent intent, int flags, int userId) { try { return mPM.queryIntentActivities( intent, intent.resolveTypeIfNeeded(mContext.getContentResolver()), flags, userId); } catch (RemoteException e) { throw new RuntimeException("Package manager has died", e); } } 

    可以看到,queryIntentActivities的真正实现是在PackageManagerService.java中,函数代码如下:

    

 public List queryIntentActivities(Intent intent, String resolvedType, int flags, int userId) { if (!sUserManager.exists(userId)) return Collections.emptyList(); enforceCrossUserPermission(Binder.getCallingUid(), userId, false, "query intent activities"); ComponentName comp = intent.getComponent(); if (comp == null) { if (intent.getSelector() != null) { intent = intent.getSelector(); comp = intent.getComponent(); } } if (comp != null) { // Explicit的Intent,直接根据component得到对应的ActivityInfo final List list = new ArrayList(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) { // Implicit Intent return mActivities.queryIntent(intent, resolvedType, flags, userId); } final PackageParser.Package pkg = mPackages.get(pkgName); if (pkg != null) { // 指定了包名的Intent return mActivities.queryIntentForPackage(intent, resolvedType, flags, pkg.activities, userId); } return new ArrayList(); } } 

    可以看到,Explicit Intent的实现较为简单,我们重点来看一下Implict Intent实现。Implicit Intent调用了queryIntent方法,我们来看一下queryIntent的实现代码:

  

 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); } 

    继续跟踪到IntentResolver.java的queryIntent方法,源码如下:

    

 public List queryIntent(Intent intent, String resolvedType, boolean defaultOnly, int userId) { String scheme = intent.getScheme(); ArrayList finalList = new ArrayList(); // 最多有4轮匹配操作 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); secondTypeCut = mWildTypeToFilter.get(baseType); } else { // We can match anything with our base type. firstTypeCut = mBaseTypeToFilter.get(baseType); secondTypeCut = mWildTypeToFilter.get(baseType); } // Any */* types always apply, but we only need to do this // if the intent type was not already */*. thirdTypeCut = mWildTypeToFilter.get("*"); } 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 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); 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; } 

    具体的查询匹配过程是由buildResolveList函数完成了。查询的匹配实现我就不贴代码了,大家自己去查询看就好了。

   

以上就是从Android源码剖析Intent查询匹配的实现的详细内容,更多请关注0133技术站其它相关文章!

赞(0) 打赏
未经允许不得转载:0133技术站首页 » Java