我们都知道Activity启动分为显示启动和隐示启动,其中显示启动很好理解就是根据指定的类的class进行启动,很好就行匹配找到目标activity,下面先来看下显示启动的匹配逻辑
/**
* Create a new component identifier from a Context and Class object.
*
* @param pkg A Context for the package implementing the component, from
* which the actual package name will be retrieved.
* @param cls The Class object of the desired component, from which the
* actual class name will be retrieved.
*/
public ComponentName(@NonNull Context pkg, @NonNull Class<?> cls) {
mPackage = pkg.getPackageName();
mClass = cls.getName();
}
再来看下ComponentName的euqlas的方法
@Override
public boolean equals(Object obj) {
try {
if (obj != null) {
ComponentName other = (ComponentName)obj;
// Note: no null checks, because mPackage and mClass can
// never be null.
//mPackage包名,mClass类的全路径名
return mPackage.equals(other.mPackage)
&& mClass.equals(other.mClass);
}
} catch (ClassCastException e) {
}
return false;
}
现在就很清楚了,显示启动匹配是按照包名以及目标类的全路径名。
显示启动的匹配规则已经很清楚了,下面来看下隐示启动的匹配逻辑,隐示启动是通过IntentFiler来指定匹配规则的,IntentFiler中有三个参数action,categories和data,然后data里面又包含MIME type,Uri scheme,host name等等。下面来直接看看下IntentFiler中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的匹配方法matchAction
public final boolean hasAction(String action) {
return action != null && mActions.contains(action);
}
从代码中可以看出,mActions是指IntentFiler中的action,也就是我们在AndroidManifest中定义的action。Intent中只能定义一个action,一个Intent Filter中可声明多个action,Intent中的action与其中的任一个action在字符串形式上完全相同(注意,区分大小写,大小写不同但字符串内容相同也会造成匹配失败),action方面就匹配成功。
然后再来看下categories的匹配规则,matchCategories
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()) {
final String category = it.next();
if (!mCategories.contains(category)) {
return category;
}
}
return null;
}
category在IntentFiler中和在Intent中都可以指定多个,这个mCategories就是IntentFiler中指定的category,matchCategories中的参数就是Intent中指定的category。从上面代码逻辑可以看出,只要Intent中指定的category在IntentFiler中都能找到就算匹配成功。
最后再来看下相对复杂一点的data的匹配逻辑,matchData
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) {
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);
if (authMatch >= 0) {
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 {
// 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;
}
首先data也是在IntentFiler中指定多个,在Intent中只能指定一个,首先来看第一个判断
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);
}
image.png
意思就是在IntentFiler中未指定任何的type和scheme,并且Intent中也未指定type和data,这时候直接就匹配成功了。接着当Intent定义的schemes不为空时,就判断如果包含Intent中设置的scheme,那就表明scheme匹配成功,然后继续匹配sspPrefix,如果schemeSpecificParts为null,直接匹配host,host匹配不成功则直接退出。
public final int matchDataAuthority(Uri data) {
if (mDataAuthorities == null || data == null) {
return NO_MATCH_DATA;
}
final int numDataAuthorities = mDataAuthorities.size();
for (int i = 0; i < numDataAuthorities; i++) {
final AuthorityEntry ae = mDataAuthorities.get(i);
int match = ae.match(data);
if (match >= 0) {
return match;
}
}
return NO_MATCH_DATA;
}
host匹配成功就开始匹配path,IntentFilter未指定任何path的话,则直接匹配成功,如果有指定path,匹配成功到最后匹配MimeType,
int authMatch = matchDataAuthority(data);
if (authMatch >= 0) {
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;
}
匹配MimeType
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;
}
}
public @NonNull Intent setType(@Nullable String type) {
mData = null;
mType = type;
return this;
}
public @NonNull Intent setData(@Nullable Uri data) {
mData = data;
mType = null;
return this;
}
public @NonNull Intent setDataAndType(@Nullable Uri data, @Nullable String type) {
mData = data;
mType = type;
return this;
}
其中设置type会将data直接置为null,设置data也会将type置为null,我的理解就是系统未避免我们传入的data和type不匹配,所以两者必须得同时设置, 这样就减少了出错的几率。
网友评论