Как работать с Context, ContentProvider и content:// (Java/Kotlin)

Короткий ответ: используйте Context для доступа к окружению и системным сервисам, ContentProvider + URI content:// — для унифицированного и безопасного обмена данными, а Intent.data/scheme — для открытия и шаринга этих URI; всегда отдавайте предпочтение applicationContext и FileProvider при обмене файлами.

Оглавление {{TOC_AUTOMATIC}}

Context: роли, виды и правильное использование

Context — это «окружение» приложения: ресурсы, файловая система, ContentResolver и системные сервисы. Главная практическая рекомендация — передавать applicationContext в долгоживущие объекты (репозитории, синглтоны) и Activity‑context только для краткоживущих UI‑операций.

Когда использовать какой Context:

  • applicationContext — для репозиториев, DB, стартов сервисов без UI.
  • Activity/Fragment context — для Inflater, создания UI, startActivity().
  • BroadcastReceiver (onReceive) — контекст ограничен временем выполнения onReceive.

Примеры: Kotlin — репозиторий получает applicationContext:

class MyRepository(private val context: Context) {
  fun getAppVersion(): String =
    context.packageManager.getPackageInfo(context.packageName, 0).versionName ?: "unknown"
}

Частая ошибка — хранить ссылку на Activity или View в синглтоне → утечки памяти. Для долгоживущих объектов используйте applicationContext.

ContentProvider и content://: структура, реализация и использование

ContentProvider — абстракция для обмена и инкапсуляции данных: он нормирует доступ через ContentResolver и URI с схемой content://. URI обычно имеют вид: content://authority/path/.../[id]

Примеры:

  • content://com.android.contacts/contacts
  • content://com.example.notes.provider/notes/42

Основные методы ContentResolver:

  • query(uri, projection, selection, selectionArgs, sort)
  • insert(uri, values)
  • update(uri, values, selection, args)
  • delete(uri, selection, args)
  • openInputStream(uri) / openOutputStream(uri)

Минимальная структура провайдера (существенные моменты):

  • наследовать ContentProvider и переопределить onCreate, query, insert, update, delete, getType;
  • зарегистрировать provider в AndroidManifest, указать authorities и exported/permissions;
  • при изменениях вызывать context?.contentResolver?.notifyChange(uri, null).

Ключевой момент безопасности: не экспортируйте провайдер с чувствительными данными без проверки прав и ограничений по URI и операциям.

Intent, data и scheme: как передавать и открывать контент

Intent может содержать URI в поле data и MIME type в type. Это позволяет другим компонентам/приложениям запросить открытие/обработку ресурса:

Kotlin — открыть контакт по URI:

fun openContact(context: Context, contactUri: Uri) {
  val intent = Intent(Intent.ACTION_VIEW, contactUri)
  context.startActivity(intent)
}

scheme — часть URI до двоеточия: content, file, https, tel. В intent‑filter можно указать android:scheme, android:host и android:pathPrefix, чтобы Activity принимала определённые URI.

Для обмена файлами между приложениями:

  • Используйте FileProvider, который выдаёт content:// URI, а не file://;
  • Передавайте флаг Intent.FLAG_GRANT_READ_URI_PERMISSION.

Java — открыть PDF через FileProvider:

Uri uri = FileProvider.getUriForFile(context, "com.example.app.fileprovider", pdfFile);
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(uri, "application/pdf");
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
context.startActivity(intent);

Для обмена файлами между приложениями всегда используйте FileProvider и URI с content:// — это безопасно и соответствует политике доступа к файлам в современных версиях Android.

Практические сценарии: коротко и применимо

  1. Получение изображения из галереи:
  • Запускаете ActivityResultContracts.GetContent() → получаете content:// URI.
  • Через contentResolver.openInputStream(uri) декодируете Bitmap.
  1. Поделиться файлом:
  • Получаете URI из FileProvider.getUriForFile(...).
  • Формируете Intent.ACTION_SEND, кладёте EXTRA_STREAM и ставите FLAG_GRANT_READ_URI_PERMISSION.
  1. Публичный доступ к данным:
  • Реализуете ContentProvider с чёткой URI‑схемой и правами.
  • Добавляете Activity с intent‑filter VIEW, чтобы другие приложения могли открыть конкретный ресурс.

Рекомендации по архитектуре и лучшие практики

  • Инкапсулируйте обращение к ContentResolver в репозитории/data‑source; тестируйте через заглушки (mock ContentResolver).
  • Не захардкодьте строки URI — создайте утилиту, строящую URI централизованно.
  • Выполняйте операции с провайдером в IO‑потоке (корутины, Executors).
  • При экспорте данных продумайте permissions, проверку вызывающего (например, привязка к UID), и минимизируйте набор операций, доступных внешним клиентам.

Частые ошибки

  • Удержание Activity‑context в длительных объектах → утечки памяти.
  • Использование file:// вместо content:// (FileProvider) → ошибки доступа в новых Android.
  • Не указывать FLAG_GRANT_READ_URI_PERMISSION при передаче URI другому приложению.
  • Экспортировать ContentProvider с чувствительными данными без ограничений или permissions.

Никогда не экспортируйте ContentProvider с чувствительными данными без чётких permissions и валидации вызывающего приложения. Иначе данные смогут прочитать/изменить любые сторонние приложения.

FAQ

Q: Нужно ли всегда реализовывать ContentProvider для внутренних данных? A: Нет. Если данные используются только внутри приложения — провайдера можно не создавать. ContentProvider нужен для унификации доступа и обмена данными с внешними приложениями или для использования абстракций (например, Room + ContentProvider в модульной архитектуре).

Q: Как безопасно дать доступ к файлу другому приложению? A: Через FileProvider и выдачу content:// URI с флагом FLAG_GRANT_READ_URI_PERMISSION; в манифесте описываете и file_paths.xml.

Q: Где хранить URI и authority? A: В одном месте — константы/утилиты внутри модуля данных; не захардкодьте строки по всему приложению.

Теперь у вас готовая практическая картина: Context — доступ к окружению, ContentProvider и content:// — унифицированная точка доступа к данным, Intent.data/scheme — механизм открытия и шаринга. Соблюдайте ограничения по контексту, используйте FileProvider и продуманную систему прав — и ваши данные будут доступны и безопасны.