从其他应用接收简单的数据

就像应用可以向其他应用发送数据一样,它也可以从其他应用接收数据。想一想用户如何与您的应用互动,以及您希望从其他应用接收哪些类型的数据。例如,社交网络应用可能希望从其他应用接收文本内容(比如有趣的网址)。

用户通常会通过 Android Sharesheet 或 intent 解析器向您的应用发送数据。接收到的所有数据都具有由提供数据的应用所设定的 MIME 类型。您的应用可以通过三种方式接收其他应用发送的数据:

  • 在清单中有匹配的 intent-filter 标记的 Activity
  • 由您的 ChooserTargetService 返回的一个或多个 ChooserTarget 对象
  • 您的应用发布的共享快捷方式。这些会取代 ChooserTarget 对象。只有当您的应用在 Android 10(API 级别 29)平台上运行时,才可以使用共享快捷方式。

共享快捷方式和 ChooserTarget 对象是深入到您应用中的特定 Activity 的直接共享链接。它们通常代表一个真实的人,并由 Android Sharesheet 显示。例如,短信应用可以提供某个人的快捷方式,该快捷方式可以直接深入链接到与此人的对话。

支持 MIME 类型

您的应用应支持接收尽可能广泛的 MIME 类型。例如,用于发送文本、图片和视频的即时通讯应用应支持接收 text/*image/*video/*。以下是在 Android 中发送简单数据时一些常用的 MIME 类型。

  • text/*,发送方经常发送 text/plaintext/rtftext/htmltext/json
  • image/*,发送方经常发送 image/jpgimage/pngimage/gif
  • video/*,发送方经常发送 video/mp4video/3gp
  • 接收方应注册受支持的文件扩展名,发送方通常会发送 application/pdf

请参考 MIME 媒体类型的 IANA 官方注册表。您可以接收 */* MIME 类型,但除非您能够处理任何类型的传入内容,否则强烈建议您不要这样做。

制作出色的共享目标

当用户点按与特定 Activity·关联的共享目标时,他们应该能够在使用共享内容之前对内容进行确认和编辑。这对于文本数据尤为重要。

点按任何直接共享目标都应该将用户转到可直接对目标主题执行操作的界面。避免向用户显示信息确认界面或将用户转到与所点按的目标无关的界面。具体来说,不要将用户转到联系人确认界面来让他们确认或重新选择要向其共享内容的联系人,因为用户已通过点按 Android Sharesheet 中的目标完成了这一操作。例如在即时通讯应用中,点按直接共享目标应将用户转到与所选人员进行对话的视图。键盘应处于可见状态,并且消息中应预填了共享的数据。

通过 Activity 接收数据

更新您的清单

intent 过滤器会告知系统,应用组件可以接受哪些 intent。您在将简单的数据发送到其他应用一课中学习了如何通过 ACTION_SEND 操作构建 intent,与此类似,您也可以创建 intent 过滤器,以便能够通过此操作接收 intent。使用 <intent-filter> 元素在清单中定义一个 intent 过滤器。例如,如果您的应用能够处理文本内容接收、单个任意类型的图片或多个任意类型的图片,您的清单内容将如下所示:

    <activity android:name=".ui.MyActivity" >
        <intent-filter>
            <action android:name="android.intent.action.SEND" />
            <category android:name="android.intent.category.DEFAULT" />
            <data android:mimeType="image/*" />
        </intent-filter>
        <intent-filter>
            <action android:name="android.intent.action.SEND" />
            <category android:name="android.intent.category.DEFAULT" />
            <data android:mimeType="text/plain" />
        </intent-filter>
        <intent-filter>
            <action android:name="android.intent.action.SEND_MULTIPLE" />
            <category android:name="android.intent.category.DEFAULT" />
            <data android:mimeType="image/*" />
        </intent-filter>
    </activity>
    

