概述
0x00 几个问题
注:这里的“调用者”是指调用startActivity
、startService
的App。
调用者的Intent中如果不指定Action,能否与目标Activity匹配上?
其中(1)目标Activity的IntentFilter中指定了Action,(2)其他条件都匹配。如果通过Action匹配一个Service?调用者要启动Service,调用者的Intent中指定了Action,但是不指定目标Service的类名。
调用者的Intent中如果不指定Category,能否与目标Activity匹配上?
其中(1)目标Activity的IntentFilter中指定了多个Category,(2)其他条件都匹配。
如果你不确定答案,就往下看看吧:)
0x01 两类Intent:显式Intent和隐式Intent
显式(Explicit)Intent,是指在Intent中指明了要启动的包名packageName和类名className。在代码中表现为调用了intent.setComponent()
或者intent.setClassName()
。
隐式(Implicit)Intent,是指在Intent中不指明 packageName和className,而是指定了Action,Data,Category。
Intent匹配用在Activity、BroadcastReceiver和Service中。
Android 5.0 (LL)之后,不可以采用隐式Intent来指定Service,因为不安全。安全的做法是,必须指定packageName,或者packageName和className。这在Android代码中强制保证了。
参考ContextImpl.java中的validateServiceIntent()
, startService()
时会调用validateServiceIntent()
以检查Intent是否合法。
private void validateServiceIntent(Intent service) {
if (service.getComponent() == null && service.getPackage() == null) {
if (getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP) {// 5.0(含)之后就会抛异常,LOLLIPOP值为21
IllegalArgumentException ex = new IllegalArgumentException(
"Service Intent must be explicit: " + service);
throw ex;
} else {// 如果targetSdkVersion是20或更小,则仅仅提示不安全
Log.w(TAG, "Implicit intents with startService are not safe: " + service
+ " " + Debug.getCallers(2, 3));
}
}
}
从上面代码中可以看到,可以通过以下Intent启动Service:
方式一:Intent中指定包名、类名,即调用intent.setComponent()
或者intent.setClassName()
设置package和class。
方式二: Intent中指定包名和Action,即调用intent.setPackage()
和intent.setAction()
。Action也可以在构造Intent时传入,即new Intent(Action) 。
0x02 隐式Intent的匹配规则
(1) 谁跟谁匹配?启动Activity、Receiver、Service的Intent和AndroidManifest.xml中声明的组件信息(目标组件)进行比较。例如Action的匹配mActions.contains(action)
,其中mActions
为AndroidManifest.xml中声明的某个组件所接受的所有Actions。实参action
为Intent中的Action。
(2) 只匹配Action,Data,Category,不匹配其他值,例如mExtras中的数据,mFlags。
(3) 匹配顺序为:Action -> Data -> Category。即先检查Action是否匹配,再检查Data,最后检查Category,只有这3项都匹配了,才算Intent匹配上。否则Intent不匹配。
对于Action、Data和Category中的某一项的匹配规则如下,再次强调,Intent是否匹配要看Action、Data和Category是否都匹配上。
Action的匹配规则
匹配项 | 发送的Intent | 组件声明(AndroidManifest.xml) | 结果 |
---|---|---|---|
Action | 包含 | 包含 | Intent中的Action要匹配组件声明时其中一个Action,才算Action匹配;否则Action不匹配。代码为mActions.contains(action) |
Action | 包含 | 不包含 | Action不匹配 |
Action | 不包含 | 包含(或不包含) | Action匹配,注意Intent中不对Action赋值,是可以匹配通过的。 |
Action匹配通过,才会进一步匹配Data。
Data的匹配规则
匹配项 | 发送的Intent | 组件声明(AndroidManifest.xml) | 结果 |
---|---|---|---|
Data | 既不包含Uri,也不包含MIME type | 不包含任何Uri和MIME type; | Data匹配 |
Data | 既不包含Uri,也不包含MIME type | 包含Uri或MIME; | Data不匹配 |
Data | 含Uri,无MIME | 含Uri并匹配,且无MIME | Data匹配 |
Data | 含Uri,无MIME | 其他情况(即除了“含Uri并匹配,且无MIME”这种情况) | Data不匹配 |
Data | 含MIME,无Uri | 含MIME并匹配,且无Uri | Data匹配 |
Data | 含MIME,无Uri | 其他情况(即除了“含MIME并匹配,且无Uri”这种情况) | Data不匹配 |
Data | 含Uri,含MIME | 含MIME并匹配且含Uri并匹配 | Data匹配 |
Data | 含Uri,含MIME | 含MIME并匹配,并且不含Uri | 如果Intent中的Uri是content: 或者file: 类型的Uri,则Data匹配,否则Data不匹配 |
其中Uri的匹配规则如下:
Uri:<scheme>://<authority>/<path>
(1) 如果intent filter中仅指定了scheme,则(Intent中)所有包含该scheme的Uri都匹配;
(2) 如果filter中指定了scheme,authority,无path,则(Intent中)所有相同scheme和authority的Uri都匹配。path忽略;
(3) 如果filter中同时指定了scheme,authority和path,则(Intent中)完全一样才能匹配。
Data 匹配通过,才会进一步匹配Category 。
Category的匹配规则
匹配项 | 发送的Intent | 组件声明(AndroidManifest.xml) | 结果 |
---|---|---|---|
Category | 包含 | 包含 | Intent中每一个category都要与AndroidManifest.xml中某组件声明的所有Categories中某项匹配,则Category匹配;否则,Category不匹配。代码见后面。 |
Category | 包含 | 不包含 | Category不匹配 |
Category | 不包含 | 包含(或不包含) | Category匹配 |
Category也匹配通过了,那么Intent就匹配上了。
0x03 附Intent匹配的代码
// match()的参数是传过来的Intent中的Action,Data和Category
public final int match(String action, String type, String scheme,
Uri data, Set<String> categories, String logTag) {
if (action != null && !matchAction(action)) {
return NO_MATCH_ACTION;
}
int dataMatch = matchData(type, scheme, data);
if (dataMatch < 0) {
return dataMatch;
}
String categoryMismatch = matchCategories(categories);
if (categoryMismatch != null) {
return NO_MATCH_CATEGORY;
}
return dataMatch;
}
// 匹配Action
public final boolean hasAction(String action) {
return action != null && mActions.contains(action);
}
public final boolean matchAction(String action) {
return hasAction(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) {
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;
}
//匹配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()) {
final String category = it.next();
if (!mCategories.contains(category)) {
return category;
}
}
return null;
}
最后
以上就是冷静丝袜为你收集整理的隐式Intent 的匹配规则0x00 几个问题0x01 两类Intent:显式Intent和隐式Intent0x02 隐式Intent的匹配规则0x03 附Intent匹配的代码的全部内容,希望文章能够帮你解决隐式Intent 的匹配规则0x00 几个问题0x01 两类Intent:显式Intent和隐式Intent0x02 隐式Intent的匹配规则0x03 附Intent匹配的代码所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复