一. 匹配过程分析
对于Intent的匹配我们知道他是去和Intent-Filter节点里面的信息做匹配,所以我们得先知道手机中所有的Intent-Filter节点信息是什么时候获取到的在哪个地方放着的。
Intent-Filter节点信息是在PackageManagerService获取到的,所以我们先来看下PackageManagerService。
- PackageManagerService开机就启动,由SystemServer进程启动。
SystemServer启动ServerThread,ServerThread的run函数中有如下代码。
pm = PackageManagerService.main(context,
factoryTest != SystemServer.FACTORY_TEST_OFF,
onlyCore);
boolean firstBoot = false;
try {
firstBoot = pm.isFirstBoot();
} catch (RemoteException e) {
}
这样PackageManagerService就跑起来了。
- 启动后它会扫描系统中所有应用程序Apk包下的AndroidManifest.xml文件,然后解析所有的AndroidManifest.xml 文件,继而形成一个庞大的信息结构树,并且保存在PackageManagerService 的相关属性下,例如PackageManagerService 部分属性如下有mActivity,mReceivers,mService,mProviders。这些变量保存的是系统中所有的activity,receivers,service,providers的节点信息。
// All available activities, for your resolving pleasure.
final ActivityIntentResolver mActivities =
new ActivityIntentResolver();
// All available receivers, for your resolving pleasure.
final ActivityIntentResolver mReceivers =
new ActivityIntentResolver();
// All available services, for your resolving pleasure.
final ServiceIntentResolver mServices = new ServiceIntentResolver();
// Keys are String (provider class name), values are Provider.
final HashMap<ComponentName, PackageParser.Provider> mProvidersByComponent =
new HashMap<ComponentName, PackageParser.Provider>();
变量都是final类型的,所以在构造函数里面肯定会去有去获取这些信息的过程 我们继续更进去 上面有有看到开机的时候回调用PackageManagerService类中的main函数,那我们就看下PackageManagerService类中main函数的内容。
public static final IPackageManager main(Context context, boolean factoryTest,
boolean onlyCore) {
PackageManagerService m = new PackageManagerService(context, factoryTest, onlyCore);
ServiceManager.addService("package", m);
return m;
}
通过这个main函数会去调用PackageManagerService构造函数。因为构造函数比较长,我们只看PackageManagerService类中的mActivity,mReceivers,mService,mProviders变量信息的获取。因为比较长我就只复制了PackageManagerService构造函数的一部分。
// Find base frameworks (resource packages without code).
mFrameworkInstallObserver = new AppDirObserver(
mFrameworkDir.getPath(), OBSERVER_EVENTS, true);
mFrameworkInstallObserver.startWatching();
scanDirLI(mFrameworkDir, PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR,
scanMode | SCAN_NO_DEX, 0);
// Collect all system packages.
mSystemAppDir = new File(Environment.getRootDirectory(), "app");
mSystemInstallObserver = new AppDirObserver(
mSystemAppDir.getPath(), OBSERVER_EVENTS, true);
mSystemInstallObserver.startWatching();
scanDirLI(mSystemAppDir, PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR, scanMode, 0);
// Collect all vendor packages.
mVendorAppDir = new File("/vendor/app");
mVendorInstallObserver = new AppDirObserver(
mVendorAppDir.getPath(), OBSERVER_EVENTS, true);
mVendorInstallObserver.startWatching();
scanDirLI(mVendorAppDir, PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR, scanMode, 0);
这里干了什么事呢,就是通过scanDirLI函数去搜索给定路径文件下面的安装包,得到包里面AndroidManifest.xml文件的信息进而得到mActivity,mReceivers,mService,mProviders变量信息。
通过这个构造函数中的代码我们可以得到应用中所有安装的APK的存放路径分别有。
1. systemt/framework(“system\framework”目录下的APK文件)。
2. data/app-private(DRM保护的APK文件)。
3. syatem/app(系统自带的APK应用程序,获得adb root权限才能删除)。
4. data/app(用户程序安装的APK目录)。
5. vendar/app(制造商的APK目录)。
同时在代码中我们也看到了startWatching(),对给定的路径进行监听,看是否有新的apk添加,删除。删除就removePackageLI(), 添加就scanPackageLI()。同时也会发送广播消息sendPackageBroadcast()。这个应该比较好理解了。
在继续跟踪代码 跟踪scanDirLI函数
private void scanDirLI(File dir, int flags, int scanMode, long currentTime) {
String[] files = dir.list();
if (files == null) {
Log.d(TAG, "No files in app dir " + dir);
return;
}
if (DEBUG_PACKAGE_SCANNING) {
Log.d(TAG, "Scanning app dir " + dir);
}
int i;
for (i=0; i<files.length; i++) {
File file = new File(dir, files[i]);
if (!isPackageFilename(files[i])) {
// Ignore entries which are not apk's
continue;
}
PackageParser.Package pkg = scanPackageLI(file,
flags|PackageParser.PARSE_MUST_BE_APK, scanMode, currentTime);
// Don't mess around with apps in system partition.
if (pkg == null && (flags & PackageParser.PARSE_IS_SYSTEM) == 0 &&
mLastScanError == PackageManager.INSTALL_FAILED_INVALID_APK) {
// Delete the apk
Slog.w(TAG, "Cleaning up failed install of " + file);
file.delete();
}
}
}
这个没什么看的,就是遍历File dir目录下面所有的.apk文件。然后继续调用scanPackageLI函数(PackageManagerService类里面是有两个scanPackageLI函数重载了一次)。
两个scanPackageLI函数分别做了什么动作呢,一个是解析给定的.apk文件的PackageParser.Package信息,另一个是通过解析到的.apk文件的PackageParser.Package信息在在分类赋给PackageManagerService类中的相应的变量。我们关心的mActivity,mReceivers,mService,mProviders变量也是在这个里面得到信息的。
至于PackageParser.Package文件具体是怎么解析的我们先不管。
到这里PackageManagerService类中成员mActivity,mReceivers,mService,mProviders是怎么来的我们就稍微清楚一点了。开机的时候就获取到了。(这几个变量中会有Intent-Filter节点信息,在匹配的时候我们会用到)。
这里插一点别的东西比如我们在应用开发过程中要获取我们应用版本之类的东西。我们经常是像这么这样的写的,他是怎么来的呢。(肯定是和PackageManagerService有关的)
PackageManager manager = getPackageManager();
try {
PackageInfo info = manager.getPackageInfo(getPackageName(), PackageManager.GET_ACTIVITIES);
if (null != info && null != info.versionName) {
mSwVersion.setText(info.versionName);
}
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
我们看到是先通过getPackageManager获取到PackageMnanager这个过程是怎么的,这个PackageMnanager有代码什么呢。比如我们是在Activity中getPackageManager()因为Activity是继承ContextWrapper的所以到ContextWrapper类中去看getPackageManager函数。
@Override
public PackageManager getPackageManager() {
return mBase.getPackageManager();
}
我们看到mBase变量,这个变量从哪里来的呢。在Activity中的attach函数中给的值这句attachBaseContext(context); 所以mBase.getPackageManager();在ContextImpl类中
@Override
public PackageManager getPackageManager() {
if (mPackageManager != null) {
return mPackageManager;
}
IPackageManager pm = ActivityThread.getPackageManager();
if (pm != null) {
// Doesn't matter if we make more than one instance.
return (mPackageManager = new ApplicationPackageManager(this, pm));
}
return null;
}
这里return的PackageMnanager是ApplicationPackageManager的一个实例,我们再继续看ApplicationPackageManager 这个类里面有IPackageManager对象这个对象就是PackageManagerService在客户端的一种体现。为什么这么说呢,我们在看上面的IPackageManager pm = ActivityThread.getPackageManager();到ActivityThread去看下,可以看到应该要涉及到进程间的通信。
public static IPackageManager getPackageManager() {
if (sPackageManager != null) {
//Slog.v("PackageManager", "returning cur default = " + sPackageManager);
return sPackageManager;
}
IBinder b = ServiceManager.getService("package");
//Slog.v("PackageManager", "default service binder = " + b);
sPackageManager = IPackageManager.Stub.asInterface(b);
//Slog.v("PackageManager", "default service = " + sPackageManager);
return sPackageManager;
}
通过sPackageManager = IPackageManager.Stub.asInterface(b);获取到IPackageManager实例。我们看到在获取的时候有用到IBinder b = ServiceManager.getService(“package”); 那么这个IBinder b有是怎么来的呢。我们在回到PackageManagerService类中的mian函数,你就能发现原因了。
public static final IPackageManager main(Context context, boolean factoryTest,
boolean onlyCore) {
PackageManagerService m = new PackageManagerService(context, factoryTest, onlyCore);
ServiceManager.addService("package", m);
return m;
}
看到没有这个”package”没,一个ServiceManager.addService,一个ServiceManager.getService。这样就获取到了PackageManagerService实例。
到这里我们就知道在我们的应用中经常用PackageInfo info = manager.getPackageInfo(getPackageName(), PackageManager.GET_ACTIVITIES);去获取包的信息。其实getPackageInfo就是调用的PackageManagerService类中的getPackageInfo函数了。
说完了Intent-Filter部分的获取,那我们就得看下Activity的启动过程中进行了。
- 首先根据老罗的博客(Activity的启动过程源码分析)跟踪到
ActivityManagerService类中的startActivity()函数,进一步跟踪到ActivityStackSupervisor类中的 startActivityMayWait()函数,再进一步跟踪到ActivityStack类中 startActivityMayWait函数这个函数里面有一句如下
// Collect information about the target of the Intent.
ActivityInfo aInfo = resolveActivity(intent, resolvedType, debug,
profileFile, profileFd, autoStopProfiler);
看到没有这个就是我们要找的Activity启动过程中Intent匹配IntentFilter的入口函数的地方。从这里我们开始Intent的匹配过程。开始跟踪
- ActivityStack类中resolveActivity函数
ActivityInfo resolveActivity(Intent intent, String resolvedType, boolean debug,
String profileFile, ParcelFileDescriptor profileFd, boolean autoStopProfiler) {
// Collect information about the target of the Intent.
ActivityInfo aInfo;
try {
ResolveInfo rInfo =
AppGlobals.getPackageManager().resolveIntent(
intent, resolvedType,
PackageManager.MATCH_DEFAULT_ONLY
| ActivityManagerService.STOCK_PM_FLAGS);
aInfo = rInfo != null ? rInfo.activityInfo : null;
} catch (RemoteException e) {
aInfo = null;
}
if (aInfo != null) {
// Store the found target back into the intent, because now that
// we have it we never want to do this again. For example, if the
// user navigates back to this point in the history, we should
// always restart the exact same activity.
intent.setComponent(new ComponentName(
aInfo.applicationInfo.packageName, aInfo.name));
// Don't debug things in the system process
if (debug) {
if (!aInfo.processName.equals("system")) {
mService.setDebugApp(aInfo.processName, true, false);
}
}
if (profileFile != null) {
if (!aInfo.processName.equals("system")) {
mService.setProfileApp(aInfo.applicationInfo, aInfo.processName,
profileFile, profileFd, autoStopProfiler);
}
}
}
return aInfo;
}
在这个函数中,我们只关心一部分如下。
ActivityInfo aInfo;
try {
ResolveInfo rInfo =
AppGlobals.getPackageManager().resolveIntent(
intent, resolvedType,
PackageManager.MATCH_DEFAULT_ONLY
| ActivityManagerService.STOCK_PM_FLAGS);
aInfo = rInfo != null ? rInfo.activityInfo : null;
} catch (RemoteException e) {
aInfo = null;
}
那这段代码中AppGlobals.getPackageManager() 是什么呢,怎么来的呢,跟踪进去可以看到。
public static IPackageManager getPackageManager() {
return ActivityThread.getPackageManager();
}
在跟踪进去。看到
public static IPackageManager getPackageManager() {
if (sPackageManager != null) {
//Slog.v("PackageManager", "returning cur default = " + sPackageManager);
return sPackageManager;
}
IBinder b = ServiceManager.getService("package");
//Slog.v("PackageManager", "default service binder = " + b);
sPackageManager = IPackageManager.Stub.asInterface(b);
//Slog.v("PackageManager", "default service = " + sPackageManager);
return sPackageManager;
}
看到这个我们就熟悉了就是和刚才一样的是PackageManagerService。
到了这里我们就知道刚才我们关心的那一段代码中其实就是调用的PackageManagerService类中的resolveIntent函数。那我们就进去看下。
- PackageManagerService类中的resolveIntent函数
public ResolveInfo resolveIntent(Intent intent, String resolvedType,
int flags) {
List<ResolveInfo> query = queryIntentActivities(intent, resolvedType, flags);
return chooseBestActivity(intent, resolvedType, flags, query);
}
看到这个函数里面调用了两个函数queryIntentActivities和chooseBestActivity一个是筛选一个应该是如果有多个筛选结果弹出选择框。继续进入到PackageManagerService类中的queryIntentActivities 函数
public List<ResolveInfo> queryIntentActivities(Intent intent,
String resolvedType, int flags) {
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);
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);
}
final PackageParser.Package pkg = mPackages.get(pkgName);
if (pkg != null) {
return mActivities.queryIntentForPackage(intent, resolvedType, flags,
pkg.activities);
}
return new ArrayList<ResolveInfo>();
}
}
这里我们关心return mActivities.queryIntent(intent, resolvedType, flags);这一句,在这一句我们看到了mActivities变量他是ActivityIntentResolver类继承至IntentResolver类的一个对象,我们看下这个类中的一些变量的作用里面会帮我们存放一些什么东西(保存了所有activity(以activity作为例子)节点信息)。他是怎么保存的呢,在哪里保存的是,mActivities.addActivity(a, “activity”);这一句PackageManagerService类中scanPackageLI函数里面。前面有提到这个函数的调用,如果忘记了可以回过头看下。
这里要在说下ActivityIntentResolver类继承至IntentResolver类的一个对象的,在IntentResolver类中有几个变量我们药先说下等下我们是有用到的。我们先说变量的含义,然后在说变量是怎么来的在哪里拿到的。在说这个之前我们知道minitype,minitype有两部分组成。媒体类型(type)与子类型(subtype)组成例如text/html表明此文件的type为text(文本),subtype为html,text/plain表明此文件的type为text(文本),subtype为txt。IntentResolver类中的几个成员变量。
1. mFilters:中存放的是所有节点下的< intent-filter>节点信息换句话说就是我们手机中所有应用中所有的< intent-filter>节点信息。
2. mSchemeToFilter:中存放的是所有scheme对应的< intent-filter>节点信息,保存的方式是(HashMap
public final void addActivity(PackageParser.Activity a, String type) {
final boolean systemApp = isSystemApp(a.info.applicationInfo);
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);
}
}
最后一句addFilter(intent); IntentResolver中addFilter函数,实现如下。
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: ");
}
}
这里面保存了我们刚才看的IntentResolver类中的几个成员变量。这一部分信息我们也得到了。
我们刚才关心return mActivities.queryIntent(intent, resolvedType, flags)的。在继续跟踪。ActivityIntentResolver类中的queryIntent。
- ActivityIntentResolver类中的queryIntent函数
public List<ResolveInfo> queryIntent(Intent intent, String resolvedType, int flags) {
mFlags = flags;
return super.queryIntent(intent, resolvedType,
(flags&PackageManager.MATCH_DEFAULT_ONLY) != 0);
}
继续跟踪,IntentResolver类中的queryIntent 函数。
public List<R> queryIntent(Intent intent, String resolvedType, boolean defaultOnly) {
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);
}
if (secondTypeCut != null) {
buildResolveList(intent, categories, debug, defaultOnly,
resolvedType, scheme, secondTypeCut, finalList);
}
if (thirdTypeCut != null) {
buildResolveList(intent, categories, debug, defaultOnly,
resolvedType, scheme, thirdTypeCut, finalList);
}
if (schemeCut != null) {
buildResolveList(intent, categories, debug, defaultOnly,
resolvedType, scheme, schemeCut, finalList);
}
sortResults(finalList);
if (debug) {
Slog.v(TAG, "Final result list:");
for (R r : finalList) {
Slog.v(TAG, " " + r);
}
}
return finalList;
}
这里就是对type的类型的处理要复杂一点。
我们看到有
ArrayList firstTypeCut = null;
ArrayList secondTypeCut = null;
ArrayList thirdTypeCut = null;
这个几个变量干嘛用的呢。里面放的是什么呢。看下代码我们可以知道。同时配合前面讲的IntentResolver类总有几个变量的说明我们就大概能分析出来这些变量的作用,比如我们随便举个例子我们要去匹配的Intent给的minitype类型是text/plain. 那么firstTypeCut 里面放的是所有的text/plain类型的IntentFiler的节点,secondTypeCut 里面放的是所有的IntentFilter节点中只是指定了text(minitype的媒体类型)的IntentFilter的节点。thirdTypeCut 中放了*类型的节点。这样我们应该就能理解为什么会这样放了,为什么会有这些变量了。
继续跟踪上面的代码 buildResolveList(intent, categories, debug, defaultOnly,
resolvedType, scheme, firstTypeCut, finalList);这一句。
- IntentResolver类中 buildResolveList函数。
private void buildResolveList(Intent intent, FastImmutableArraySet<String> categories,
boolean debug, boolean defaultOnly,
String resolvedType, String scheme, List<F> src, List<R> dest) {
final String action = intent.getAction();
final Uri data = intent.getData();
final String packageName = intent.getPackage();
final boolean excludingStopped = intent.isExcludingStopped();
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;
if (debug) Slog.v(TAG, "Matching against filter " + filter);
if (excludingStopped && isFilterStopped(filter)) {
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 && !packageName.equals(packageForFilter(filter))) {
if (debug) {
Slog.v(TAG, " Filter is not from package " + packageName + "; skipping");
}
continue;
}
// 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));
if (!defaultOnly || filter.hasCategory(Intent.CATEGORY_DEFAULT)) {
final R oneResult = newResult(filter, match);
if (oneResult != null) {
dest.add(oneResult);
}
} 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 (dest.size() == 0 && hasNonDefaults) {
Slog.w(TAG, "resolveIntent failed: found match, but none with Intent.CATEGORY_DEFAULT");
}
}
这里我们只是关心match = filter.match(action, resolvedType, scheme, data, categories, TAG); 这一句。实现的地方在哪里呢。先看filter是从哪里来的呢,这里F filter = src.get(i);他就是我们的每个IntentFilter节点我们前面也有说他是怎么来的。继续跟踪代码
- IntentFilter类中match函数。
public final int match(String action, String type, String scheme,
Uri data, Set<String> categories, String logTag) {
if (action != null && !matchAction(action)) {
if (false) Log.v(
logTag, "No matching action " + action + " for " + this);
return NO_MATCH_ACTION;
}
int dataMatch = matchData(type, scheme, data);
if (dataMatch < 0) {
if (false) {
if (dataMatch == NO_MATCH_TYPE) {
Log.v(logTag, "No matching type " + type
+ " for " + this);
}
if (dataMatch == NO_MATCH_DATA) {
Log.v(logTag, "No matching scheme/path " + data
+ " for " + this);
}
}
return dataMatch;
}
String categoryMismatch = matchCategories(categories);
if (categoryMismatch != null) {
if (false) {
Log.v(logTag, "No matching category " + categoryMismatch + " for " + this);
}
return NO_MATCH_CATEGORY;
}
// It would be nice to treat container activities as more
// important than ones that can be embedded, but this is not the way...
if (false) {
if (categories != null) {
dataMatch -= mCategories.size() - categories.size();
}
}
return dataMatch;
}
我们能看到匹配的顺序是action,data,categories。具体的怎么匹配的那就简单了,也就data的匹配稍微复杂一点。我们稍微看下data的匹配函数。
public final int matchData(String type, String scheme, Uri data) {
final ArrayList<String> types = mDataTypes;
final ArrayList<String> schemes = mDataSchemes;
final ArrayList<AuthorityEntry> authorities = mDataAuthorities;
final ArrayList<PatternMatcher> paths = mDataPaths;
int match = MATCH_CATEGORY_EMPTY;
if (types == null && schemes == null) {
return ((type == null && data == null)
? (MATCH_CATEGORY_EMPTY+MATCH_ADJUSTMENT_NORMAL) : NO_MATCH_DATA);
}
if (schemes != null) {
if (schemes.contains(scheme != null ? scheme : "")) {
match = MATCH_CATEGORY_SCHEME;
} else {
return NO_MATCH_DATA;
}
if (authorities != null) {
int authMatch = matchDataAuthority(data);
if (authMatch >= 0) {
if (paths == null) {
match = authMatch;
} else if (hasDataPath(data.getPath())) {
match = MATCH_CATEGORY_PATH;
} else {
return NO_MATCH_DATA;
}
} else {
return NO_MATCH_DATA;
}
}
} else {
// Special case: match either an Intent with no data URI,
// or with a scheme: URI. This is to give a convenience for
// the common case where you want to deal with data in a
// content provider, which is done by type, and we don't want
// to force everyone to say they handle content: or file: URIs.
if (scheme != null && !"".equals(scheme)
&& !"content".equals(scheme)
&& !"file".equals(scheme)) {
return NO_MATCH_DATA;
}
}
if (types != null) {
if (findMimeType(type)) {
match = MATCH_CATEGORY_TYPE;
} else {
return NO_MATCH_TYPE;
}
} else {
// If no MIME types are specified, then we will only match against
// an Intent that does not have a MIME type.
if (type != null) {
return NO_MATCH_TYPE;
}
}
return match + MATCH_ADJUSTMENT_NORMAL;
}
通过代码我们发现如果scheme没有指定,那么Intent Fileter data中设置的其它的属性均无效。如果host没有设定,那么Intent Filter data中设置的port,path,pathRrefix,pathPattern均无效。
到这里整个Intent的匹配的过程我们就稍微知道一点了。
二. 总结
从整个Intent的匹配过程中我们可以知道。
1. scheme没有指定,那么Intent Fileter data中设置的其它的属性均无效(IntentFilter类中matchData函数中可以分析到)
2. 如果host没有设定,那么Intent Filter data中设置的port,path,pathRrefix,pathPattern均无效(IntentFilter类中matchData函数中可以分析到)
3. 如果AndroidManifest.xml文件中的Intent-Filter节点没有指定任何的action,那么任何隐式的Intent都匹配过不到给该Intent-Filter节点。(PackageParser类中parseActivity函数,如果intent.countActions() == 0就不会加入到 a.intents.add(intent);中)
4. 在我们自己写AndroidManifest.xml中Intent-Filter节点的时候如果想让隐式调用,我们就加上android.intent.category.DEFAULT的category。为什么这么说呢IntentResolver类中buildResolveList函数中,if (!defaultOnly || filter.hasCategory(Intent.CATEGORY_DEFAULT)) 这一部分。
5. 如果Intent中有多个category,那么这个Intent的所有的category在Intent-Filter节点中全部找到才算匹配通过。(IntentFilter类中matchCategories函数可以看到)
6. 隐式匹配的时候如果一个Intent中没有指定Action,如果想这个intent能匹配到Intet-Filter节点那么这个Intent至少得指定data 或者type中的一个。要不能也是匹配不到的。(IntentResolver类中queryIntent函数可以分析出来)
7. 如果我们想要匹配的Intent-Filter节点有action 和data信息。但是Intent只是指定一个action。是匹配不到的。(IntentResolver类中queryIntent函数可以分析出来)