当另一个应用尝试通过构建 intent 并将它传递给 startActivity() 来共享任何这些内容时,您的应用都将在 Android Sharesheet 或 intent 解析器中列为一个选项。如果用户选择您的应用,相应的 Activity(上例中的 .ui.MyActivity)将会启动。然后,您需要在自己的代码和界面中妥当处理相关内容。

注意:如需详细了解 intent 过滤器和 intent 解析,请参阅 intent 和 intent 过滤器

处理传入的内容

如需处理 Intent 传送的内容,请调用 getIntent() 来获取 Intent 对象。获得该对象后,您就可以检查其内容以确定后续操作。请注意,如果此 Activity 可从系统的其他部分(例如启动器)启动,您在检查 Intent 时需要将这一点考虑在内。

要格外注意检查传入的数据,因为您永远不知道其他应用可能会向您发送什么。例如,可能设置了错误的 MIME 类型,或者所发送的图像可能非常大。另外,请记住,一定要在主(“界面”)线程以外的独立线程中处理二进制数据。

Kotlin

    override fun onCreate(savedInstanceState: Bundle?) {
        ...
        when {
            intent?.action == Intent.ACTION_SEND -> {
                if ("text/plain" == intent.type) {
                    handleSendText(intent) // Handle text being sent
                } else if (intent.type?.startsWith("image/") == true) {
                    handleSendImage(intent) // Handle single image being sent
                }
            }
            intent?.action == Intent.ACTION_SEND_MULTIPLE
                    && intent.type?.startsWith("image/") == true -> {
                    handleSendMultipleImages(intent) // Handle multiple images being sent
            }
            else -> {
                // Handle other intents, such as being started from the home screen
            }
        }
        ...
    }

    private fun handleSendText(intent: Intent) {
        intent.getStringExtra(Intent.EXTRA_TEXT)?.let {
            // Update UI to reflect text being shared
        }
    }

    private fun handleSendImage(intent: Intent) {
        (intent.getParcelableExtra<Parcelable>(Intent.EXTRA_STREAM) as? Uri)?.let {
            // Update UI to reflect image being shared
        }
    }

    private fun handleSendMultipleImages(intent: Intent) {
        intent.getParcelableArrayListExtra<Parcelable>(Intent.EXTRA_STREAM)?.let {
            // Update UI to reflect multiple images being shared
        }
    }
    

Java

    void onCreate (Bundle savedInstanceState) {
        ...
        // Get intent, action and MIME type
        Intent intent = getIntent();
        String action = intent.getAction();
        String type = intent.getType();

        if (Intent.ACTION_SEND.equals(action) && type != null) {
            if ("text/plain".equals(type)) {
                handleSendText(intent); // Handle text being sent
            } else if (type.startsWith("image/")) {
                handleSendImage(intent); // Handle single image being sent
            }
        } else if (Intent.ACTION_SEND_MULTIPLE.equals(action) && type != null) {
            if (type.startsWith("image/")) {
                handleSendMultipleImages(intent); // Handle multiple images being sent
            }
        } else {
            // Handle other intents, such as being started from the home screen
        }
        ...
    }

    void handleSendText(Intent intent) {
        String sharedText = intent.getStringExtra(Intent.EXTRA_TEXT);
        if (sharedText != null) {
            // Update UI to reflect text being shared
        }
    }

    void handleSendImage(Intent intent) {
        Uri imageUri = (Uri) intent.getParcelableExtra(Intent.EXTRA_STREAM);
        if (imageUri != null) {
            // Update UI to reflect image being shared
        }
    }

    void handleSendMultipleImages(Intent intent) {
        ArrayList<Uri> imageUris = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM);
        if (imageUris != null) {
            // Update UI to reflect multiple images being shared
        }
    }
    

收到数据之后更新界面的操作可能很简单(比如只需填充 EditText),也可能比较复杂(比如需要将有趣的照片滤镜应用到图片)。接下来执行什么操作取决于您的应用。

