CATEGORY_ALTERNATIVE 的目的到底是什么

What exactly is the purpose of CATEGORY_ALTERNATIVE

我一直在阅读有关 CATEGORY_ALTERNATIVE 的文档,我想知道 "an alternative action to the data the user is currently viewing" 的确切含义。

有人可以提供更多有关如何使用此类别的示例吗?

我只找到了一些关于创建 Menus 的信息和一些引用 "When the system offers the user a choice of activities to do a job, the system lists activities with filters possessing this category"。但是话又说回来,在第二种情况下 - 如果我们需要包含 CATEGORY_DEFAULT 以便 activity 接收隐式意图,这将如何工作。

假设您正在使用某个 Messenger 应用程序,并且一位朋友向您发送了一个有趣的 link。然后你可以用它做几件事:

  • 在浏览器中打开它
  • 与他人分享
  • 也许可以保存在一些笔记应用程序中
  • ...

如果您将 link 视为 "data you are currently viewing" 那么如果 Messenger 为您提供一个选项以 select 从目前可用的一系列替代操作中选择合适的操作就好了在您的设备上可用。

使用 Intent.CATEGORY_ALTERNATIVEMenu.addIntentOptions(),框架为应用程序提供了一种方法,该应用程序是某种类型数据的初始 "owner",可以让用户访问进一步的操作。这些操作可以由设备上 运行 并且通过 Manifest.xml 中的条目声明其处理此特定数据类型的能力的任何应用程序实现,更具体地说是在 <intent-filter> 标签中.

让我们考虑一个非常简单(如果不是很现实)的例子:

我的应用有一些 String 值显示给用户。备选行动可以是

  • 根据一些正则表达式
  • 解析 String
  • 正在对 String
  • 的字母进行排序
  • 转换 String,例如大写字母
  • ...

我的应用程序将 ActivityFragment 覆盖 onCreateOptionsMenu() 如下(Fragment 的代码片段)

@Override
public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) {
    super.onCreateOptionsMenu(menu, inflater);
    inflater.inflate(R.menu.home_menu, menu);

    // Create an Intent that describes the requirements to fulfill, to be included
    // in our menu. The offering app must include a category value of Intent.CATEGORY_ALTERNATIVE.
    Intent intent = new Intent();
    intent.putExtra(Intent.EXTRA_TEXT, "Lorem ipsum");
    intent.setType("text/plain");
    intent.addCategory(Intent.CATEGORY_ALTERNATIVE);

    // Search and populate the menu with acceptable offering applications.
    menu.addIntentOptions(
            R.id.intent_group,  // Menu group to which new items will be added
            0,      // Unique item ID (none)
            0,      // Order for the items (none)
            this.getActivity().getComponentName(),   // The current activity name
            null,   // Specific items to place first (none)
            intent, // Intent created above that describes our requirements
            0,      // Additional flags to control items (none)
            null);  // Array of MenuItems that correlate to specific items (none)
}

能够处理给定 StringActivity 将在 Manifest.xml 中声明一个 IntentFilter 作为其标签的 child 标签:

<activity android:name=".ToUpperCaseActivity">
      <intent-filter android:label="@string/label_action_text_to_uppercase">
            <action android:name="de.ddvsidedown.alternativeoptionsapp.action.ALL_CAPS" />
            <category android:name="android.intent.category.ALTERNATIVE" />
            <data android:mimeType="text/plain" />
        </intent-filter>
</activity>

<activity android:name=".SortActivity">
        <intent-filter android:label="@string/label_action_sort_text">
            <action android:name="de.ddvsidedown.alternativeoptionsapp.action.SORT" />
            <category android:name="android.intent.category.ALTERNATIVE" />
            <data android:mimeType="text/plain" />
        </intent-filter>
</activity>

然后当用户点击溢出图标时,我的应用程序将显示以下弹出菜单...

... 运行时将直接导航到所选的 Activity。在那里,可以按如下方式检索文本:

 if(getIntent().hasExtra(Intent.EXTRA_TEXT)) {

        char[] ca = getIntent().getCharSequenceExtra(Intent.EXTRA_TEXT).toString().toCharArray();
        Arrays.sort(ca);
        String message = buildString(ca);
        Log.d(TAG, "onCreate: " + message);
        TextView textView = findViewById(R.id.tvResult);
        textView.setText(message);
}

请注意,检索数据的确切代码取决于数据类型,就像 "normal" Intent 完全没有 <category> 标记或 <category android:name="android.intent.category.DEFAULT" /><intent-filter> .

how does this work if we need to include CATEGORY_DEFAULT in order for an activity to receive implicit intents

如果您想让 Activity 出现在选择器对话框中,您需要在 Activity<intent-filter> 中添加 <category android:name="android.intent.category.DEFAULT"/>。顺便说一下,一个 Activity 可以有多个 <intent-filter> 标签

我们修改一下例子:我的app也有"SHARE"Button。如果点击,则执行以下代码:

 /**
 * Will only work with <category android:name="android.intent.category.DEFAULT" />
 */
private void shareText() {
    // Create the text message with a string
    Intent sendIntent = new Intent();
    //sendIntent.setAction(Intent.ACTION_SEND);
    sendIntent.putExtra(Intent.EXTRA_TEXT, "Lorem ipsum");
    sendIntent.setType("text/plain");

    // Verify that the intent will resolve to an activity
    if (sendIntent.resolveActivity(getActivity().getPackageManager()) != null) {
        startActivity(sendIntent);
    }
}

运行时将向用户显示一个选择器对话框。每个带有 <intent-filter><category android:name="android.intent.category.DEFAULT"/>Activity 都将与应用程序标题和应用程序的启动器图标一起显示。 (虽然在同一个应用程序中可以有两个 ActivityCATEGORY_DEFAULT,但很明显这看起来不太好 - 可以调整文本和/或图标,但这绝对是超出本题范围)

如果我注释掉 ACTION_SEND,则选择器对话框的标题为 "Complete action using",而如果我设置 ACTION_SEND,则标题为 "Share with".

how [does] the system decide[...] about handling many apps with 'alternative' category especially without knowing the action name

我的应用通过设置(或省略)操作、类别以及数据类型来配置其 Intent。运行时总是选择符合我的 Intent 的所有要求的 Activitys。这意味着,另一个 Activity 可能会提供处理数据类型 "text/*" 而不仅仅是 "text/plain" 但如果它只提供 "image/*" 则不会向用户显示。