Android 中启动Activity 有两种方式
1.显式启动
Intent intent = new Intent(this, SecondActivity.class);
startActivity(intent);
2.隐式启动
<activity
android:name=".ui.activity.TestDemo">
<intent-filter>
<category android:name="android.intent.category.DEFAULT" />
<action android:name="android.intent.action.VIEW" />
<data
android:host="com.gl.demo"
android:scheme="example"
android:path="/TestDemo1"/>
</intent-filter>
</activity>
String uri ="example://com.gl.demo/TestDemo1";
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(uri));
context.startActivity(intent);
上述流程图中
显式匹配 getActivityInfo ()
PackageManagerService.java
显式启动
new Intent(this, SecondActivity.class); 构造的conmponentName
public Intent(Context packageContext, Class<?> cls) {
mComponent = new ComponentName(packageContext, cls);
}
@Override
public ActivityInfo getActivityInfo(ComponentName component, int flags, int userId) {
....
synchronized (mPackages) {
//根据class name获取
PackageParser.Activity a = mActivities.mActivities.get(component);
//检查是否已安装并且可用
if (a != null && mSettings.isEnabledAndMatchLPr(a.info, flags, userId)) {
PackageSetting ps = mSettings.mPackages.get(component.getPackageName());
if (ps == null) return null;
// Make shallow copies
return PackageParser.generateActivityInfo(a, flags, ps.readUserState(userId),
userId);//生成一个匹配的ActivityInfo
}
}
return null;
}
上述代码中,重要的是mActivities.mActivities
mActivities : PackageManagerService.ActivityIntentResolver
mActivities.mActivities 的存储过程就是解析AndroidManifest.xml 中的<activity>标签解析并存储的过程,此处不在扩张这个过程。
下面摘出一些简要代码,便于读者简单了解一下mActivities 的数据来源。
PackageParser.java
private boolean parseBaseApplication(Package owner, Resources res,
XmlResourceParser parser, int flags, String[] outError)
throws XmlPullParserException, IOException {
if (tagName.equals("activity")) {//解析xml 中<activity>标签
Activity a = parseActivity(owner, res, parser, flags, outError, false,
owner.baseHardwareAccelerated);
if (a == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
owner.activities.add(a);
}
}
PackageManagerService.java
private PackageParser.Package scanPackageDirtyLI(PackageParser.Package pkg,
final int policyFlags, final int scanFlags, long currentTime, UserHandle user)
throws PackageManagerException {
....省略代码....
//此处的pkg 对应上段代码中的owner,pkg.activities 就是解析完manifest中所有的<activity>标签
N = pkg.activities.size();
r = null;
for (i=0; i<N; i++) {
PackageParser.Activity a = pkg.activities.get(i);
a.info.processName = fixProcessName(pkg.applicationInfo.processName,
a.info.processName, pkg.applicationInfo.uid);
mActivities.addActivity(a, "activity");
....省略代码....
}
}
public final void addActivity(PackageParser.Activity a, String type) {
mActivities.put(a.getComponentName(), a);
....省略代码....
}
隐式匹配 queryIntent()
ActivityIntentResolver.java
public List<R> queryIntent(Intent intent, String resolvedType, boolean defaultOnly,
int userId) {
String scheme = intent.getScheme();
ArrayList<R> finalList = new ArrayList<R>();
F[] firstTypeCut = null;
F[] secondTypeCut = null;
F[] thirdTypeCut = null;
F[] schemeCut = null;
.... 省略了 MIME_TYPE 的匹配
// 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).
/**
* All of the URI schemes (such as http) that have been registered.
* private final ArrayMap<String, F[]> mSchemeToFilter = new ArrayMap<String, F[]>();
*/
if (scheme != null) {
schemeCut = mSchemeToFilter.get(scheme);
}
.... 省略代码...
FastImmutableArraySet<String> categories = getFastIntentCategories(intent);
.... 省略代码...
if (schemeCut != null) {
buildResolveList(intent, categories, debug, defaultOnly,
resolvedType, scheme, schemeCut, finalList, userId);
}
sortResults(finalList);
return finalList;
}
private void buildResolveList(Intent intent, FastImmutableArraySet<String> categories,
boolean debug, boolean defaultOnly,
String resolvedType, String scheme, F[] src, List<R> dest, int userId) {
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.length : 0;
boolean hasNonDefaults = false;
int i;
F filter;
for (i=0; i<N && (filter=src[i]) != null; i++) {
int match;
....省略代码....
//隐式匹配的关键代码都在 filter.match 方法中
match = filter.match(action, resolvedType, scheme, data, categories, TAG);
if (match >= 0) {
//defaultOnly
if (!defaultOnly || filter.hasCategory(Intent.CATEGORY_DEFAULT)) {
final R oneResult = newResult(filter, match, userId);
if (oneResult != null) {
dest.add(oneResult);
}
} else {
hasNonDefaults = true;
}
}
}
}
上述代码中,隐式匹配的关键处理代码都在
filter.match(action, resolvedType, scheme, data, categories, TAG)
此处的filter 指向 F
在ActivityResolver 中F 即 PackageParser.ActivityIntentInfo
所以filter.match ()方法最终指向了IntentFilter 中,终于看到了熟悉的面孔
IntentFilter.java
public final int match(String action, String type, String scheme,
Uri data, Set<String> categories, String logTag) {
//<action> 匹配
if (action != null && !matchAction(action)) {
}
//<data> 匹配
int dataMatch = matchData(type, scheme, data);
if (dataMatch < 0) {
return dataMatch;
}
//<category>匹配
String categoryMismatch = matchCategories(categories);
if (categoryMismatch != null) {
return NO_MATCH_CATEGORY;
}
return dataMatch;
}
<action>匹配规则
IntentFilter.java
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>匹配规则
IntentFilter.java
public final int matchData(String type, String scheme, Uri data) {
final ArrayList<String> types = mDataTypes;//对应<data android:mimeType="*/*">
final ArrayList<String> schemes = mDataSchemes;//对应<data android:schema="">
int match = MATCH_CATEGORY_EMPTY;
if (types == null && schemes == null) {
return ((type == null && data == null)
? (MATCH_CATEGORY_EMPTY+MATCH_ADJUSTMENT_NORMAL) : NO_MATCH_DATA);
}
//schema 匹配成功,才继续;匹配失败则直接返回
if (schemes != null) {
if (schemes.contains(scheme != null ? scheme : "")) {
match = MATCH_CATEGORY_SCHEME;
} else {
return NO_MATCH_DATA;
}
//ssp android:sspPrefix="" 有ssp 标签的则此数组不为空,用的比较少,此处略
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.
//auth tag <data>标签下有<android:host=""> mDataAuthorities 不为空
final ArrayList<AuthorityEntry> authorities = mDataAuthorities;
if (authorities != null) {
int authMatch = matchDataAuthority(data);//host 匹配继续;失败
if (authMatch >= 0) {
// <data>标签下有<android:path=""> mDataPaths不为空
final ArrayList<PatternMatcher> paths = mDataPaths;
if (paths == null) {
match = authMatch;
} else if (hasDataPath(data.getPath())) {//path 匹配
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;
}
}
....省略代码....
return match + MATCH_ADJUSTMENT_NORMAL;
}
从上述代码可以看出 data 匹配:
匹配 schema 成功,则继续匹配;否则不匹配;
匹配 host 成功,继续匹配;否则不匹配;
匹配 path 成功,继续匹配;否则不匹配
<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 中包含的category 在IntentFilter 中没有,则匹配失败。
那么AndroidManifest.xml 中的<intent-filter>是如何对应解析到IntentFilter 中的呢,下面展示一些关键代码,便于大家与上面数据来源做对比。
private boolean parseIntent(Resources res, XmlResourceParser parser,
boolean allowGlobs, boolean allowAutoVerify, IntentInfo outInfo, String[] outError)
throws XmlPullParserException, IOException {
//IntentInfo extends IntentFilter ,outInfo 的方法对应到IntentFilter 中
String nodeName = parser.getName();
if (nodeName.equals("action")) {//<action>解析
....
outInfo.addAction(value);
} else if (nodeName.equals("category")) {//<category>解析
....
outInfo.addCategory(value);
} else if (nodeName.equals("data")) {
sa = res.obtainAttributes(parser,
com.android.internal.R.styleable.AndroidManifestData);
String str = sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifestData_mimeType, 0);
if (str != null) {//<android:mimeType="">
try {
outInfo.addDataType(str);
} catch (IntentFilter.MalformedMimeTypeException e) {
outError[0] = e.toString();
sa.recycle();
return false;
}
}
str = sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifestData_scheme, 0);//<android:schema="">
if (str != null) {
outInfo.addDataScheme(str);
}
....
String host = sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifestData_host, 0);//<android:host="">
String port = sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifestData_port, 0);//<android:port="">
if (host != null) {
outInfo.addDataAuthority(host, port);
}
str = sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifestData_path, 0);//<android:path="">
....
}
}