确保用户能够识别您的应用

在 Android Sharesheet 和 intent 解析器中,您的应用由其图标标签表示。两者都在清单中定义。您可以设置 Activity 或 intent 过滤器标签来提供更多上下文。

从 Android 10(API 级别 29)开始,Android Sharesheet 将仅使用清单中的 application 标签上设置的图标。在 intent-filteractivity 标签上设置的图标将被忽略。

注意:最佳共享目标不需要在相关的 Activity 或 intent 过滤器中设置标签和图标。用户只需通过接收方应用的名称和图标就可以理解共享时会发生什么情况。

提供直接共享目标

直接共享于 Android 6.0(API 级别 23)中引入,支持应用通过 ChooserTargetService 提供 ChooserTarget 对象。在此机制下,系统会根据需要被动检索结果,从而造成目标加载缓慢。

在 Android 10(API 级别 29)中,新的 Sharing Shortcuts API 取代了 ChooserTargetService Direct Share API。Sharing Shortcuts API 允许应用提前发布直接共享目标,而不是被动地按需检索结果。ChooserTargetService 直接共享机制将继续有效,但以这种方式提供的目标的排名要低于使用 Sharing Shortcuts API 提供的目标。

ShortcutManagerCompat 是一个 AndroidX API,提供向后兼容旧版 ChooserTargetService DirectShare API 的共享快捷方式。这是发布共享快捷方式和 ChooserTargets 的首选方式。请参阅下面的说明

发布直接共享目标

您只能使用动态快捷方式来发布直接共享目标。按照以下步骤使用新 API 发布直接共享目标:

  1. 在应用的 XML 资源文件中声明 share-target 元素。如需了解详情,请参阅下面的声明共享目标部分。
  2. 将类别匹配的动态快捷方式发布到声明的 share-target。可以使用 AndroidX 中的 ShortcutManagerShortcutManagerCompat 添加、访问、更新和移除快捷方式。使用 AndroidX 中的兼容库是首选方法,因为它可以向后兼容更低版本的 Android 系统。

Sharing Shortcuts API

ShortcutInfo.Builder 包含新的以及改进的方法,可提供有关共享目标的更多信息:

setCategories()
这不是一种新方法,但现在也将类别用于过滤可以处理共享 intent 或操作的快捷方式。如需了解详情,请参阅声明共享目标部分。要用作共享目标的快捷方式必须具有此字段。
setLongLived()

指定快捷方式在由应用取消发布或设为不可见后(作为动态或固定快捷方式)是否有效。如果某个快捷方式长期存在,则可能被各种系统服务缓存,即使已取消发布为动态快捷方式也是如此。

使快捷方式长期存在有助于提高其排名。如需了解详情,请参阅获得最佳排名

setPerson()setPersons()

将一个或多个 Person 对象关联到快捷方式。可以通过这种方式更好地了解用户在不同应用中的行为,并帮助框架中的潜在预测服务在 ShareSheet 中提供更好的建议。在快捷方式中添加 Person 信息属于可选操作,但如果共享目标可以与 Person 关联,则强烈建议您这样做。请注意,某些共享目标(例如云)无法与 Person 相关联。

在共享目标和相关通知中添加具有唯一键的特定 Person 对象可以提高目标的排名。如需了解详情,请参阅获得最佳排名

对于典型的短信应用,应该为每个联系人发布单独的快捷方式,并且 Person 字段应该包含该联系人的信息。如果目标可以与多个人关联(例如群聊),请将多个 Person 对象添加到单个共享目标。

发布单个 Person 的快捷方式时,请在 setLongLabel() 中添加其全名,并在 setShortLabel() 中添加其昵称或名字等简称。

如需查看发布共享快捷方式的示例,请参阅共享快捷方式示例代码

获得最佳排名

