将数据发送到其他应用

Android 通过 intent 及其关联的 extra 让用户可以使用自己喜爱的应用快速分享信息。

Android 为用户提供了两种在应用之间分享数据的方式:

  • Android Sharesheet 主要用于将内容发送到应用外部和/或直接 发送给其他用户。例如,将网址分享给朋友。
  • Android intent 解析器最适合将数据传递到 明确定义的任务的下一个阶段。例如,从您的应用中打开 PDF,并让用户 选择他们偏好的查看器。

构建 intent 时,您需要指定希望 intent 执行的操作。 Android 使用 ACTION_SEND 操作将数据从一个 activity 发送到另一个 activity,甚至跨进程边界发送数据。您需要指定数据及其类型。系统会自动识别可以接收数据的兼容 activity 并将其显示给用户。对于 intent 解析器,如果只有一个 activity 可以处理相应 intent,该 activity 将立即启动。

为什么使用 Android Sharesheet

图 1.Android Sharesheet。

我们强烈建议您使用 Android Sharesheet 在各个应用之间为您的用户打造一致的体验。请勿显示您应用自己的分享目标列表,也不要创建您自己的 Sharesheet 变体。

借助 Android Sharesheet,用户只需点按一下,即可根据相关的应用建议,与合适的人员分享信息。 Sharesheet 可以推荐自定义解决方案无法提供的目标,并使用一致的排名。 这是因为 Sharesheet 可以考虑只有系统才能获取的应用和用户活动信息。

Android Sharesheet 还为开发者提供了许多方便的功能。例如,您可以执行以下操作:

使用 Android Sharesheet

对于所有类型的分享,请创建一个 intent 并将其操作设置为 Intent.ACTION_SEND。 如需显示 Android Sharesheet,请调用 Intent.createChooser(),并向其传递您的 Intent 对象。 它会返回您的 intent 的一个版本,该版本将始终显示 Android Sharesheet。

发送文本内容

Android Sharesheet 最直接也是最常见的用法是将文本内容从一个 activity 发送到另一个 activity。例如,大多数浏览器都可以将当前显示页面的网址作为文本与其他应用分享。这对于通过电子邮件或社交网络与朋友分享文章或网站非常有用。下面举例说明如何执行此操作:

fun shareText(context: Context) {
    val sendIntent: Intent = Intent().apply {
        action = ACTION_SEND
        putExtra(Intent.EXTRA_TEXT, "This is my text to send.")
        type = "text/plain"
    }

    val shareIntent = Intent.createChooser(sendIntent, null)
    context.startActivity(shareIntent)
}

(可选)您可以添加 extra 以包含更多信息,如电子邮件收件人(EXTRA_EMAILEXTRA_CCEXTRA_BCC)、电子邮件主题 (EXTRA_SUBJECT),等等。

注意 :某些电子邮件应用(如 Gmail)要求以 String[] 的形式表示诸如 EXTRA_EMAILEXTRA_CC 之类的 extra,请使用 `putExtra(String, String[])` 将此类 extra 添加到您的 intent 中。使用 putExtra(String, String[]) 将这些内容添加到 intent 中。

发送二进制内容

使用 ACTION_SEND 操作分享二进制数据。 应设置适当的 MIME 类型,并在 extra EXTRA_STREAM 中添加数据的 URI,如以下示例所示。 这通常用于分享图片,但可以用于分享任何类型的二进制内容。

fun shareBinaryContent(context: Context) {
    val shareIntent: Intent = Intent().apply {
        action = ACTION_SEND
        // Example: content://com.google.android.apps.photos.contentprovider/...
        val imageUri: Uri =
            Uri.parse("content://com.google.android.apps.photos.contentprovider/0/1/mediakey/1")
        putExtra(Intent.EXTRA_STREAM, imageUri)
        type = "image/jpeg"
    }
    context.startActivity(Intent.createChooser(shareIntent, null))
}

接收应用需要具备相应的权限才能访问 Uri 指向的数据。我们建议采用以下两种方式:

  • 将数据存储在您自己的 ContentProvider 中,确保其他应用具有访问您的提供程序的相应权限。提供访问权限的首选机制是使用按 URI 的权限,这是 临时权限,并且仅向接收应用授予访问权限。如需创建此类 ContentProvider,请使用 FileProvider 帮助程序类。
  • 使用系统 MediaStoreMediaStore 主要用于存储视频、音频和图片 MIME 类型的数据,不过从 Android 3.0(API 级别 11)开始,它也可以存储非媒体类型的数据。如需了解详情,请参阅 MediaStore.Files。 可以使用 scanFile() 将文件插入 MediaStore,之后,系统会将适合分享的 content:// 样式的 Uri 传递给提供的 onScanCompleted() 回调。请注意,添加到系统 MediaStore 后,设备上的任何应用都可以访问该内容。

使用正确的 MIME 类型

