將資料傳送至其他應用程式

Android 提供兩種方式,讓使用者在應用程式之間共用資料:

  • Android Sharesheet 主要用於將內容傳送到應用程式外部,或直接傳送給其他使用者。例如與好友分享網址。
  • Android 意圖解析器最適合用於將資料傳遞至定義明確工作的下一個階段。例如,從應用程式開啟 PDF,並讓使用者選擇偏好的檢視器。

使用 Android Sharesheet 的原因

圖 1. Android Sharesheet。

使用 Android Sharesheet,為使用者打造一致的跨應用程式體驗。請勿顯示應用程式自己的分享目標清單,或建立自己的 Sharesheet 變化版本。

使用者只要輕觸一下 Android Sharesheet,就能將資訊分享給合適的對象,並取得相關應用程式建議。Sharesheet 可以建議自訂解決方案無法使用的目標,並使用一致的排名。這是因為 Sharesheet 可以考量只有系統才能取得的應用程式和使用者活動資訊。

Android Sharesheet 也提供許多實用的開發人員功能。例如,您可以執行下列操作:

使用 Android Sharesheet

如要分享所有類型的內容,請建立意圖,並將其動作設為 Intent.ACTION_SEND。如要顯示 Android Sharesheet,請呼叫 Intent.createChooser(),並傳遞 Intent 物件。這個方法會傳回一律顯示 Android Sharesheet 的意圖版本。

傳送文字內容

Android Sharesheet 最直接且最常見的用途,是將文字內容從一個活動傳送到另一個活動。舉例來說,大多數瀏覽器都能以文字形式與其他應用程式分享目前顯示的網頁網址。這項功能很適合用來透過電子郵件或社群網路與好友分享文章或網站。在 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_EMAIL、EXTRA_CC、EXTRA_BCC) 或電子郵件主旨 (EXTRA_SUBJECT)。

注意:部分電子郵件應用程式 (例如 Gmail) 會預期 String[] 包含 EXTRA_EMAIL 和 EXTRA_CC 等額外項目。使用 putExtra(String, String[]) 將這些項目新增至意圖。

傳送二進位內容

使用 ACTION_SEND 動作分享二進位資料。設定適當的 MIME 類型,並將資料的 URI 放入額外的 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/*" 比對可處理任何圖片類型的活動。雖然可以共用混合類型,但請避免這樣做,因為接收者不清楚要傳送的內容。如需傳送多種類型,請使用 "*/*"。接收應用程式會剖析及處理資料。範例如下:

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.EXTRA_TITLE 新增說明,再呼叫 Intent.createChooser(),並使用 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 上的文字內容預覽。

在分享工作表中新增自訂動作

圖 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 可讓您使用 Intent.EXTRA_INITIAL_INTENTS 指定最多兩個自訂活動意圖,這些意圖會列在應用程式建議之前。如果是直接分享目標 (例如對話或聯絡人),請改用 ShortcutManagerCompat 發布動態捷徑 (請參閱「提供直接分享目標」):

圖 4. Android Sharesheet 上的自訂目標。

呼叫 Intent.createChooser()「之後」,將 Intent.EXTRA_INITIAL_INTENTS 新增至分享意圖:

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 新增至意圖:

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 意圖解析器

圖 5. ACTION_SEND 意圖解析器。

Android 意圖解析器最適合用於將資料傳送至其他應用程式,做為明確定義工作流程的一部分。

如要使用 Android 意圖解析器,請建立意圖並新增額外項目,就像呼叫 Android Sharesheet 一樣。不過,請勿呼叫 Intent.createChooser()。

如果有多個已安裝的應用程式,其篩選器與 ACTION_SEND 和 MIME 類型相符,系統會顯示名為「意圖解析器」的消歧對話方塊,讓使用者選擇要分享的目標。如果只有一個應用程式相符,就會執行該應用程式。

以下範例說明如何使用 Android 意圖解析器傳送文字:

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)
}

瞭解詳情

如要進一步瞭解如何傳送資料,請參閱「意圖和意圖篩選器」。