ChooserActivity 选择器
ResolverActivity 和 ChooserActivity 都显示“让用户选择应用”的界面,但来源不同:ResolverActivity 是 PMS 在普通隐式 Intent 多匹配且没有首选项时构造的兜底结果;ChooserActivity 是应用通过 Intent.createChooser() 明确请求的分享选择器。Android 17 的 Chooser 实现在 packages/modules/IntentResolver 模块,PMS 不负责绘制分享面板,也不负责生成 Direct Share 排名。
1. 两种选择器
| 选择器 | 触发条件 | 是否记录默认应用 | 主要内容 |
|---|---|---|---|
| ResolverActivity | 普通 Intent 无法单选 | 支持“始终使用” | ResolverListAdapter 候选 |
| ChooserActivity | ACTION_CHOOSER | 不提供始终使用语义 | 分享预览、Direct Share、profile tabs |
2. Chooser Intent
源码文件:frameworks/base/core/java/android/content/Intent.java
public static Intent createChooser(
Intent target, CharSequence title, IntentSender sender) {
Intent intent = new Intent(ACTION_CHOOSER);
intent.putExtra(EXTRA_INTENT, target);
if (title != null) {
intent.putExtra(EXTRA_TITLE, title);
}
if (sender != null) {
intent.putExtra(EXTRA_CHOOSER_RESULT_INTENT_SENDER, sender);
}
int permFlags = target.getFlags()
& (FLAG_GRANT_READ_URI_PERMISSION
| FLAG_GRANT_WRITE_URI_PERMISSION
| FLAG_GRANT_PERSISTABLE_URI_PERMISSION
| FLAG_GRANT_PREFIX_URI_PERMISSION);
if (permFlags != 0) {
ClipData targetClipData = target.getClipData();
if (targetClipData == null && target.getData() != null) {
targetClipData = new ClipData(
null, new String[] { target.getType() },
new ClipData.Item(target.getData()));
}
if (targetClipData != null) {
intent.setClipData(targetClipData);
intent.addFlags(permFlags);
}
}
return intent;
}Chooser Intent 的 EXTRA_INTENT 才是真正要解析的目标;外层 action 是 ACTION_CHOOSER。目标 Intent 的 URI 授权 flags 和 ClipData 会迁移到外层,保证选择器启动目标时仍能传递读写授权。sender 用于用户选中目标后的结果通知,不是 PMS 的 PreferredActivity 记录。
3. Resolver 来源
源码文件:frameworks/base/services/core/java/com/android/server/pm/ResolveIntentHelper.java
if ((privateResolveFlags
& PackageManagerInternal.RESOLVE_NON_RESOLVER_ONLY) != 0) {
return null;
}
ri = new ResolveInfo(mResolveInfoSupplier.get());
ri.activityInfo = new ActivityInfo(ri.activityInfo);
ri.activityInfo.labelRes =
ResolverActivity.getLabelRes(intent.getAction());
if (ri.userHandle == null) {
ri.userHandle = UserHandle.of(userId);
}
if (ri.activityInfo.metaData == null) {
ri.activityInfo.metaData = new Bundle();
}
ri.activityInfo.metaData.putBoolean(
Intent.METADATA_DOCK_HOME, true);
return ri;这是普通 resolve 的兜底:PMS 返回一个代表系统 ResolverActivity 的 ResolveInfo。ChooserActivity 不由此分支直接产生;应用主动创建 ACTION_CHOOSER 后,系统根据 chooser component 启动模块中的 ChooserActivity。
4. Resolver 选择
源码文件:packages/modules/IntentResolver/java/src/com/android/intentresolver/ResolverActivity.java
public void startSelected(int which, boolean always,
boolean hasIndexBeenFiltered) {
if (isFinishing()) {
return;
}
ResolveInfo ri = mMultiProfilePagerAdapter
.getActiveListAdapter()
.resolveInfoForPosition(which, hasIndexBeenFiltered);
TargetInfo target = mMultiProfilePagerAdapter
.getActiveListAdapter()
.targetInfoForPosition(which, hasIndexBeenFiltered);
if (target == null) {
return;
}
if (onTargetSelected(target, always)) {
if (always) {
MetricsLogger.action(this,
MetricsProto.MetricsEvent.ACTION_APP_DISAMBIG_ALWAYS);
} else {
MetricsLogger.action(this,
MetricsProto.MetricsEvent.ACTION_APP_DISAMBIG_JUST_ONCE);
}
finish();
}
}ResolverActivity 的 always 参数来自“始终”按钮;onTargetSelected 会构造匹配 filter,并调用 addPreferredActivity 或 setLastChosen。这是普通 Intent 的默认应用机制,ChooserActivity 不复用这套“始终”按钮。
5. Chooser 模块
源码文件:packages/modules/IntentResolver/java/src/com/android/intentresolver/ChooserActivity.java
public class ChooserActivity extends Hilt_ChooserActivity
implements ResolverListAdapter.ResolverListCommunicator,
PackagesChangedListener, StartsSelectedItem {
private ChooserRequest mRequest;
private ChooserMultiProfilePagerAdapter
mChooserMultiProfilePagerAdapter;
private IntentSender mShareResultSender;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mActivityModelRepository.initialize(this::createActivityModel);
setTheme(R.style.Theme_DeviceDefault_Chooser);
mChooserHelper.setInitializer(this::initialize);
mChooserHelper.setOnChooserRequestChanged(
this::onChooserRequestChanged);
}ChooserActivity 的状态核心是 ChooserRequest,而不是直接把外层 Intent 当作唯一配置。request 包含目标 Intent、附加目标、排除组件、分享 target filter、回调 sender、预览和交互 chooser 参数。初始化由 ChooserHelper 回调触发,便于处理 configuration change 和 chooser request 更新。
6. Profile 目标
private void initialize() {
mViewModel = new ViewModelProvider(this)
.get(ChooserViewModel.class);
mRequest = mViewModel.getRequest().getValue();
mProfiles = new ProfileHelper(
mUserInteractor, mBackgroundDispatcher);
mProfileAvailability = new ProfileAvailability(
mUserInteractor, getCoroutineScope(getLifecycle()),
mBackgroundDispatcher);
mPinnedSharedPrefs = getPinnedSharedPrefs(this);
updateShareResultSender();
createProfileRecords(
new AppPredictorFactory(
this,
Objects.toString(mRequest.getSharedText(), null),
mRequest.getShareTargetFilter(),
mAppPredictionAvailable),
mRequest.getShareTargetFilter());
mChooserMultiProfilePagerAdapter = createMultiProfilePagerAdapter(
this, mProfilePagerResources, mRequest, mProfiles,
mProfileRecords.values(), mProfileAvailability,
mMaxTargetsPerRow, this::expandDrawer);
}初始化阶段先建立 request、profile 可用性、分享结果 sender 和 AppPredictor,再创建多 profile pager adapter。Direct Share 的候选来自 AppPredictor/ShortcutLoader,不是 PMS 的 IntentFilter 列表;PMS 候选是 app targets 的基础集合。
private ProfileRecord createProfileRecord(
Profile profile, IntentFilter targetIntentFilter,
List<ChooserTarget> callerTargets,
AppPredictorFactory factory) {
UserHandle userHandle = profile.getPrimary().getHandle();
AppPredictor appPredictor = factory.create(userHandle);
ShortcutLoader shortcutLoader = ActivityManager.isLowRamDeviceStatic()
? null
: createShortcutLoader(
this, appPredictor, userHandle,
targetIntentFilter,
shortcutsResult -> onShortcutsLoaded(
userHandle, shortcutsResult));
ProfileRecord record = new ProfileRecord(
profile, appPredictor, shortcutLoader, callerTargets);
mProfileRecords.put(userHandle.getIdentifier(), record);
return record;
}每个 profile 都有独立 AppPredictor 和 ShortcutLoader;低 RAM 设备跳过 ShortcutLoader。调用者提供的 ChooserTarget 只放入启动 profile,其他 profile 使用空列表。
7. Profile 目标来源
for (ProfileRecord record : profileRecords) {
Profile profile = record.profile;
boolean isCrossProfile = !profile.equals(launchedAs);
ChooserGridAdapter adapter = createChooserGridAdapter(
context,
isCrossProfile
? prepareCrossProfileIntents(
getContentResolver(),
mRequest.getTargetIntent(),
mRequest.getCrossProfilePayloadIntents(),
getEffectiveProfile(launchedAs)
.getPrimary().getHandle(),
getEffectiveProfile(profile)
.getPrimary().getHandle())
: request.getPayloadIntents(),
isCrossProfile ? null : initialIntentArray,
profile.getPrimary().getHandle());
tabs.add(new TabConfig<>(
profile.getType().ordinal(),
profilePagerResources.profileTabLabel(profile.getType()),
profilePagerResources.profileTabAccessibilityLabel(
profile.getType()),
profile.getType().name(), adapter));
}Chooser 的 profile tab 不只是把同一列表复制多份。跨 profile 时会调用 prepareCrossProfileIntents 重写 payload,并且不带启动 profile 的 initial intents;每个 tab 使用目标 profile 的 UserHandle。工作资料、私人资料和 clone/private profile 的可用性由 ProfileAvailability 决定。
8. 选择目标与回调
源码文件:packages/modules/IntentResolver/java/src/com/android/intentresolver/ChooserActivity.java
protected boolean onTargetSelected(TargetInfo target) {
if (mRefinementManager.maybeHandleSelection(
target, mRequest.getRefinementIntentSender(),
getApplication(), getMainThreadHandler())) {
return false;
}
updateModelAndChooserCounts(target);
maybeRemoveSharedText(target);
safelyStartActivity(target);
return !target.isSuspended();
}
public void startSelected(int which, boolean always, boolean filtered) {
TargetInfo target = mChooserMultiProfilePagerAdapter
.getActiveListAdapter()
.targetInfoForPosition(which, filtered);
if (target != null && target.isNotSelectableTargetInfo()) {
return;
}
if (target != null && onTargetSelected(target)) {
MetricsLogger.action(this,
MetricsEvent.ACTION_APP_DISAMBIG_TAP);
finish();
}
}ChooserActivity 忽略 always 参数,不写 PreferredActivity。选中目标可能先进入 refinement intent sender;没有 refinement 时更新预测统计、按策略移除分享文本,再安全启动目标。挂起应用由 ActivityManager 弹出提示,Chooser 自己返回 false 并保持生命周期。
9. 结果 sender
private void updateShareResultSender() {
IntentSender chosenComponentSender =
mRequest.getChosenComponentSender();
mShareResultSender = mShareResultSenderFactory.create(
mViewModel.getActivityModel().getLaunchedFromUid(),
chosenComponentSender);
}Intent.createChooser(target, title, sender) 传入的 sender 会进入 ChooserRequest,再由 ShareResultSenderFactory 包装。它用于把用户选中的组件通知给调用方,与 ResolverActivity 的“设置默认应用”持久化是两条不同链。
10. 排查清单
- 看到 ResolverActivity:检查 PMS 的
chooseBestActivity,它表示普通 Intent 歧义未解决。 - 看到 ChooserActivity:检查外层 Intent 是否为
ACTION_CHOOSER,并读取EXTRA_INTENT。 - 候选缺失:区分 PMS app targets、Chooser 的 filtered component names、profile 可用性和 Direct Share 异步加载。
- “始终使用”不生效:Chooser 不写 PreferredActivity;只有 ResolverActivity 的 always 分支才会持久化默认偏好。
- 分享 URI 权限丢失:检查
createChooser是否迁移 target 的 grant flags 和 ClipData。 - 选择后没有回调:检查
EXTRA_CHOOSER_RESULT_INTENT_SENDER是否存在,以及 ShareResultSender 是否被创建。 - 工作资料列表不同:检查 cross-profile payload 重写和 profile tab 的 UserHandle。
11. 源码阅读路线
Intent.createChooser:确认外层 action、目标 Intent、ClipData 和授权 flags。ResolveIntentHelper.chooseBestActivity:确认普通 Intent 何时得到 ResolverActivity。packages/modules/IntentResolver/.../ResolverActivity.java:阅读 always/once 选择和 PreferredActivity 写入。packages/modules/IntentResolver/.../ChooserActivity.java:阅读 ChooserRequest、profile records 和选择回调。createMultiProfilePagerAdapter:区分 app targets、initial intents、cross-profile payload。onTargetSelected/startSelected:确认 refinement、预测统计、文本处理和目标启动顺序。
ChooserActivity 是“应用明确要求用户选择”的分享工作流;ResolverActivity 是 PMS 在普通解析无法单选时返回的系统兜底。两者都展示候选,但默认偏好、数据来源、跨 profile 处理和回调语义完全不同。
