将数据发送到其他应用

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

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

为何使用 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 对象。它会返回一个始终显示 Android Sharesheet 的 intent 版本。

发送文本内容

Android Sharesheet 最直接、最常见的用途是将文本内容从一个 activity 发送到另一个 activity。例如,大多数浏览器都可以将当前显示的网页的网址作为文本与其他应用分享。这对于通过电子邮件或社交网络与朋友分享文章或网站非常有用。在 Jetpack Compose 中,使用 LocalContext.current 检索当前上下文,然后调用 context.startActivity()。 下面举例说明如何执行此操作:

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_EMAIL、EXTRA_CC、EXTRA_BCC)或电子邮件主题 (EXTRA_SUBJECT)。

注意:某些电子邮件应用(例如 Gmail)需要 String[] 才能添加 EXTRA_EMAIL 和 EXTRA_CC 等 extra。使用 putExtra(String, String[]) 将这些内容添加到 intent 中。

发送二进制内容

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

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 辅助类。
  • 使用系统 MediaStore。 MediaStore 主要用于视频、音频和图片 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 类型,而不是对其进行硬编码,请使用以下方法:

如果您无法确定具体的 MIME 类型,请使用 application/octet-stream 表示通用二进制数据,或使用 */* 作为最后的手段。

如需详细了解 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 作为点击该图标时调用的操作。创建一个包含所有自定义操作的数组,并将其指定为分享 Intent 的 EXTRA_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,您最多可以指定两个自定义 activity intent,这些 intent 会在使用 Intent.EXTRA_INITIAL_INTENTS 的应用建议之前列出。 对于直接分享目标(例如对话或联系人),请改用 ShortcutManagerCompat 发布动态快捷方式(请参阅提供直接分享目标):

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

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

val sendIntent = Intent(ACTION_SEND).apply {
    type = "text/plain"
    putExtra(Intent.EXTRA_TEXT, previewText)
}

val customIntent = Intent().apply {
    component = ComponentName(context.packageName, "${context.packageName}.CustomActivity")
}

val shareIntent = Intent.createChooser(sendIntent, null).apply {
    putExtra(
        Intent.EXTRA_INITIAL_INTENTS,
        arrayOf(customIntent)
    )
}
context.startActivity(shareIntent)

请谨慎使用此功能。您每添加一个自定义 Intent,系统建议的数量就会减少。一般来说,请避免添加自定义目标。添加 Intent.EXTRA_INITIAL_INTENTS 的一个常见且适当的示例是,提供用户可以对共享内容执行的其他操作。例如,用户分享图片,而 Intent.EXTRA_INITIAL_INTENTS 用于让他们改为发送链接。

按组件排除特定目标

您可以通过提供 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 过滤器。