Android Sharesheet 显示固定数量的直接共享目标,并按照排名对这些建议进行排序。以下方法可能有助于您提高快捷方式的排名:

  • 确保所有 shortcutId 独一无二,且不在不同的目标上重复使用。
  • 通过调用 setLongLived(true) 确保快捷方式长期存在。
  • 提供一个排名,以便在缺少训练数据的情况下,可以用来比较应用中的快捷方式。请参阅 setRank()。排名越高,表示快捷方式越重要。

为了进一步提高排名,我们强烈建议社交应用采取以上所有做法,并且:

为尽可能提高排名,社交应用可以采取以上所有做法,并且:

  • 在提供的 Person 对象中,提供设备上的关联联系人的有效 URI,请参阅 setUri()

以下是应用了所有可能的排名改进方法的快捷方式示例。

Kotlin

    val person = Person.Builder()
      ...
      .setName(fullName)
      .setKey(staticPersonIdentifier)
      .setUri("tel:$phoneNumber") // alternatively "mailto:$email" or CONTENT_LOOKUP_URI
      .build()

    val shortcutInfo = ShortcutInfoCompat.Builder(myContext, staticPersonIdentifier)
      ...
      .setShortLabel(firstName)
      .setLongLabel(fullName)
      .setPerson(person)
      .setLongLived(true)
      .setRank(personRank)
      .build()
    

Java

    Person person = Person.Builder()
      ...
      .setName(fullName)
      .setKey(staticPersonIdentifier)
      .setUri("tel:"+phoneNumber) // alternatively "mailto:"+email or CONTENT_LOOKUP_URI
      .build();

    ShortcutInfoCompat shortcutInfo = ShortcutInfoCompat.Builder(myContext, staticPersonIdentifier)
      ...
      .setShortLabel(firstName)
      .setLongLabel(fullName)
      .setPerson(person)
      .setLongLived(true)
      .setRank(personRank)
      .build();
    

Kotlin

    val notif = NotificationCompat.Builder(myContext, channelId)
      ...
      .setShortcutId(staticPersonIdentifier)
      .build()
    

Java

    Notification notif = NotificationCompat.Builder(myContext, channelId)
      ...
      .setShortcutId(staticPersonIdentifier)
      .build();

    

提供快捷方式图像

要制作共享快捷方式,您需要通过 setIcon() 添加图像。

共享快捷方式可以跨系统 Surface 显示,并且可能会被改变外观。如需确保快捷方式的显示方式与预期相符,可以通过 IconCompat.createWithAdaptiveBitmap() 提供自适应位图。

自适应位图应遵循为自适应图标设定的准则和尺寸。最常见的实现方法是将所需的正方形位图缩放至 72x72dp 的大小,并将它居中放置在 108x108dp 的透明画布中。

不要提供框在特定形状中的图像。比如,在 Android 10(API 级别 29)之前,经常会为直接共享 ChooserTarget 提供框在圆形中的用户头像。现在,Android 10 中的 Android Sharesheet 和其他系统 Surface 可为快捷方式图像设置形状和主题背景。通过 ShortcutManagerCompat 提供共享快捷方式的首选方式,可以自动为您将向后兼容的直接共享 ChooserTarget 的形状设为圆形。

声明共享目标

共享目标必须在应用的资源文件中声明,这与静态快捷方式定义类似。将共享目标定义连同其他静态快捷方式定义一起添加到资源文件中的 <shortcuts> 根元素中。每个 <share-targets> 元素都包含与共享数据类型、匹配类别以及将处理共享 intent 的目标类有关的信息。XML 代码如下所示:

    <shortcuts xmlns:android="http://schemas.android.com/apk/res/android">
      <share-target android:targetClass="com.example.android.sharingshortcuts.SendMessageActivity">
        <data android:mimeType="text/plain" />
        <category android:name="com.example.android.sharingshortcuts.category.TEXT_SHARE_TARGET" />
      </share-target>
    </shortcuts>
    

