- 浏览: 503455 次
文章分类
最新评论
-
偶然——相逢:
这个要将arcigs升级到10吗?
ArcGIS for Android 1.1 MapView 的Activity退出时整个程序崩溃问题 -
偶然——相逢:
还是有问题哦
ArcGIS for Android 1.1 MapView 的Activity退出时整个程序崩溃问题 -
JThink:
怎么没用阿
ubuntu10.04安装JDK5 -
JThink:
则呢吗没用阿
ubuntu10.04安装JDK5 -
lanting:
在ExtJs4中,引入脚本库时,Ext-base.js是否不需 ...
myeclipse搭建extjs4.0.7
Intent匹配规则以及解析框架深入分析
本文原创 ,转载必须注明出处 :http://blog.csdn.net/qinjuning
前言:本文博客是公司培训之间的PPT整理而成,在此把相关的细节发布出来,希望对大家能有所帮助。
关于Intent以及IntentFilter的基本知识,大家可以参阅如下资料,
SDK中对Intent与IntentFilter的介绍 ---- 英文
其中文翻译如下:
Android开发之旅: Intents和Intent Filters(理论部分)
我重点分析一下两个方面:
第一部分、Intent以及IntentFilter说明以及匹配规则分析
第一部分 、Intent以及IntentFilter说明以及匹配规则分析
想当初我看Intent相关知识时,对Intent、IntentFilter的理解就很差劲,总觉得系统定义了一个Intent,为何还要整理个
IntentFilter出来"祸害"广大程序猿呢?但不解归不解,在具体使用咱可不能含糊,于是只好依葫芦画瓢了,反正绝对还不错。
一、温故而知新 :Intent与IntentFilter两问。
二、它们之间的关系是如何呢?
public class Intent implements Parcelable, Cloneable { private String mAction; //action值 private Uri mData; //uri private String mType; //MimeType private String mPackage; //所在包名 private ComponentName mComponent; //组件信息 private int mFlags; //Flag标志位 private HashSet<String> mCategories; //Category值 private Bundle mExtras; //附加值信息 //... }
IntentFilter类源码(部分) 路径位于:\frameworks\base\core\java\android\content\IntentFilter.java
public class IntentFilter implements Parcelable { //... //保存了所有action字段的值 private final ArrayList<String> mActions; //保存了所有Category的值 private ArrayList<String> mCategories = null; //保存了所有Schema(模式)的值 private ArrayList<String> mDataSchemes = null; //保存了所有Authority字段的值 private ArrayList<AuthorityEntry> mDataAuthorities = null; //保存了所有Path的值 private ArrayList<PatternMatcher> mDataPaths = null; //保存了所有MimeType的值 private ArrayList<String> mDataTypes = null; //... }
PS :大家可以参详下Intent与IntentFilter类中不同字段的属性类型。Intent中属性类型基本上都是单个类型的,而IntentFilter
属性都是集合类型的。从这方面思考,更可以加深我们的理解。
三、Intent匹配规则
匹配种类有如下三种:
比较好理解的是,进行匹配时Intent携带的Action字段值和Category字段值必须包含在IntentFilter中,否则匹配失败。
SDK中说明的具体规则如下:
PS :可别说我不会总结出来给大家分享,其实我觉得很多知识都需要自己去尝试,去努力吸收,只要经过自己的消化,
学到的知识就是自己的了。
上面的规则比较生硬吧。我们去源码中去看看Intent与IntentFilter的具体匹配方法吧。
该方法是IntentFilter中的match()方法,该方法的内部处理逻辑就是按照上面的规则去判断的,大家可以仔细体味下,该方法
我们在后面讲到Intent解析过程时也会用到。 具体逻辑在代码中进行了说明。
public class IntentFilter implements Parcelable { //匹配算法,,按照匹配规则进行 //Intent与该IntentFilter进行匹配时调用该方法参数表示Intent的相关属性值 public final int match(String action, String type, String scheme, Uri data, Set<String> categories, String logTag){ //首先、匹配Action字段 if (action != null && !matchAction(action)) { if (Config.LOGV) Log.v( logTag, "No matching action " + action + " for " + this); return NO_MATCH_ACTION; } //其次、匹配数据(Uri和MimeType)字段 int dataMatch = matchData(type, scheme, data); //... //最后,匹配Category字段值 String categoryMatch = matchCategories(categories); //... } //... }
第二部分、 Intent的解析过程分析
一、引入PackageManager
我们知道Android源码总是贴心的(不知道有没有10086贴心),它对外提供了很多借口供应用程序调用,例如AudioManger
(音频管理)、TelephoneManger(电话管理)、同样也提供了一个包管理-----PackageManager,通过它我们可以、获取应用
程序包得信息,例如图标、Lable标签等。具体关于PackageManager的使用,可以参考我的另外一篇文章 :
<<Android中获取应用程序(包)的信息-----PackageManager的使用(一)>>
二、PackageManagerService ---- 重量级选手
前面所说PackageManager不过是个傀儡,所有相关的操作都是由PackageManagerService 完成的。这儿我们简单的
分析下PackageManagerService 的特性:
①、开机就启动,由SystemServer进程启动 ;
②、启动后它会扫描系统中所有应用程序Apk包下的AndroidManifest.xml文件,然后解析所有的
AndroidManifest.xml文件,继而形成一个庞大的信息结构树,并且保存在PackageManagerService 的相关属性下。
它会扫描这两个目录:
/system/app------------------> 系统应用程序
/data/app ------------------> 第三方应用程序(所有安装的Apk包都会在该目录下保存一份拷贝)
扫描完成后,于是所有的信息结构就构建了。PackageManagerService 的四个重要属性如下:
class PackageManagerService extends IPackageManager.Stub { //... //保存了所有Activity节点信息 。 自定义类 // All available activities, for your resolving pleasure. final ActivityIntentResolver mActivities = new ActivityIntentResolver(); //保存了所有BroadcastReceiver节点信息 。 自定义类 // All available receivers, for your resolving pleasure. final ActivityIntentResolver mReceivers = new ActivityIntentResolver(); //保存了所有Service节点信息。 。 自定义类 // All available services, for your resolving pleasure. final ServiceIntentResolver mServices = new ServiceIntentResolver(); //保存了所有ContentProvider节点信息 , 以Hash值保存 // Keys are String (provider class name), values are Provider. final HashMap<ComponentName, PackageParser.Provider> mProvidersByComponent = new HashMap<ComponentName, PackageParser.Provider>(); //... }
@Override public PackageManager getPackageManager() { //... // Doesn't matter if we make more than one instance. return (mPackageManager = new ApplicationPackageManager(this, pm)); //... } class ContextImpl extends Context { //... /*package*/ static final class ApplicationPackageManager extends PackageManager{ //... @Override public ActivityInfo getActivityInfo(ComponentName className, int flags){ //... } } //... }
1、保存数据采用的数据结构
作用:保存了每个<intent-filter>节点信息
ActivityIntentInfo类:继承至IntentInfo类
作用:保存了<activity />节点下的< intent-filter>节点信息
ServiceIntentInfo:继承至IntentInfo类
作用:保存了<service />节点下的< intent-filter >节点信息
Activity类:保存了<activity />节点信息
Service类:保存了<service />节点信息
PS:这些都是PackageParser类的内部类 。PackageParser的主要功能就是解析AndroidManifest.xml文件
IntentResolver类:模板类,父类,保存了<activity/>、<service/>、<receiver />节点的共同信息。
ActivityIntentResolver类:继承至IntentResolver类。
作用:保存了所有<activity/>或者<receiver/>节点信息。(Activity或者BroadcastReceiver信息就是用该自定义类保存的)
ServiceIntentResolver类:继承至IntentResolver类,保存了
作用:保存了所有<service/>节点信息。(Service信息就是用该自定义类保存的)。
一个简单的UML图表示如下:
2、Intent解析采用的算法
不同的数据结构决定了不同的算法,而不同的算法又决定着性能,例如时间复杂度以及空间复杂度等。 在具体讲解解析
采用的算法时,我们先理解下这个情景。
假设一个女人决定参加一个相亲节目(大家可以理解成《非诚勿扰》),然后她向主办方提出如下条件:
1、身高 ?175cm以上 ;
2、 财富 ?100万;
3、 学历 ?本科以上 ;
……
主办发经理一看,你丫的要求还真高。但客户是万能的,该经理也只能去找到满足这些条件的男人咯。
最开始,该经理是这么想的,我把所有男的都给遍历一遍,肯定把满足这些条件的男人给揪出来。他找啊找,觉得这么找下去
是不是太二B了(呵呵,你也是这么想的吗?)。经理就开始想:“我为什么不能根据这些条件把所有男的给分成三个种群呢?有钱
的男人在一起,高个子的男人在一起,高学历的男人在一起,这样查找起来不是更快吗 ? “
于是,有了如下的划分: PS, 你是属于哪一类额 ?
二B算法(没有分类) 高级点的算法(分类后)
可能大家对这种根据关键值分类的好处不能一目了然。我们举个一般例子吧:
假设当前共有100个男的。 其中有钱的有20人,高个子有30人,高学历男人有10人。
根据第一种算法分类,我们需要比较100次,而第二种算法我们总共只需要比较60次。从整个基数来分析,算法肯定优化了
最后,对不同的分类中查询的结果进行组合重新排列下,即可得到我们的满足该女性的要求。
同样的,在进行Intent匹配时,Android也采用了第二种方法来进行算法匹配。它根据一些关键值Action、MimeType、
Schema字段去进行分类。分类之后的集合大致如下:
于是在进行具体匹配时,我们只是需要根据关键值从不同集合中获取即可。
事实上,由于MimeType的通配符(*)的特性,它的匹配可以说是最难的。参考IntentResolver类,真正的关键值如下:
//模板类 F类型可能为ActivityIntentInfo或ServiceIntentInfo,R对象是ResolverInfo类 public class IntentResolver<F extends IntentFilter, R extends Object> { //保存了所有<intent-filter>节点信息 //All filters that have been registered. private final HashSet<F> mFilters = new HashSet<F>(); /** All of the MIME types that have been registered, such as "image/jpeg", * "image/*", or "{@literal *}/*". */ //关键值表示MimeType形如: image/jpeg 、 image/*、/* 类型 private final HashMap<String, ArrayList<F>> mTypeToFilter /** * 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. */ //关键值表示MimeType形如:image、 image/*、* 类型 private final HashMap<String, ArrayList<F>> mBaseTypeToFilter /** * 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. */ //这个关键字段表示MimeType形如 :image、* 类型 private final HashMap<String, ArrayList<F>> mWildTypeToFilter //All of the URI schemes (such as http) that have been registered. //关键值字段表示Schema private final HashMap<String, ArrayList<F>> mSchemeToFilter /** * All of the actions that have been registered, but only those that did * not specify data. */ //关键值字段表示:Action private final HashMap<String, ArrayList<F>> mActionToFilter //All of the actions that have been registered and specified a MIME type. //关键值字段表示:Action和MimeType。 即该<intent-filter>节点必须包含action和MimeType private final HashMap<String, ArrayList<F>> mTypedActionToFilter }
于是,通过这些关键字段我们可以去特定集合去查找,最后将结果在重新组合下,那不就万事大吉了。
最后,我们通过代码走读的方式,以一个查询Activity的信息的方法,带领大家去熟悉具体流程。该方法原型为:
//通过给定的intent,查询所有匹配的Activity组件信息
abstract List<ResolveInfo> queryIntentActivities(Intent intent, int flags)
PS:其实查询Activity、Service、BroadcastReceiver的流程基本上是相同的。
Step 1、获取PackageManager代理对象,调用该方法:
PackageManager mPackageManger = this.getPackageManager() ; //为了说明,这儿我们简单查询一个Intent对象,即所有应用程序的启动Activity Intent mainIntent = new Intent() ; mainIntent.setAction(Intent.ACTION_MAIN); mainIntent.addCategory(Intent.CATEGORY_LAUNCHER); mPackageManger.queryIntentActivities(mainIntent, 0); //获取PackageManager对象 @Override public PackageManager getPackageManager() { //... IPackageManager pm = ActivityThread.getPackageManager(); if (pm != null) { // Doesn't matter if we make more than one instance. return (mPackageManager = new ApplicationPackageManager(this, pm)); } }
Step 2、该PackageManager代理对象实则为ApplicatonPackageManager 对象,该对象是ContextIml的内部类。
@Override public List<ResolveInfo> queryIntentActivities(Intent intent, int flags) { try { //mPM对象就是PackageManagerService的客户端 return mPM.queryIntentActivities( intent, intent.resolveTypeIfNeeded(mContext.getContentResolver()), flags); } catch (RemoteException e) { throw new RuntimeException("Package manager has died", e); } }
Step 3、调用服务端PackageManagerService对象的对应方法。该方法位于PackageManagerService.java类中
public List<ResolveInfo> queryIntentActivities(Intent intent, String resolvedType, int flags) { //是否设置了组件ComponetName 信息 ComponentName comp = intent.getComponent(); if (comp != null) { List<ResolveInfo> list = new ArrayList<ResolveInfo>(1); ActivityInfo ai = getActivityInfo(comp, flags); if (ai != null) { ResolveInfo ri = new ResolveInfo(); ri.activityInfo = ai; list.add(ri); } return list; } synchronized (mPackages) { //是否设置了包名 String pkgName = intent.getPackage(); if (pkgName == null) { //调用mActivities去查询 return (List<ResolveInfo>)mActivities.queryIntent(intent, resolvedType, flags); } PackageParser.Package pkg = mPackages.get(pkgName); if (pkg != null) { return (List<ResolveInfo>) mActivities.queryIntentForPackage(intent, resolvedType, flags, pkg.activities); } return null; } }
首先、该方法判断IntentComponentName是否存在,如果存在则为显示匹配了,直接返回特定组件相关信息;
其次、判断是否设置了包名,即packageName,如果没有指定包名,则查询所有的应用程序包去找匹配的组件信息。如果
指定了packageName,则去指定包下去查找;
接着,调用特定的类继续查找。由于我们找的是Activity组件信息,因此去ActivityIntentResolver类去查找。
Step 4、调用mActivities自定义类去查找
//调用父类IntentResolver方法去查找 public List queryIntent(Intent intent, String resolvedType, int flags) { mFlags = flags; return super.queryIntent(intent, resolvedType, (flags&PackageManager.MATCH_DEFAULT_ONLY) != 0); }
该过程只是简单的调用了父类IntentResolver去查找。
Step5 、进入IntentResolver类去真正的实现查找,该方法为于IntentResolver.java类中。
public List<R> queryIntent(Intent intent, String resolvedType, boolean defaultOnly) { String scheme = intent.getScheme(); //用来保存查找到的组件信息,如Activity等 ArrayList<R> finalList = new ArrayList<R>(); //根据关键值去特定集合查询到的一个可能结果 ArrayList<F> firstTypeCut = null; ArrayList<F> secondTypeCut = null; ArrayList<F> thirdTypeCut = null; ArrayList<F> schemeCut = null; //首先是否制定的数据类型 MimeType // 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("*")) { //匹配特定的MimeType if (resolvedType.length() != slashpos+2|| resolvedType.charAt(slashpos+1) != '*') { firstTypeCut = mTypeToFilter.get(resolvedType); secondTypeCut = mWildTypeToFilter.get(baseType); } //... } } //根据模式去匹配特定的集合 if (scheme != null) { schemeCut = mSchemeToFilter.get(scheme); } //可能的话在去匹配Action所在集合 if (resolvedType == null && scheme == null && intent.getAction() != null) { firstTypeCut = mActionToFilter.get(intent.getAction()); } //对我们前面通过关键字查询的一个集合,在此循环遍历匹配,将匹配到的结果保存在finalList集合中 if (firstTypeCut != null) { buildResolveList(intent, debug, defaultOnly, resolvedType, scheme, firstTypeCut, finalList); } if (secondTypeCut != null) { buildResolveList(intent, debug, defaultOnly, resolvedType, scheme, secondTypeCut, finalList); } if (thirdTypeCut != null) { buildResolveList(intent, debug, defaultOnly,resolvedType, scheme, thirdTypeCut, finalList); } if (schemeCut != null) { buildResolveList(intent, debug, defaultOnly, resolvedType, scheme, schemeCut, finalList); } //根据IntentFilter的一些优先级进行排序 sortResults(finalList); return finalList; }
buildResolveList()方法的主要作用是将可能的集合在循环遍历,将匹配的结果值保存在finalList集合中。
该方法为于IntentResolver.java类中,方法原型如下:
//通过前面关键字查找的可能集合,循环遍历进行匹配,匹配成功就加入到dest集合中,即finalList集合中 private void buildResolveList(Intent intent, boolean debug, boolean defaultOnly, String resolvedType, String scheme, List<F> src, List<R> dest) { Set<String> categories = intent.getCategories(); final int N = src != null ? src.size() : 0; boolean hasNonDefaults = false; int i; for (i=0; i<N; i++) { F filter = src.get(i); int match; //是否已经加入到匹配结果中去了,不允许重复添加 // Do we already have this one? if (!allowFilterResult(filter, dest)) { continue; } //调用Intent-filter方法去匹配该Intent信息 match = filter.match( intent.getAction(), resolvedType, scheme, intent.getData(), categories, TAG); //匹配成功,就存放在finalList集合中 if (match >= 0) { if (!defaultOnly || filter.hasCategory(Intent.CATEGORY_DEFAULT)) { //调用子类的newResult()方法去返回一个ResolvInfo对象 final R oneResult = newResult(filter, match); if (oneResult != null) { dest.add(oneResult); } } else { hasNonDefaults = true; } } else { //... } } //... }
这个函数的逻辑判断如下:
首先、通过给定的关键字去特定集合查询一个可能的匹配集合,然后将这些集合信息保存在如下集合中:
ArrayList<F>firstTypeCut =null;
ArrayList<F>secondTypeCut =null;
ArrayList<F>thirdTypeCut =null;
ArrayList<F>schemeCut =null;
然后、连续四次调用buildResolveList()去进行匹配。每次调用结束后,参数finalList保存的是匹配结果的累加值,所以这
四次调用过程中finalList集合包含的结果是一次累加的过程。当然了,四次连续调用buildResolveList()的次序可以不分先后。
最后、调用sortResults()从匹配集合中进行一些排序等。
总结:第二部分通过重点介绍了PackageManagerService的功能匹配Intent采用的数据结构和算法,也只是简单入了下门,希
望大家能参考源码,认真理解Intent匹配过程。
相关推荐
### Android Intent原理深入解析 #### 一、概览与引言 在移动开发领域,特别是在Android平台上,**Intent**作为核心通信机制之一,扮演着关键角色。它不仅用于应用程序内部不同组件之间的通信,也是实现跨应用通信...
本篇将深入解析"Android应用源码之Intent_DataSample_应用",探讨Intent在数据传递中的应用。 首先,Intent分为显式Intent和隐式Intent。显式Intent通过指定组件的全限定类名来直接启动目标组件,而隐式Intent则是...
通过分析这个毕业设计项目,你可以了解到Intent如何在Android应用中扮演关键角色,如何实现不同组件间的通信,以及如何利用Intent进行数据传输和事件响应。对于初学者来说,这是一个很好的实践平台,可以加深对...
《Android应用框架原理与程序设计36技》是一本深入探讨Android系统应用开发的专业书籍,其随书源码实例完整版rar文件包含了丰富的代码示例,帮助读者更好地理解和实践书中所讲解的技术。这本书主要涵盖了Android应用...
源码中展示了Intent的构造、解析和发送流程,以及IntentFilter的匹配逻辑。理解这部分内容能提升我们对Android组件通信的理解,从而编写更灵活、高效的代码。 七、项目结构与模块化设计 AndroidAppCodeFramework-...
这篇文章将深入解析这些概念,并通过源码分析和实用工具来帮助开发者更好地理解和使用。 **Activity**: Activity是Android中的用户界面组件,它代表了用户能看到和交互的屏幕。一个App可以有多个Activity,每个...
5. **Activity与Intent**:阐述Activity的启动过程、生命周期和Intent的作用,以及IntentFilter的匹配规则。 6. **Service**:探讨Service的启动、绑定和服务组件间的交互,以及后台服务的管理。 7. **...
这份"Android应用源码之源代码分析.zip"的压缩包显然包含了关于Android应用源码的详细解析,适合开发者深入理解Android系统的工作原理以及应用程序的运行机制。 1. **Android应用程序结构**: Android应用主要由...
- **Intent匹配查询分析**:详细分析了PackageManagerService如何根据Intent查找匹配的Activity。 - **queryIntentActivities总结**:总结了queryIntentActivities函数的工作原理及其在实际应用中的重要性。 - **...
在这个详细分析中,我们将深入探讨Android Framework的架构、主要组件及其工作原理,帮助开发者更好地理解和利用这个强大的平台。 一、Android Framework概述 Android Framework是基于Java语言构建的,它提供了...
- **权限管理**:可以通过设置URI匹配规则和访问权限来保护数据的安全性。 #### 四、高级特性 1. **Intent Filters**:用于指定接收者能够处理的Intent类型。 2. **Permissions**:为了保护用户的隐私和数据安全...
源码分析可以帮助我们理解Intent的创建、解析和匹配过程,以及如何通过Intent实现组件间的通信。 5. **Service管理**:Service是后台运行的组件,源码可以展示Service的启动、绑定过程,以及如何在Service中执行长...
"Android 上百实例源码分析以及开源分析 4"提供了丰富的学习资源,帮助开发者们深入剖析Android系统的运作机制,以及各种常见功能和组件的实现原理。下面将详细探讨这个资料集中的关键知识点。 1. **Android源码...
3. Intent:Android 15的Intent系统用于应用程序间的交互,源码解析有助于理解Intent的解析、匹配和执行过程。 4. BroadcastReceiver:广播接收器是系统事件通知的重要机制,源码分析能帮助开发者掌握如何注册、...
源码分析可以帮助理解Intent的构造、解析和匹配过程,这对于构建高效的应用交互至关重要。 4. **Service**:后台运行的服务,不提供用户界面。源码中可以学习如何创建和管理Service,以及如何与Activity或其他组件...
查看源码能帮助我们理解Intent如何匹配和解析,以及如何通过BroadcastReceiver进行全局广播。 3. **Service管理**:Service在后台执行任务,不与用户直接交互。源码分析有助于理解Service的启动方式(startService...
#### 二、Activity与Intent深入解析 虽然原文未直接提及Activity与Intent的具体细节,但根据上下文推断,此处应该是指Android框架中两个核心组件——Activity和Intent的作用与使用方式。 **2.1 Activity简介** -...
4. **Intent与IntentFilter**:Intent是Android中对象间通信的主要手段,IntentFilter则是广播接收者和服务注册的关键,源码可以帮助理解其匹配规则和工作流程。 5. **权限管理系统**:Android的权限模型是其安全性...
6. **Intent Filter**:Intent Filter用于匹配Intent,它指定了接收者可以处理的Intent类型。通常在AndroidManifest.xml文件中定义。 7. **Intent Receiver**:Intent Receiver是一种特殊的Broadcast Receiver,...