关于Intent以及IntentFilter的基本知识,你们能够参阅以下资料,html
SDK中对Intent与IntentFilter的介绍 ---- 英文java
其中文翻译以下:
android
我重点分析一下两个方面:算法
第一部分 、Intent以及IntentFilter说明以及匹配规则分析数据结构
第二部分:Intent的解析过程分析app
想当初我看Intent相关知识时,对Intent、IntentFilter的理解就不好劲,总以为系统定义了一个Intent,为什么还要整理个数据结构和算法
IntentFilter出来"祸害"广大程序猿呢?但不解归不解,在具体使用咱可不能含糊,因而只好依葫芦画瓢了,反正绝对还不错。ide
* 它们是什么 ?函数
* 它们的区别在哪儿 ?源码分析
事实上,这两个问题能够概括为Intent和Intent的主要功能是什么 ? 你们能够先扪心自问下,看看你的掌握程度如何 ?
个人理解以下:
* Intent : 主要功能是根据特定的条件找到匹配的组件,继而对该组件执 行一些操做。好比执行startActivity()时,系统
首先要找到特定的Activity组件,而后执行onCreate()方法;startService()也得先找的特定的Service组件,而后执行
onCreate()或者onStart()方法 。
* IntentFilter :主要功能是为某个组件向系统注册一些特性(固然一个组件能够注册多个IntentFilter),以便Intent找到对应
的组件。
经过前面对Intent以及IntentFilter的分析,咱们很容易在语意上得出它们实际上是个先后关系。
* IntentFilter在前:任何一个组件必须先经过IntentFilter注册。
* Intent 在后 :根据特定信息,找到以前以及注册过的组件。
源码分析:
Intent类源码(部分) 路径位于:\frameworks\base\core\java\android\content\Intent.java
[java] view plaincopyprint?
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
[java] view plaincopyprint?
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
属性都是集合类型的。从这方面思考,更能够加深咱们的理解。
匹配种类有以下三种:
* 动做(Action)检测
* 种类(Category)检测
* 数据(Data & MimeType)检测
比较好理解的是,进行匹配时Intent携带的Action字段值和Category字段值必须包含在IntentFilter中,不然匹配失败。
SDK中说明的具体规则以下:
* 一个Intent对象既不包含URI,也不包含数据类型 ; 仅当过滤器也不指定任何URIs和数据类型时,才能经过检测;
不然不能经过。
* 一个Intent对象包含URI,但不包含数据类型:仅当过滤器也不指定数据类型,同时它们的URI匹配,才能经过检测。
例如,mailto:和tel:都不指定实际数据。
* 一个Intent对象包含数据类型,但不包含URI:仅当过滤也只包含数据类型且与Intent相同,才经过检测。
* 一个Intent对象既包含URI,也包含数据类型(或数据类型可以从URI推断出) ; 数据类型部分,只有与过滤器中之一
匹配才算经过;URI部分,它的URI要出如今过滤器中,或者它有content:或file: URI,又或者过滤器没有指定URI。
换句话说,若是它的过滤器仅列出了数据类型,组件假定支持content:和file: 。
PS :可别说我不会总结出来给你们分享,其实我以为不少知识都须要本身去尝试,去努力吸取,只要通过本身的消化,
学到的知识就是本身的了。
上面的规则比较生硬吧。咱们去源码中去看看Intent与IntentFilter的具体匹配方法吧。
该方法是IntentFilter中的match()方法,该方法的内部处理逻辑就是按照上面的规则去判断的,你们能够仔细体味下,该方法
咱们在后面讲到Intent解析过程时也会用到。 具体逻辑在代码中进行了说明。
[java] view plaincopyprint?
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以及IntentFilter的主要职能
二、Intent与IntentFilter的关系
三、匹配规则说明
在继续看本部分以前,但愿您最好对PackageManagerService-----程序包管理服务有必定的认知(没有也是OK的咯)。
能够参考下面这篇问题去看看PackageManagerService的功能和相关流程。
老罗的博客:<<Android应用程序安装过程源代码分析>>
咱们知道Android源码老是贴心的(不知道有没有10086贴心),它对外提供了不少借口供应用程序调用,例如AudioManger
(音频管理)、TelephoneManger(电话管理)、一样也提供了一个包管理-----PackageManager,经过它咱们能够、获取应用
程序包得信息,例如图标、Lable标签等。具体关于PackageManager的使用,能够参考个人另一篇文章 :
<<Android中获取应用程序(包)的信息-----PackageManager的使用(一)>>
2、PackageManagerService ---- 重量级选手
前面所说PackageManager不过是个傀儡,全部相关的操做都是由PackageManagerService 完成的。这儿咱们简单的
分析下PackageManagerService 的特性:
①、开机就启动,由SystemServer进程启动 ;
②、启动后它会扫描系统中全部应用程序Apk包下的AndroidManifest.xml文件,而后解析全部的
AndroidManifest.xml文件,继而造成一个庞大的信息结构树,而且保存在PackageManagerService 的相关属性下。
它会扫描这两个目录:
/system/app ------------------> 系统应用程序
/data/app ------------------> 第三方应用程序(全部安装的Apk包都会在该目录下保存一份拷贝)
扫描完成后,因而全部的信息结构就构建了。PackageManagerService 的四个重要属性以下:
[java] view plaincopyprint?
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>();
//...
}
值得注意这些属性类型的不一样。Activity、BroadcastReceiver、Service都采用了自定义类去保存相关信息,从类名上看,
类结构应该很类似。而ContentProvider只是简单的采用了HashMap键值对去保存了信息? 莫非有错 ? 咱们回忆下
AndroidManifest.xml定义组件信息时,Activity、BroadcastReceiver、Service均可以经过<intent-filter>去隐式匹配的,而
ContentProvider只须要一个Uri数据便可找到对应的ContentProvider组件信息了。 所以才采用了这两种结构去保存信息。
其实咱们经过getPackageManager()方法得到的PackageManager对象,只是PackageManagerService的客户端,
该客户端类是ApplicationPackageManager,它是ContextIml类的内部类,显然该类存在于用户空间中。
源代码(部分)以下:
[java] view plaincopyprint?
@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){
//...
}
}
//...
}
它与PackageManagerService的简单关系以下:
三 、保存数据采用的数据结构和解析算法
毫无疑问,保存全部信息是一项很复杂的工程,在具体讲解匹配过程时,咱们先看看系统为了保存这些结构定义的一些
数据结构。
IntentInfo类:继承至IntentFilter类
做用:保存了每一个<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图表示以下:
不一样的数据结构决定了不一样的算法,而不一样的算法又决定着性能,例如时间复杂度以及空间复杂度等。 在具体讲解解析
采用的算法时,咱们先理解下这个情景。
假设一个女人决定参加一个相亲节目(你们能够理解成《非诚勿扰》),而后她向主办方提出以下条件:
1、身高 ? 175cm以上 ;
二、 财富 ? 100万 ;
三、 学历 ? 本科以上 ;
……
主办发经理一看,你丫的要求还真高。但客户是万能的,该经理也只能去找到知足这些条件的男人咯。
最开始,该经理是这么想的,我把全部男的都给遍历一遍,确定把知足这些条件的男人给揪出来。他找啊找,以为这么找下去
是否是太二B了(呵呵,你也是这么想的吗?)。经理就开始想:“我为何不能根据这些条件把全部男的给分红三个种群呢?有钱
的男人在一块儿,高个子的男人在一块儿,高学历的男人在一块儿,这样查找起来不是更快吗 ? “
因而,有了以下的划分: PS, 你是属于哪一类额 ?
二B算法(没有分类) 高级点的算法(分类后)
可能你们对这种根据关键值分类的好处不能一目了然。咱们举个通常例子吧:
假设当前共有100个男的。 其中有钱的有20人,高个子有30人,高学历男人有10人。
根据第一种算法分类,咱们须要比较100次,而第二种算法咱们总共只须要比较60次。从整个基数来分析,算法确定优化了
最后,对不一样的分类中查询的结果进行组合从新排列下,便可获得咱们的知足该女性的要求。
一样的,在进行Intent匹配时,Android也采用了第二种方法来进行算法匹配。它根据一些关键值Action、MimeType、
Schema字段去进行分类。分类以后的集合大体以下:
因而在进行具体匹配时,咱们只是须要根据关键值从不一样集合中获取便可。
事实上,因为MimeType的通配符(*)的特性,它的匹配能够说是最难的。参考IntentResolver类,真正的关键值以下:
[java] view plaincopyprint?
//模板类 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 一、获取PackageManager代理对象,调用该方法:
[java] view plaincopyprint?
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 二、该PackageManager代理对象实则为ApplicatonPackageManager 对象,该对象是ContextIml的内部类。
[java] view plaincopyprint?
@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 三、调用服务端PackageManagerService对象的对应方法。该方法位于PackageManagerService.java类中
[java] view plaincopyprint?
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 四、调用mActivities自定义类去查找
[java] view plaincopyprint?
//调用父类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类中。
[java] view plaincopyprint?
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类中,方法原型以下:
[java] view plaincopyprint?
//经过前面关键字查找的可能集合,循环遍历进行匹配,匹配成功就加入到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匹配过程。