共享目标中的数据元素与 intent 过滤器中的数据规范类似。每个共享目标都可以有多个类别,这些类别仅用于将应用的已发布快捷方式匹配到相应的共享目标定义。类别可以具有应用定义的任意值。

如果用户在 Android Sharesheet 中选择与上述 share-target 示例相匹配的共享快捷方式,应用将获得以下共享 intent:

    Action: Intent.ACTION_SEND
    ComponentName: {com.example.android.sharingshortcuts /
                    com.example.android.sharingshortcuts.SendMessageActivity}
    Data: Uri to the shared content
    EXTRA_SHORTCUT_ID: <ID of the selected shortcut>
    

如果用户从启动器快捷方式打开共享目标,则应用将获得在将共享快捷方式添加到 ShortcutManagerCompat 时创建的 intent。由于这是另一个 intent,Intent.EXTRA_SHORTCUT_ID 将不可用;如果需要,您必须手动传递 ID。

使用 AndroidX 提供共享快捷方式和 ChooserTarget

为了能够使用 AndroidX 兼容库,应用清单必须包含元数据 chooser-target-service 和 intent-filter 集。请参阅当前的 ChooserTargetService Direct Share API。

由于此服务已在兼容库中声明,因此用户无需在应用清单中声明此服务。但是,必须将从共享 Activity 到该服务的链接视为选择器目标提供程序。

在以下示例中,ChooserTargetService 的实现为 androidx.core.content.pm.ChooserTargetServiceCompat,已在 AndroidX 中定义:

    <activity
        android:name=".SendMessageActivity"
        android:label="@string/app_name"
        android:theme="@style/SharingShortcutsDialogTheme">
        <!-- This activity can respond to Intents of type SEND -->
        <intent-filter>
            <action android:name="android.intent.action.SEND" />
            <category android:name="android.intent.category.DEFAULT" />
            <data android:mimeType="text/plain" />
        </intent-filter>
        <!-- Only needed if you import the sharetarget AndroidX library that
             provides backwards compatibility with the old DirectShare API.
             The activity that receives the Sharing Shortcut intent needs to be
             taken into account with this chooser target provider. -->
        <meta-data
            android:name="android.service.chooser.chooser_target_service"
            android:value="androidx.sharetarget.ChooserTargetServiceCompat" />
    </activity>
    

共享快捷方式常见问题解答

当前的 Sharing Shortcuts API 和旧版 ChooserTargetService Direct Share API 的主要区别是什么?

Sharing Shortcuts API 采用推送模型,而旧版 Direct Share API 采用的是提取模型。采用推送模型可以在准备 ShareSheet 时,大幅提高检索直接共享目标的速度。从应用开发者的角度来看,在使用新版 API 时,应用需要提前提供直接共享目标列表,并且每当应用的内部状态发生变化(例如,如果在短信应用中添加了新联系人)时都可能需要更新快捷方式列表。

如果不迁移到使用 Sharing Shortcuts API,会发生什么?

在 Android 10(API 级别 29)及更高版本中,Android Sharesheet 将为使用 Sharing Shortcuts API 通过 ShortcutManager 提供的共享目标设定更高的优先级。因此,您发布的共享目标可能会被其他应用的共享目标淹没,并且可能永远不会在进行共享时显示。

我能否在应用中同时使用 ChooserTargetService 和 Sharing Shortcuts DirectShare API,以实现向后兼容性?

请不要这样做!请使用所提供的支持库 API ShortcutManagerCompat。混用这两组 API 可能会导致在检索共享目标时出现异常行为。

共享目标的已发布快捷方式与启动器快捷方式(长按启动器中的应用图标时通常使用这些快捷方式)有何不同?

为“共享目标”发布的快捷方式也是一种启动器快捷方式,当长按您的应用图标时,它会显示在菜单中。每个 Activity 的快捷方式数量上限也适用于应用发布的快捷方式总数(共享目标和旧版启动器快捷方式数量之和)。