通常情况下,我们都是通过android系统,context.startActivity(intent)方法开启Activity,若intent 中包含action, 当action匹配多个Activity时,系统会跳出选择启动那个Activity界面. 此现象在android系统中是非常常见的。
但是,若问通过action怎么查找对应的Activity? 可能大部分学者,都感觉很困惑。
我们简单追其原因:
[1]首先观看UI, 当有多个Activity选择时,会出现"Use a different app"的字符串。
[2]通过字符串定位,字符串位置:
frameworks/base/core/res/res/values-en-rAU/strings.xml
<string name="use_a_different_app" msgid="8134926230585710243">"Use a different app"</string>
[3]再通过String ID定位 对应xml布局:
frameworks/base/core/res/res/layout/resolver_different_item_header.xml
[4]通过layout ID 定位对应的Activity.
frameworks/base/core/java/com/android/internal/app/ResolverActivity.java
[5]分析:既然是一个Action对应多个Activity ,那么肯定是一个集合保存数据。果然在
ResolverActivity中发现了适配器 mAdapter.
[6]有适配器则再次寻找,加载数据的地方。
frameworks/base/core/java/com/android/internal/app/ResolverActivity.java
/**
* Rebuild the list of resolvers. In some cases some parts will need some asynchronous work
* to complete.
*
* @return Whether or not the list building is completed.
*/
protected boolean rebuildList() {
...
currentResolveList = mUnfilteredResolveList =
mResolverListController.getResolversForIntent(shouldGetResolvedFilter(),
shouldGetActivityMetadata(),
mIntents);
[7]继续跟踪getResolversForIntent的来源:
android/frameworks/base/core/java/com/android/internal/app/ResolverListController.java
public List<ResolverActivity.ResolvedComponentInfo> getResolversForIntent(
boolean shouldGetResolvedFilter,
boolean shouldGetActivityMetadata,
List<Intent> intents) {
List<ResolverActivity.ResolvedComponentInfo> resolvedComponents = null;
for (int i = 0, N = intents.size(); i < N; i++) {
final Intent intent = intents.get(i);
int flags = PackageManager.MATCH_DEFAULT_ONLY
| (shouldGetResolvedFilter ? PackageManager.GET_RESOLVED_FILTER : 0)
| (shouldGetActivityMetadata ? PackageManager.GET_META_DATA : 0);
if (intent.isWebIntent()
|| (intent.getFlags() & Intent.FLAG_ACTIVITY_MATCH_EXTERNAL) != 0) {
flags |= PackageManager.MATCH_INSTANT;
}
final List<ResolveInfo> infos = mpm.queryIntentActivities(intent, flags);
// Remove any activities that are not exported.
int totalSize = infos.size();
for (int j = totalSize - 1; j >= 0 ; j--) {
ResolveInfo info = infos.get(j);
if (info.activityInfo != null && !info.activityInfo.exported) {
infos.remove(j);
}
}
if (infos != null) {
if (resolvedComponents == null) {
resolvedComponents = new ArrayList<>();
}
addResolveListDedupe(resolvedComponents, intent, infos);
}
}
public void addResolveListDedupe(List<ResolverActivity.ResolvedComponentInfo> into,
Intent intent,
List<ResolveInfo> from) {
final int fromCount = from.size();
final int intoCount = into.size();
for (int i = 0; i < fromCount; i++) {
final ResolveInfo newInfo = from.get(i);
boolean found = false;
// Only loop to the end of into as it was before we started; no dupes in from.
for (int j = 0; j < intoCount; j++) {
final ResolverActivity.ResolvedComponentInfo rci = into.get(j);
if (isSameResolvedComponent(newInfo, rci)) {
found = true;
rci.add(intent, newInfo);
break;
}
}
if (!found) {
final ComponentName name = new ComponentName(
newInfo.activityInfo.packageName, newInfo.activityInfo.name);
final ResolverActivity.ResolvedComponentInfo rci =
new ResolverActivity.ResolvedComponentInfo(name, intent, newInfo);
rci.setPinned(isComponentPinned(name));
into.add(rci);
}
}
}
通过上面getResolversForIntent()和 addResolveListDedupe(),可以看出, 数据来源于 mpm.queryIntentActivities(), 再看源码mpm 定义 private final PackageManager mpm; 就是一个PackageManager 实例。
[8]接下来寻找PackageManager的queryIntentActivities方法:
/**
* Retrieve all activities that can be performed for the given intent.
*
* @param intent The desired intent as per resolveActivity().
* @param flags Additional option flags to modify the data returned. The
* most important is {@link #MATCH_DEFAULT_ONLY}, to limit the
* resolution to only those activities that support the
* {@link android.content.Intent#CATEGORY_DEFAULT}. Or, set
* {@link #MATCH_ALL} to prevent any filtering of the results.
* @return Returns a List of ResolveInfo objects containing one entry for
* each matching activity, ordered from best to worst. In other
* words, the first item is what would be returned by
* {@link #resolveActivity}. If there are no matching activities, an
* empty list is returned.
*/
public abstract List<ResolveInfo> queryIntentActivities(Intent intent,
@ResolveInfoFlags int flags);
通过注释可以知,此方法从intent查找出所有的activity信息。
[9]根据上面分析,我们可以描述一下完整的通过Action获取Activity集合信息.
实例演示:
//获取PackageManager对象
PackageManager pm = context.getPackageManager();
//构建Action
Intent intent = new Intent(StorageManager.ACTION_MANAGE_STORAGE);
//获取Activity相关信息的集合
final List<ResolveInfo> infos = pm.queryIntentActivities(intent, 0);
//遍历集合
for (ResolveInfo resolveInfo : infos) {
String packageName = resolveInfo.activityInfo.getComponentName().getPackageName();
String className = resolveInfo.activityInfo.getComponentName().getClassName();
if (tPackageName.equals(packageName) && tClassName.equals(className)) {
Log.d("TEST", packageName+"/"+className+" contains the action ACTION_MANAGE_STORAGE");
}
}