Как отправлять данные в другие приложения

В Android есть два способа обмена данными между приложениями:

  • Панель "Поделиться" в Android в основном предназначена для отправки контента за пределы приложения или напрямую другому пользователю. Например, вы можете поделиться URL с другом.
  • Разрешитель намерений Android лучше всего подходит для передачи данных на следующий этап четко определенной задачи. Например, если пользователь открывает PDF-файл из вашего приложения, он может выбрать средство просмотра.

Преимущества панели "Поделиться"

Рисунок 1. Меню "Поделиться" на устройстве Android.

Используйте панель "Поделиться" в Android, чтобы обеспечить единообразие для пользователей в разных приложениях. Не показывайте собственный список получателей и не создавайте собственные варианты панели "Поделиться".

Панель "Поделиться" в Android позволяет пользователям отправлять информацию нужным людям, используя подходящие приложения, всего одним нажатием. Панель "Поделиться" может предлагать цели, недоступные для специальных решений, и использует последовательную систему ранжирования. Это связано с тем, что при выборе приложений для панели "Поделиться" учитывается информация о действиях в приложениях и действиях пользователей, которая доступна только системе.

Кроме того, в ней есть много полезных функций для разработчиков. Например, вы можете:

Как использовать окно "Поделиться" в Android

Для всех типов обмена данными создайте намерение и задайте для него действие Intent.ACTION_SEND. Чтобы показать окно "Поделиться" на устройстве Android, вызовите метод Intent.createChooser() и передайте ему объект Intent. Он возвращает версию вашего намерения, которая всегда показывает панель "Поделиться".

Отправка текстового контента

Самый простой и распространенный способ использования таблицы обмена Android – передача текстового контента из одного действия в другое. Например, большинство браузеров могут передавать 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, ожидают 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. Файлы можно вставлять в MediaStore с помощью scanFile(). После этого Uri в стиле content://, подходящий для предоставления доступа, передается в предоставленный обратный вызов onScanCompleted(). Обратите внимание, что после добавления в систему MediaStore контент будет доступен любому приложению на устройстве.

Используйте правильный MIME-тип

Укажите наиболее подходящий MIME-тип для отправляемых данных. Например, используйте text/plain, чтобы поделиться обычным текстом. Вот несколько распространенных MIME-типов, используемых при отправке данных в Android:

Получатели регистрируются для Отправители отправляют
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 может показываться предпросмотр контента. Некоторые предварительные версии функций доступны только для определенных типов.

Как поделиться несколькими файлами

Чтобы поделиться несколькими фрагментами контента, используйте действие 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) на панели "Поделиться" показывается предварительный просмотр текста. Иногда текст, которым делятся пользователи, бывает трудно понять. Представьте, что вам нужно поделиться сложным URL, например 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.

Как добавить собственные действия в меню "Поделиться"

Рисунок 3. Специальные действия в меню "Поделиться" на устройствах Android.

В Android 14 (уровень API 34) и более поздних версиях приложения могут добавлять в системный интерфейс обмена данными собственные действия. Настраиваемые действия показываются в виде небольших значков в верхней части панели "Поделиться". Приложения могут указывать любой объект Intent в качестве действия, которое выполняется при нажатии на значок.

Чтобы добавить собственные действия в меню "Поделиться" на устройстве Android, сначала создайте ChooserAction с помощью ChooserAction.Builder. Вы можете указать PendingIntent в качестве действия, которое будет выполняться при нажатии на значок. Создайте массив, содержащий все ваши настраиваемые действия, и укажите его как EXTRA_CHOOSER_CUSTOM_ACTIONS объекта share Intent.

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 позволяет указать до двух намерений для действий, которые будут показаны перед рекомендуемыми приложениями. Для этого используется Intent.EXTRA_INITIAL_INTENTS. Для получателей Direct Share (например, чатов или контактов) публикуйте динамические ярлыки, используя вместо этого ShortcutManagerCompat (см. Как предоставить получателей Direct Share):

Рисунок 4. Специальные цели на панели "Поделиться" в Android.

Добавьте Intent.EXTRA_INITIAL_INTENTS в намерение поделиться после вызова Intent.createChooser():

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.EXTRA_EXCLUDE_COMPONENTS в намерение после вызова Intent.createChooser():

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 позволяет получить эту информацию, предоставляя ComponentName целей, которые выбирают пользователи, с помощью IntentSender.

Сначала создайте PendingIntent для BroadcastReceiver и укажите его IntentSender в Intent.createChooser():

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, создайте намерение и добавьте дополнительные параметры, как при вызове панели "Поделиться". Однако не вызывайте 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)
}

Подробнее…

Подробнее об отправке данных см. в разделе Интенты и фильтры интентов.