为要发送的数据提供最具体的可用 MIME 类型。例如,分享纯文本时,请使用 text/plain。以下是在 Android 中发送数据时一些常用的 MIME 类型:

接收器注册 发送器发送
text/*
  • text/plain
  • text/rtf
  • text/html
  • text/json
`image/*`
  • image/jpg
  • image/png
  • image/gif
video/*
  • video/mp4
  • video/3gp
支持的文件扩展名 application/pdf

如需详细了解 MIME 类型,请参阅 IANA 官方 MIME 媒体类型注册表。

Android Sharesheet 可能会根据提供的 MIME 类型显示内容预览。某些 预览功能仅适用于特定类型。

分享多份内容

如需分享多份内容,请将 ACTION_SEND_MULTIPLE 操作与指向该内容的一系列 URI 一起使用。MIME 类型因您所分享的混合内容而有所不同。例如,如果您分享三张 JPEG 图片,则使用 "image/jpg" 类型。但如果分享混合类型的图片,类型应为 "image/*",以与处理任何类型的图片的 activity 匹配。虽然可以分享混合类型的内容,但我们强烈建议您不要这样做,因为接收者不清楚发送者打算发送的内容。如果必须发送多种类型的内容,请使用 "*/*"。由接收应用解析和处理数据。示例如下:

fun shareMultiple(context: Context) {
    val imageUris: ArrayList<Uri> = arrayListOf(
        Uri.parse("content://com.google.android.apps.photos.contentprovider/0/1/mediakey/1"),
        Uri.parse("content://com.google.android.apps.photos.contentprovider/0/1/mediakey/2")
    )

    val shareIntent = Intent().apply {
        action = Intent.ACTION_SEND_MULTIPLE
        putParcelableArrayListExtra(Intent.EXTRA_STREAM, imageUris)
        type = "image/*"
    }
    context.startActivity(Intent.createChooser(shareIntent, null))
}

确保提供的 Uri 对象指向接收应用可以访问的数据。

向文本预览添加富媒体内容

从 Android 10(API 级别 29)开始,Android Sharesheet 可以显示分享的文本的预览。在某些情况下,分享的文本可能难以理解。不妨考虑分享复杂的网址,例如 https://www.google.com/search?ei=2rRVXcLkJajM0PEPoLy7oA4。更丰富的预览可以确保用户了解分享的内容。

如需预览文本,您可以设置标题和/或缩略图。在调用 Intent.createChooser() 之前,向 Intent.EXTRA_TITLE 添加说明,并使用 ClipData 添加相关缩略图。

注意:图片内容 URI 通过 FileProvider(通常来自配置的<cache-path>)提供。 如需了解详情,请参阅共享文件。确保授予 Sharesheet 正确的权限,让其能够读取要用作缩略图的任何图片。如需了解详情,请参阅 Intent.FLAG_GRANT_READ_URI_PERMISSION

示例如下:

fun richContentToTextPreviewShares(context: Context) {
    val share = Intent.createChooser(
        Intent().apply {
            action = ACTION_SEND
            putExtra(Intent.EXTRA_TEXT, "https://developer.android.com/training/sharing/")

            // (Optional) Here you're setting the title of the content
            putExtra(Intent.EXTRA_TITLE, "Introducing content previews")

            // (Optional) Here you're passing a content URI to an image to be displayed
            data =
                Uri.parse("content://com.google.android.apps.photos.contentprovider/0/1/mediakey/A123456789")
            flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
        },
        null
    )
    context.startActivity(share)
}

预览效果如下所示:

图 2.Android Sharesheet 上的文本内容预览。

向 Sharesheet 添加自定义操作

图 3.Android Sharesheet 上的自定义操作。

在 Android 14(API 级别 34)及更高版本中,应用可以向 Android Sharesheet 添加自定义操作。 自定义操作在 Android Sharesheet 顶部显示为小操作图标,应用可以将任何 Intent 指定为点击该图标时调用的操作。

如需在 Android Sharesheet 上添加自定义操作,请先使用 ChooserAction.Builder 创建 ChooserAction 。 您可以将 PendingIntent 指定为点击该图标时调用的操作。创建一个包含所有自定义操作的数组,并将其指定为分享 IntentEXTRA_CHOOSER_CUSTOM_ACTIONS

fun sharesheetCustomActions(context: Context, previewText: String) {
    val sendIntent = Intent(ACTION_SEND)
        .setType("text/plain")
        .putExtra(Intent.EXTRA_TEXT, previewText)
    val shareIntent = Intent.createChooser(sendIntent, null)
    val customActions = arrayOf(
        ChooserAction.Builder(
            Icon.createWithResource(context, R.drawable.ic_logo),
            "Custom",
            PendingIntent.getBroadcast(
                context,
                1,
                Intent(Intent.ACTION_VIEW),
                PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_CANCEL_CURRENT
            )
        ).build()
    )
    shareIntent.putExtra(Intent.EXTRA_CHOOSER_CUSTOM_ACTIONS, customActions)
    context.startActivity(shareIntent)
}

