我们启动Activity时可以使用显式调用和隐式调用,显式调用需要制定要启动组件的信息,例如包名和类名,而隐式调用则通过IntentFilter进行匹配,下面则记录一下IntentFiler的过滤规则
一、action
- action是一个字符串,Intent中的action必须与<intent-fileter>中的任何一个action完全相同
- action区分大小写,大小写不同则匹配失败
二、category
- Intent中可以有多个category,Intenter中每一个category都必须与<intent-fileter>任何一个匹配
- 如果Intent没有添加category,系统调用startActivity或者startActivityForResult会默认带上android.intent.category.DEFAULT
- 需要隐式启动的Activity必须在<intent-fileter>中加android.intent.category.DEFAULT标签,原因如2
三、data
- Activity若含有data标签,则Intent必须要有与之匹配的data
- data的语法:
Scheme:URI的模式。<data android:scheme="string" android:host="string" android:port="string" android:path="string" android:pathPattern="string" android:pathPrefix="string" android:mimeType="string"/>Host:URI主机名
Port:URI端口号
Path、pathPattern、pathPrefix:表述路径信息,path标识完整的路径信息;pathPattern 也表示完整路径信息,但是它里面可以包含通配符"*"表示0个或多个任意字符;pathPrefix表示路径的前缀信息。mimeType:指媒体类型,如"image/*","audio/*","video/*",data标签可以只有android:mimeType。 - 如果没有指定URI而指定了mimeType,则Intent中的URI默认是content或者file才能匹配。
四、源码解析
下面来看下源码是如何对这些进行解析过滤的,过滤的逻辑在IntentFilter.java中
public final int match(String action, String type, String scheme,
Uri data, Set<String> categories, String logTag) {
if (action != null && !matchAction(action)) {
// 过滤action
return NO_MATCH_ACTION;
}
int dataMatch = matchData(type, scheme, data);
if (dataMatch < 0) {
// 过滤data
return dataMatch;
}
String categoryMismatch = matchCategories(categories);
if (categoryMismatch != null) {
// 过滤category
return NO_MATCH_CATEGORY;
}
return dataMatch;
}从这里可以看到,IntentFilter会先匹配<action>然后匹配<data>,最后才会匹配<category>
<action>匹配规则
public final boolean matchAction(String action) {
return hasAction(action);
}
public final boolean hasAction(String action) {
return action != null && mActions.contains(action);
}从代码中可以看出,一个Intent Filter中可声明多个action,Intent中的action与其中的任一个action在字符串形式上完全相同(注意,区分大小写,大小写不同但字符串内容相同也会造成匹配失败),action方面就匹配成功。
<data>匹配规则
public final int matchData(String type, String scheme, Uri data) {
final ArrayList<String> types = mDataTypes;
final ArrayList<String> schemes = mDataSchemes;
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) {
// 先进行schemes匹配,失败则返回
if (schemes.contains(scheme != null ? scheme : "")) {
match = MATCH_CATEGORY_SCHEME;
} else {
return NO_MATCH_DATA;
}
final ArrayList<PatternMatcher> schemeSpecificParts = mDataSchemeSpecificParts;
if (schemeSpecificParts != null && data != null) {
match = hasDataSchemeSpecificPart(data.getSchemeSpecificPart())
? MATCH_CATEGORY_SCHEME_SPECIFIC_PART : NO_MATCH_DATA;
}
if (match != MATCH_CATEGORY_SCHEME_SPECIFIC_PART) {
// If there isn't any matching ssp, we need to match an authority.
final ArrayList<AuthorityEntry> authorities = mDataAuthorities;
if (authorities != null) {
int authMatch = matchDataAuthority(data); // host匹配
if (authMatch >= 0) {
// path匹配
final ArrayList<PatternMatcher> paths = mDataPaths;
if (paths == null) {
match = authMatch;
} else if (hasDataPath(data.getPath())) {
match = MATCH_CATEGORY_PATH;
} else {
return NO_MATCH_DATA;
}
} else {
return NO_MATCH_DATA;
}
}
}
// If neither an ssp nor an authority matched, we're done.
if (match == NO_MATCH_DATA) {
return NO_MATCH_DATA;
}
} else {
if (scheme != null && !"".equals(scheme)
&& !"content".equals(scheme)
&& !"file".equals(scheme)) {
return NO_MATCH_DATA;
}
}
if (types != null) {
// 进行mimeType匹配
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;
}从代码中可以看到,<data>的匹配顺序如下:
- 匹配schemes
- 匹配host
- 匹配path
- 匹配mimeType
<category>匹配规则
public final String matchCategories(Set<String> categories) {
if (categories == null) {
return null;
}
Iterator<String> it = categories.iterator();
if (mCategories == null) {
return it.hasNext() ? it.next() : null;
}
while (it.hasNext()) {
// 对Intent中每一个category都要进行比对
final String category = it.next();
if (!mCategories.contains(category)) {
return category;
}
}
return null;
}从这里可以看到,Intent中的所有category都必须进行匹配成功就可以
参考资料
《Android开发艺术探索》