Как работать с Uri, Intent и доступом к файлам в Android (Java)

Коротко: используйте Uri с ContentResolver для чтения/записи и FileProvider для безопасной передачи файлов через Intent — добавляйте флаги доступа (FLAG_GRANT_READ_URI_PERMISSION) и при необходимости takePersistableUriPermission() для долгосрочного доступа.

Основы Uri в Android

Uri — это абстрактный идентификатор ресурса: content://, file://, http:// и т. д. Для создания используйте:

Uri u1 = Uri.parse("content://com.example/app/files/1");
Uri u2 = Uri.fromFile(new File("/path/to/file.jpg")); // избегайте на Android 7+

Полезные методы: getScheme(), getLastPathSegment(), getPath() (не всегда надежен для content://). Всегда проверяйте схему: если content, читайте через ContentResolver, а не напрямую с файловой системы.

Если getScheme() возвращает content, используйте getContentResolver().openInputStream(uri) для чтения.

Использование Intent с Uri: выбор и передача файлов

Типичные Intent'ы:

  • ACTION_GET_CONTENT / ACTION_OPEN_DOCUMENT — выбрать файл (получаете Uri в onActivityResult / Activity Result API).
  • ACTION_VIEW — открыть ресурс у внешнего приложения.
  • ACTION_SEND — поделиться файлом.

Пример выбора изображения:

Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("image/*");
startActivityForResult(intent, REQUEST_PICK);

В onActivityResult:

Uri uri = data.getData(); // используйте ContentResolver для чтения

При отправке Uri другому приложению:

  • Генерируйте content:// через FileProvider.
  • Добавьте intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) (и WRITE при необходимости).
  • Укажите тип MIME с setDataAndType() или setType().

Доступ к файлам и FileProvider (надежный способ)

Начиная с Android 7, file:// Uri могут привести к FileUriExposedException. Решение — FileProvider.

  1. Манифест:
<provider
  android:name="androidx.core.content.FileProvider"
  android:authorities="com.example.myapp.fileprovider"
  android:exported="false"
  android:grantUriPermissions="true">
  <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths"/>
</provider>
  1. res/xml/file_paths.xml:
<paths>
  <files-path name="my_files" path="."/>
</paths>
  1. Генерация Uri:
File f = new File(getExternalFilesDir(Environment.DIRECTORY_PICTURES), "photo.jpg");
Uri photoUri = FileProvider.getUriForFile(context, "com.example.myapp.fileprovider", f);
intent.putExtra(Intent.EXTRA_STREAM, photoUri);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);

Если не включить grantUriPermissions="true" или не добавить флаг разрешения в Intent, получатель не сможет открыть Uri — будет Permission Denial.

Чтение и запись через ContentResolver

Чтение:

try (InputStream in = getContentResolver().openInputStream(uri)) {
    byte[] bytes = in.readAllBytes();
}

Запись (если провайдер позволяет):

try (OutputStream out = getContentResolver().openOutputStream(uri)) {
    out.write(data);
}

Для долгосрочного доступа к документам используйте takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION) после получения Uri от ACTION_OPEN_DOCUMENT.

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

  • FileUriExposedException — используйте FileProvider.
  • Permission Denial — забыли FLAG_GRANT_READ_URI_PERMISSION.
  • Ожидание реального файлового пути из content:// — многие провайдеры не возвращают путь; не пытайтесь использовать new File(uri.getPath()).
  • Неправильные file_paths.xml — указывайте корректные корневые каталоги (external-files-path, files-path и т.д.).

FAQ

  • Как получить реальный путь файла из content://? Часто невозможно или ненужно. Работа ведётся через ContentResolver (InputStream). Извлечение реального пути ненадёжно и зависит от провайдера.
  • Нужен ли MANAGE_EXTERNAL_STORAGE? Обычно нет. Предпочитайте Scoped Storage и ACTION_OPEN_DOCUMENT. MANAGE_EXTERNAL_STORAGE подходит лишь для специальных случаев и жестко проверяется в Google Play.
  • Как дать постоянный доступ к документу? Используйте ACTION_OPEN_DOCUMENT, затем getContentResolver().takePersistableUriPermission().

Эти практики покроют большинство сценариев работы с Uri в Android на Java: чтение/запись, выбор файлов, безопасная передача через Intent и обход распространённых ошибок.