添加自定义目标

通过 Android Sharesheet,您可以指定最多两个 ChooserTarget 对象,这些对象 在分享快捷方式和通过 ChooserTargetServices 加载的选择器目标之前显示。此外,您还可以指定最多两个指向 activity 的 intent,在应用推荐之前列出:

图 4.Android Sharesheet 上的自定义目标。

在调用 Intent.createChooser() 之后,将 Intent.EXTRA_CHOOSER_TARGETSIntent.EXTRA_INITIAL_INTENTS 添加到您的分享 intent:

val share = Intent.createChooser(shareIntent, null).apply {
    putExtra(
        Intent.EXTRA_CHOOSER_TARGETS,
        arrayOf(chooserTargetJessica, chooserTargetSpyros)
    )
    putExtra(
        Intent.EXTRA_INITIAL_INTENTS,
        arrayOf(intentTargetNearbyShare, intentTargetMaps)
    )
}

请谨慎使用此功能。您添加的每个自定义 IntentChooserTarget 都会减少系统推荐的数量。我们通常不建议添加自定义目标。添加 Intent.EXTRA_INITIAL_INTENTS 的一个常见适当示例是提供用户可以对共享内容执行的其他操作。例如,用户分享图片,并使用 Intent.EXTRA_INITIAL_INTENTS 让用户改为发送链接。添加 Intent.EXTRA_CHOOSER_TARGETS 的一个常见适当示例是公开您的应用提供的相关人员或设备。

按组件排除特定目标

您可以通过提供 Intent.EXTRA_EXCLUDE_COMPONENTS 来排除特定目标。 仅当您要移除的目标在您的控制范围内时,才执行此操作。一个常见的用例是,当用户从您的应用内分享内容时,隐藏您应用的分享目标,因为他们的意图很可能是分享到您的应用之外。

在调用 Intent.createChooser() 之后,将 Intent.EXTRA_EXCLUDE_COMPONENTS 添加到您的 intent:

fun excludeSpecificTargets(context: Context) {
    val share = Intent.createChooser(Intent(ACTION_SEND), null).apply {
        // Only use for components you have control over
        val excludedComponentNames =
            arrayOf(ComponentName("com.example.android", "ExampleClass"))
        putExtra(Intent.EXTRA_EXCLUDE_COMPONENTS, excludedComponentNames)
    }
    context.startActivity(share)
}

获取有关分享的信息

了解用户何时分享以及他们选择的目标可能很有用。Android Sharesheet 可让您通过使用 IntentSender 提供用户选择的目标的 ComponentName 来获取此信息。

首先为 BroadcastReceiver 创建 PendingIntent,并在 Intent.createChooser() 中提供其 IntentSender

fun infoAboutSharing(context: Context, requestCode: Int) {
    var share = Intent(ACTION_SEND)
    // ...
    val pi = PendingIntent.getBroadcast(
        context, requestCode,
        Intent(context, ShareBroadcastReceiver::class.java),
        PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
    )
    share = Intent.createChooser(share, null, pi.intentSender)
    context.startActivity(share)
}

MyBroadcastReceiver 中接收回调,并在 Intent.EXTRA_CHOOSER_RESULT 中查找:

override fun onReceive(context: Context?, intent: Intent) {
    val TAG = ShareBroadcastReceiver::class.simpleName
    val chooserResult: ChooserResult? = IntentCompat.getParcelableExtra(
        intent,
        Intent.EXTRA_CHOOSER_RESULT,
        ChooserResult::class.java,
    )
    chooserResult?.let {
        Log.i(TAG,
            "Share callback: isShortcut: ${it.isShortcut}, type: ${typeToString(it.type)}, componentName: ${it.selectedComponent}",
        )
    } ?: Log.i(TAG, "chooserResult is null")
}

如需了解详情,请参阅平台分享示例

使用 Android intent 解析器

图 5.ACTION_SEND intent 解析器。

在明确定义的任务流中将数据发送到另一个应用时,使用 Android intent 解析器最为合适。

如需使用 Android intent 解析器,请创建一个 intent 并添加 extra,就像调用 Android Sharesheet 时执行的操作一样。不过,请勿调用 Intent.createChooser()

如果有多个安装的应用符合 ACTION_SEND 和 MIME 类型过滤条件,系统将显示一个称为“intent 解析器”的消除歧义对话框,使用户能选择要与之分享内容的目标。 如果只有一个应用符合条件,系统会运行该应用。

下面举例说明如何使用 Android intent 解析器发送文本:

fun intentResolver(context: Context) {
    val sendIntent: Intent = Intent().apply {
        action = ACTION_SEND
        putExtra(Intent.EXTRA_TEXT, "This is my text to send.")
        type = "text/plain"
    }
    context.startActivity(sendIntent)
}

了解详情

如需详细了解如何发送数据,请参阅 intent 和 intent 过滤器。