다른 앱에 데이터 전송

Android에서는 사용자가 두 가지 방식으로 앱 간에 데이터를 공유할 수 있습니다.

  • Android Sharesheet는 주로 앱 외부나 다른 사용자에게 직접 콘텐츠를 보내도록 설계되었습니다. 예를 들어, 친구와 URL을 공유합니다.
  • 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를 가장 간단하고 일반적으로 사용하는 방법은 한 활동에서 다른 활동으로 텍스트 콘텐츠를 전송하는 것입니다. 예를 들어 대부분의 브라우저는 현재 표시된 페이지의 URL을 텍스트로 다른 앱과 공유할 수 있습니다. 이는 이메일이나 소셜 네트워킹을 통해 친구와 기사나 웹사이트를 공유하는 데 유용합니다. 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과 같은 일부 이메일 앱은 EXTRA_EMAIL 및 EXTRA_CC와 같은 추가 항목에 String[]를 예상합니다. putExtra(String, String[])를 사용하여 인텐트에 추가합니다.

바이너리 콘텐츠 전송

ACTION_SEND 작업을 사용하여 바이너리 데이터를 공유할 수 있습니다. 다음 예와 같이 적절한 MIME 유형을 설정하고 추가 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 도우미 클래스를 사용합니다.
  • 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 유형에 대한 자세한 내용은 MIME 미디어 유형의 IANA 공식 레지스트리를 참조하세요.

제공된 MIME 유형에 따라 Android Sharesheet에 콘텐츠 미리보기가 표시될 수 있습니다. 일부 프리뷰 기능은 특정 유형에만 사용할 수 있습니다.

여러 콘텐츠 공유하기

여러 콘텐츠를 공유하려면 ACTION_SEND_MULTIPLE 작업을 콘텐츠를 가리키는 URI 목록과 함께 사용합니다. MIME 유형은 공유하는 콘텐츠의 조합에 따라 다릅니다. 예를 들어 JPEG 이미지 3개를 공유하는 경우 "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와 같은 복잡한 URL을 공유한다고 가정해 보겠습니다. 더 풍부한 미리보기를 통해 사용자에게 공유되는 내용을 확인할 수 있습니다.

텍스트를 미리 보는 경우 제목, 썸네일 이미지 또는 둘 다 설정할 수 있습니다. 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의 텍스트 콘텐츠 미리보기

공유 시트에 맞춤 작업 추가

그림 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를 사용하여 앱 추천 전에 나열되는 커스텀 활동 인텐트를 최대 2개까지 지정할 수 있습니다. 직접 공유 타겟 (예: 대화 또는 연락처)의 경우 대신 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)
}

자세히 알아보기

데이터 전송에 관한 자세한 내용은 인텐트 및 인텐트 필터를 참고하세요.