Android Studio: урок Kotlin. Отправка файлов через FileProvider

Когда приложение создаёт фотографию, документ или любой другой файл, часто возникает задача отправить его в другое приложение — мессенджер, почту или облачное хранилище. Напрямую передавать путь к файлу через Intent нельзя: современные версии Android запрещают это ради безопасности. Вместо этого используют FileProvider — специальный компонент, который генерирует безопасные временные ссылки. Сегодня в Android Studio мы разберём, как настроить FileProvider и написать код на Kotlin. Этот урок подойдёт начинающим и поможет освоить один из важных механизмов обмена данными между программами.


Зачем нужен FileProvider

До Android 7.0 (API 24) приложения могли передавать друг другу файлы через обычные URI, указывающие на реальный путь в файловой системе. Но такой подход создавал уязвимости: любое приложение могло прочитать чужие файлы. Google запретила прямые ссылки, и теперь для межпрограммного обмена нужно использовать специальный контент-провайдер, который сам решает, какие файлы и на какое время открывать.

FileProvider — это готовая реализация такого провайдера, входящая в библиотеку AndroidX. Он генерирует временные URI с разрешениями на чтение или запись, которые автоматически отзываются после того, как принимающее приложение завершит работу с файлом.


Добавление FileProvider в манифест

Настройка начинается с AndroidManifest.xml. Нужно объявить сам провайдер внутри тега <application>, указав класс из библиотеки AndroidX и файл с описанием доступных папок. Также обязательно задать атрибут android:authorities — уникальное имя, обычно в формате имя_пакета.fileprovider.

<application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:theme="@style/Theme.MyApp">

    <provider
        android:name="androidx.core.content.FileProvider"
        android:authorities="${applicationId}.fileprovider"
        android:exported="false"
        android:grantUriPermissions="true">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/file_paths" />
    </provider>

</application>

Атрибут exported="false" означает, что провайдер не доступен для всех подряд — только через явные Intent. grantUriPermissions="true" разрешает выдавать временные права на чтение или запись.


Создание файла с путями

Теперь нужно описать, какие папки доступны для FileProvider. Создайте в res новую папку xml (если её ещё нет), а в ней — файл file_paths.xml. В нём перечисляются физические директории и их псевдонимы. Например, разрешим делиться файлами из внутренней папки files и из кэша.

<?xml version="1.0" encoding="utf-8"?>
<paths>
    <files-path
        name="shared_files"
        path="." />
    <cache-path
        name="shared_cache"
        path="." />
</paths>

Здесь files-path ссылается на Context.getFilesDir(), а cache-path — на Context.getCacheDir(). Атрибут path может указывать на конкретную подпапку, например images/, если вы хотите открыть доступ только к части хранилища.


Получение URI для файла

Предположим, приложение сохранило текстовый файл в папку files. Чтобы поделиться им, нужно получить URI через FileProvider.getUriForFile(), передав контекст, authority (тот же, что в манифесте) и сам файл. Затем этот URI можно вложить в Intent.

val file = File(filesDir, "report.txt")
// убедимся, что файл существует
if (file.exists()) {
    val uri = FileProvider.getUriForFile(
        this,
        "${packageName}.fileprovider",
        file
    )
    // теперь uri можно передавать
}

URI будет выглядеть примерно как content://com.example.app.fileprovider/shared_files/report.txt. Это виртуальный адрес, по которому другое приложение сможет прочитать файл, если мы дадим ему разрешение.


Отправка файла через Intent

Создадим Intent с действием ACTION_SEND, укажем MIME-тип файла (например, text/plain для текста), добавим URI как extra и установим флаг FLAG_GRANT_READ_URI_PERMISSION, чтобы принимающее приложение получило временный доступ.

val shareIntent = Intent(Intent.ACTION_SEND).apply {
    type = "text/plain"
    putExtra(Intent.EXTRA_STREAM, uri)
    addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
}
startActivity(Intent.createChooser(shareIntent, "Поделиться файлом"))

Вызов createChooser показывает стандартное меню выбора приложений: Gmail, Telegram, Bluetooth и так далее. Пользователь выбирает программу, и файл передаётся.


Пример: кнопка «Поделиться»

Соберём всё вместе в одном Activity. Разместим кнопку, при нажатии на которую создаётся или используется существующий файл, а затем запускается Intent.

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val btnShare = findViewById<Button>(R.id.btnShare)
        btnShare.setOnClickListener {
            shareFile()
        }
    }

    private fun shareFile() {
        // создаём демонстрационный файл
        val file = File(filesDir, "report.txt")
        file.writeText("Это отчёт, сгенерированный приложением.")

        val uri = FileProvider.getUriForFile(
            this,
            "${packageName}.fileprovider",
            file
        )
        val shareIntent = Intent(Intent.ACTION_SEND).apply {
            type = "text/plain"
            putExtra(Intent.EXTRA_STREAM, uri)
            addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
        }
        startActivity(Intent.createChooser(shareIntent, "Поделиться отчётом"))
    }
}

Полезные советы

  • Своевременно создавайте файл. Перед получением URI убедитесь, что файл существует, иначе приложение упадёт с исключением. Если файла ещё нет, создайте его перед вызовом getUriForFile.
  • Используйте подпапки для порядка. В file_paths.xml можно указать конкретный path, чтобы изолировать файлы для обмена от остальных данных приложения.
  • Проверяйте MIME-тип. Неправильный тип может привести к тому, что некоторые приложения не отобразятся в списке выбора. Для изображений используйте "image/*", для PDF — "application/pdf".
  • Не забывайте про флаг разрешения. Без FLAG_GRANT_READ_URI_PERMISSION принимающее приложение не сможет прочитать файл и выдаст ошибку.

Коротко о главном

Сегодняшний урок в Android Studio на языке Kotlin показал, как безопасно передавать файлы другим приложениям через FileProvider. Вы настроили провайдер в манифесте, описали доступные папки, получили URI и отправили его через Intent. Теперь ваше приложение может делиться документами, изображениями и любыми другими файлами, оставаясь защищённым и совместимым со всеми современными версиями Android. Попробуйте добавить кнопку «Поделиться» в свой проект — это займёт несколько минут и сделает программу гораздо полезнее.