Skip to content

ChooserActivity 选择器

解析 createChooser、ResolverActivity 与 ChooserActivity 的候选展示和选择回调。

AndroidIntentChooserActivityResolverActivity

ChooserActivity 选择器 ​

ResolverActivity 和 ChooserActivity 都显示“让用户选择应用”的界面,但来源不同:ResolverActivity 是 PMS 在普通隐式 Intent 多匹配且没有首选项时构造的兜底结果;ChooserActivity 是应用通过 Intent.createChooser() 明确请求的分享选择器。Android 17 的 Chooser 实现在 packages/modules/IntentResolver 模块,PMS 不负责绘制分享面板,也不负责生成 Direct Share 排名。

1. 两种选择器 ​

选择器触发条件是否记录默认应用主要内容
ResolverActivity普通 Intent 无法单选支持“始终使用”ResolverListAdapter 候选
ChooserActivityACTION_CHOOSER不提供始终使用语义分享预览、Direct Share、profile tabs

2. Chooser Intent ​

源码文件:frameworks/base/core/java/android/content/Intent.java

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

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

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

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 目标 ​

java
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 的基础集合。

java
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 目标来源 ​

java
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

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 ​

java
private void updateShareResultSender() {
    IntentSender chosenComponentSender =
            mRequest.getChosenComponentSender();
    mShareResultSender = mShareResultSenderFactory.create(
            mViewModel.getActivityModel().getLaunchedFromUid(),
            chosenComponentSender);
}

Intent.createChooser(target, title, sender) 传入的 sender 会进入 ChooserRequest,再由 ShareResultSenderFactory 包装。它用于把用户选中的组件通知给调用方,与 ResolverActivity 的“设置默认应用”持久化是两条不同链。

10. 排查清单 ​

  1. 看到 ResolverActivity:检查 PMS 的 chooseBestActivity,它表示普通 Intent 歧义未解决。
  2. 看到 ChooserActivity:检查外层 Intent 是否为 ACTION_CHOOSER,并读取 EXTRA_INTENT。
  3. 候选缺失:区分 PMS app targets、Chooser 的 filtered component names、profile 可用性和 Direct Share 异步加载。
  4. “始终使用”不生效:Chooser 不写 PreferredActivity;只有 ResolverActivity 的 always 分支才会持久化默认偏好。
  5. 分享 URI 权限丢失:检查 createChooser 是否迁移 target 的 grant flags 和 ClipData。
  6. 选择后没有回调:检查 EXTRA_CHOOSER_RESULT_INTENT_SENDER 是否存在,以及 ShareResultSender 是否被创建。
  7. 工作资料列表不同:检查 cross-profile payload 重写和 profile tab 的 UserHandle。

11. 源码阅读路线 ​

  1. Intent.createChooser:确认外层 action、目标 Intent、ClipData 和授权 flags。
  2. ResolveIntentHelper.chooseBestActivity:确认普通 Intent 何时得到 ResolverActivity。
  3. packages/modules/IntentResolver/.../ResolverActivity.java:阅读 always/once 选择和 PreferredActivity 写入。
  4. packages/modules/IntentResolver/.../ChooserActivity.java:阅读 ChooserRequest、profile records 和选择回调。
  5. createMultiProfilePagerAdapter:区分 app targets、initial intents、cross-profile payload。
  6. onTargetSelected/startSelected:确认 refinement、预测统计、文本处理和目标启动顺序。

ChooserActivity 是“应用明确要求用户选择”的分享工作流;ResolverActivity 是 PMS 在普通解析无法单选时返回的系统兜底。两者都展示候选,但默认偏好、数据来源、跨 profile 处理和回调语义完全不同。