Как работать с content://, ContentProvider и файлами в Android
В двух словах: content:// — схема URI для доступа к данным, которыми управляет ContentProvider; для чтения/записи используйте ContentResolver, для простого шаринга файлов — FileProvider, а для выбора/доступа к внешним документам — Storage Access Framework (SAF). Ниже — практические примеры и рекомендации, чтобы сразу применить в проекте.
Что такое ContentProvider и когда он нужен
ContentProvider инкапсулирует доступ к данным (табличным и файловым) и предоставляет единый API через ContentResolver. Используйте его, если нужно:
- открыть данные для других приложений (виджеты, sync, сторонние клиенты);
- реализовать единый CRUD‑интерфейс с контролем прав. Если данные используются только внутри приложения — ContentProvider можно не делать (Room/файлы хватит).
Для простого шаринга файлов (поделиться фото или PDF через Intent) обычно быстрее и безопаснее настроить FileProvider из androidx.core, а не писать свой ContentProvider.
Основные операции: читать и писать content:// URI
Примеры Kotlin — минимально и надёжно.
Чтение как InputStream:
context.contentResolver.openInputStream(uri)?.use { input ->
val bytes = input.readBytes()
// обработать bytes
}
Запись через OutputStream:
context.contentResolver.openOutputStream(uri)?.use { out ->
out.write(data)
}
Если нужен seek/descriptor:
context.contentResolver.openFileDescriptor(uri, "r")?.use { pfd ->
FileInputStream(pfd.fileDescriptor).use { fis ->
// читать с поддержкой seek
}
}
Если вы получаете uri из ACTION_OPEN_DOCUMENT и хотите долгосрочный доступ:
val flags = intent.flags and (Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
context.contentResolver.takePersistableUriPermission(uri, flags)
FileProvider, SAF и дизайн решения
FileProvider
- Настраивается в манифесте и res/xml/file_paths.xml.
- Возвращает content:// URI для локальных файлов и позволяет выдавать временные права через FLAG_GRANT_READ_URI_PERMISSION. Пример генерации URI и отправки:
val uri = FileProvider.getUriForFile(context, "${BuildConfig.APPLICATION_ID}.fileprovider", file)
val intent = Intent(Intent.ACTION_SEND).apply {
putExtra(Intent.EXTRA_STREAM, uri)
type = "image/jpeg"
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
}
context.startActivity(Intent.createChooser(intent, "Share"))
Storage Access Framework (DocumentsProvider)
- Используйте, когда нужно дать пользователю выбирать файлы/папки вне приложения или работать с облачными провайдерами.
- Подходит для Scoped Storage на Android 11+.
Не передавайте file:// URI между приложениями — это приведёт к исключению. Не выставляйте android:exported="true" для провайдера без необходимости.
Частые ошибки
- Нет FLAG_GRANT_READ_URI_PERMISSION при отправке Intent → получатель не откроет URI.
- Неправильный authority в manifest → getUriForFile/ContentResolver не найдёт провайдера.
- Не закрытые потоки/ParcelFileDescriptor → утечки ресурсов.
- Доступ по file:// вместо content:// → SecurityException на современных версиях.
FAQ
- Нужно ли всегда делать ContentProvider для базы данных? Только если данные будут использоваться другими приложениями или системными компонентами.
- Когда выбирать SAF вместо FileProvider? Если пользователь должен выбирать файлы/папки или нужен доступ к облачному хранилищу.
- Как дать долгосрочный доступ к uri из picker? Вызвать takePersistableUriPermission после получения результата.
Проверочный чек‑лист перед релизом:
- Правильный authority и android:grantUriPermissions для FileProvider.
- addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) при шаринге.
- Закрыты все потоки и дескрипторы.
- Минимальные разрешения/права для провайдера, проверка MIME/размера перед обработкой внешних потоков.
Если хотите, подготовлю:
- готовый пример ContentProvider + Room (Kotlin) с UI;
- файл res/xml/file_paths.xml под вашу структуру;
- инструкцию по миграции под Scoped Storage.