GetExternalStoragePublicDirectory устарел в Android Q

Поскольку getExternalStoragePublicDirectory устарел в Android Q, рекомендуется использовать другие средства. тогда как мы можем указать, что мы хотим хранить сгенерированные фотографии из приложения камеры в папке DCIM или в пользовательской подпапке в DCIM?

В документации указано, что следующие 3 варианта являются новыми предпочтительными альтернативами:

  1. Контекст # getExternalFilesDir (строка)
  2. Медиасторе
  3. Намерение#ACTION_OPEN_DOCUMENT

Вариант 1 исключен, так как это будет означать, что фотографии будут удалены, если приложение будет удалено.

Вариант 3 также не подходит, так как пользователь должен выбрать местоположение через файловый менеджер SAF.

У нас остается вариант 2, MediaStore; но на момент этого вопроса нет документации о том, как использовать его в качестве замены для getExternalStoragePublicDirectory в Android Q.

101
0
65 533
7
Перейти к ответу Данный вопрос помечен как решенный

Ответы 7

Ответ принят как подходящий

Основываясь на документах, используйте DCIM/... для RELATIVE_PATH, где ... — это любой ваш пользовательский подкаталог. Итак, вы бы закончили с чем-то вроде этого:

      val resolver = context.contentResolver
      val contentValues = ContentValues().apply {
        put(MediaStore.MediaColumns.DISPLAY_NAME, "CuteKitten001")
        put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg")
        put(MediaStore.MediaColumns.RELATIVE_PATH, "DCIM/PerracoLabs")
      }

      val uri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues)

      resolver.openOutputStream(uri).use {
        // TODO something with the stream
      }

Обратите внимание, что, поскольку RELATIVE_PATH является новым для уровня API 29, вам нужно будет использовать этот подход на более новых устройствах и использовать getExternalStoragePublicDirectory() на старых.

Спасибо за ответ. В качестве примечания, я только что увидел в документации, что есть также новое поле «IS_PENDING», которое, я предполагаю, что, возможно, мне также следует включить при создании ContentValues, чтобы быть в безопасности при записи файла, а затем обновить после того, как файл будет полностью сохранен.

PerracoLabs 06.06.2019 00:41

Что, если бы я хотел получить корневой путь для выполнения других операций, а не только для вставки фотографии. например у меня было Environment.getExternalStorageDirectory() + "/myFolder". теперь, что я должен сделать, чтобы получить этот путь с MediaStore как String/File/Uri?

S.R 01.07.2019 16:50

@S.R: у вас нет доступа файловой системы к произвольным местам во внешнем хранилище на Android Q (по умолчанию) и Android R (для всех приложений). Таким образом, вы должны сосредоточиться на выполнении других операций, используя подход, не требующий доступа к файловой системе. Или придерживайтесь getExternalFilesDir() на Context.

CommonsWare 01.07.2019 16:53

последний вопрос! Безопасно ли использовать getParentFile на getExternalFilesDir()4 раза на всех устройствах, чтобы получить то, что я ищу?

S.R 01.07.2019 16:59

@S.R: Нет. Во-первых, как я уже писал, у вас нет доступа к файловой системе к произвольным местам во внешнем хранилище на Android Q (по умолчанию) и Android R (для всех приложений). У вас не будет доступа к местоположению, к которому вы пытаетесь получить доступ. Кроме того, нет необходимости, чтобы ваш алгоритм работал на каком-либо конкретном устройстве.

CommonsWare 01.07.2019 17:03

Добавьте свою статью: commonsware.com/blog/2019/04/22/….

CoolMind 15.07.2019 12:09

Интересно, действительно ли нет возможности реализовать это один раз. Это работает, но я понял это намного раньше. Я не обязательно хочу продолжать использовать getExternalStoragePublicDirectory(), а также новый подход. Теперь у меня есть несколько if/else, чтобы определить версию Android, выкинув кодовую базу, чтобы сделать это как по-новому, так и по-старому.

Max 20.07.2019 18:18

Этот новый подход не позволяет вам получить доступ к ранее созданным файлам, не так ли? Например, если я сделал приложение для камеры, которое помещает файлы в указанную вами папку, а затем удалил и переустановил свое приложение, я больше не смогу получить к ним доступ, не так ли? Я думаю, что разрешение на хранение было заменено другими разрешениями, более ограниченными, нет (может быть, может обрабатывать только медиафайлы)? Кроме того, есть ли список всех возможных типов файлов, которые разрешено создавать? Это только медиафайлы или любой файл (например, PDF, ZIP, APK,...)?

android developer 23.07.2019 20:06

@androiddeveloper: «если я сделаю приложение для камеры, которое помещает файлы в указанную вами папку, а затем удалю и переустановлю свое приложение, я больше не смогу получить к ним доступ, не так ли?» -- правильный. С точки зрения Android файлы из предыдущей установки вашего приложения ничем не отличаются от файлов из какого-то другого произвольного приложения. «Я думаю, что разрешение на хранение было заменено другими разрешениями, которые более ограничены, нет (возможно, могут обрабатывать только медиафайлы)?» -- они избавились от них в бета-версии 3.

CommonsWare 23.07.2019 20:10

@androiddeveloper: «есть ли список всех возможных типов файлов, которые разрешено создавать?» -- Не то, чтобы я знаю. «Это только медиафайлы или любой файл (например, PDF, ZIP, APK,...)?» -- Я думаю, что вы ограничены медиафайлами с точки зрения использования MediaStore.

CommonsWare 23.07.2019 20:11

@CommonsWare Они сильно изменили свои решения в бета-версиях Q. Очень трудно следить и знать, что происходит на самом деле. То есть сейчас только SAF для доступа к файлам? И MediaStore, чтобы получить глобальные, предназначенные только для медиафайлов (которые я не знаю, что может быть «медиа», за исключением, может быть, некоторых общих файлов изображений, видео и аудио)? На самом деле довольно грустно, что ПК, подключенный к устройству, может легко показать вам все файлы и с путями (хотя и не настоящими путями), а на самом устройстве мы должны использовать SAF...

android developer 23.07.2019 20:23

@androiddeveloper: «Теперь для доступа к файлам есть только SAF?» -- АФАИК, да. «И MediaStore, чтобы получить глобальные, которые предназначены только для медиафайлов (которые я не знаю, что может быть «медиа», за исключением, может быть, некоторых общих файлов изображений, видео и аудио)?» -- АФАИК, да.

CommonsWare 23.07.2019 20:23

@CommonsWare Не могли бы вы указать мне самую последнюю документацию о MediaStore? Я считаю плохим решением то, что Google даже не объясняет, что такое «медиа». Я должен написать им об этом. Я искал во многих местах и ​​все еще не мог найти. Я заметил, что вы написали как минимум 10 статей о разрешении на хранение. Я предполагаю, что вы заработаете больше, когда Q действительно выйдет из строя. Я надеюсь, что будет способ преодолеть ограничения на пути к файлам. Это требуется для многих библиотек, в том числе для самой платформы Android (например, для разбора файлов APK и DB).

android developer 23.07.2019 20:36

@androiddeveloper: я уверен, что вы уже читали developer.android.com/preview/privacy/scoped-storage и developer.android.com/preview/…. Я ничего не знаю в стандартной документации об изменениях Android Q по этим темам.

CommonsWare 23.07.2019 20:42

@CommonsWare Спасибо. Я думаю, что глядя на документы, которые вы показали, это означает, что «медиа» означает «Фотографии, которые хранятся в MediaStore.Images.», «Видео, которые хранятся в MediaStore.Video.», «Музыкальные файлы, которые хранятся в MediaStore.Audio." . Достигнув каждого из них, кажется, что все дело в миметипе: audio/*, video/*, image/*. Что приложения медиаплеера (видео, аудио и изображения) запрашивают у ОС на Q, если это не разрешение на хранение? Что видит пользователь? Интересно, обновил ли Google свои образцы для Q в этом вопросе.

android developer 24.07.2019 09:24

@androiddeveloper: «Что приложения медиаплеера (видео, аудио и изображения) запрашивают у ОС на Q, если это не разрешение на хранение?» -- просят READ_EXTERNAL_STORAGE. Это описано в developer.android.com/preview/privacy/scoped-storage.

CommonsWare 24.07.2019 12:58

@CommonsWare Понятно. Кажется, я пропустил эту часть. Итак, насколько я помню, разрешение на хранение сильно уменьшилось. Он разрешает доступ только к медиафайлам, и даже тогда он не может получить всю информацию о них (метаданные) по умолчанию. И он даже не разрешает доступ к этим файлам с использованием пути к файлу, потому что если бы это было так, это означало бы, что я мог бы просто использовать InputStream, чтобы получить все, что находится внутри медиафайла, включая защищенные метаданные... Верно?

android developer 24.07.2019 13:42

@androiddeveloper: Это описание соответствует моему пониманию.

CommonsWare 24.07.2019 13:48

@CommonsWare Я вижу. Спасибо.

android developer 24.07.2019 15:29

@CommonsWare Подождите, что касается медиафайлов, здесь говорится, что для получения всех метаданных нам потребуется разрешение «ACCESS_MEDIA_LOCATION». Добавляя его, я заметил, что он принадлежит к группе разрешений на хранение. Так зачем он вообще нужен? Ведь он будет предоставлен вместе с разрешением на хранение... Странно

android developer 24.07.2019 20:03

«ACCESS_MEDIA_LOCATION», по-видимому, является их главной причиной абстрагирования нас от файловой системы. Без этого разрешения вы не сможете увидеть временную метку места, где были сделаны фотографии (обычно это формат .jpg, поэтому вы можете извлечь его из самого файла, а не из хранилища). Если у вас нет разрешения на доступ к .jpg, вы можете получить доступ к входному потоку, который предоставляет медиастор, вы удалите эту информацию из потока. Разрешение на хранение используется только для того, чтобы медиастор давал вам доступ к носителям, которые вы сами не создавали.

Max 13.08.2019 18:59

Есть ли для этого пример Java?

Gaurav Mall 25.08.2019 22:48

@GauravMall: У кого-то может быть такой - у меня нет. Извиняюсь!

CommonsWare 25.08.2019 22:49

Ох, ну ладно. Кстати, этот код есть в какой-то документации?

Gaurav Mall 25.08.2019 22:51

Опубликовал ответ на Java. Спасибо за ответ :)

Gaurav Mall 25.08.2019 22:58

@GauravMall: «этот код есть в какой-то документации?» -- Кажется, я создал его на основе этот пример сохранения видео через MediaStore. Я просто настроил его для изображений.

CommonsWare 25.08.2019 23:35

@CommonsWare Как я могу создать папку в Android Q, поскольку getExternalStoragePublicDirectory() устарела. Помощь будет оценена.

Tushar Pingale 20.10.2019 16:36

@TusharPingale: используйте методы Context, например getExternalFilesDir(). См. это и это.

CommonsWare 20.10.2019 16:39

Проблема с MEDIA STORE: он предназначен для медиафайлов (аудио, изображений и видео). Мое приложение сохраняет 2 базы данных (sqlite) в каталоге DOWNLOAD, и мне нужны эти файлы для работы... Я не хочу загружать и сохранять эти файлы db в MOVIES или другом медиа-каталоге, я хочу продолжать загружать свои файлы в подкаталог каталога DOWNLOAD, как я делаю уже несколько лет! Я не могу обновить цель SDK приложения до 29 из-за этой проблемы. Есть ли у кого-то решение? СПАСИБО

Christian 06.02.2020 17:27

@Christian: «Проблема с MEDIA STORE: он создан для медиафайлов (аудио, изображений и видео)» — Android 10 также добавил MediaStore.Downloads. «Я хочу продолжить загрузку моих файлов в подкаталог каталога DOWNLOAD» — используйте MediaStore.Downloads. См. commonsware.com/blog/2020/01/11/…

CommonsWare 06.02.2020 20:19

@CommonsWare Проблема в том, что нет способа «обновить» файлы, созданные моим приложением (без перезагрузки устройства или ожидания X времени), или есть? MediaScannerConnection требует, чтобы я передал файловый объект. Также невозможно узнать, когда MediaRecorder закончит запись файла — до Q мы могли использовать FileObserver. Это оставляет мне один вариант — использовать getExternalFilesDir(). Проблема в том, что файл не будет доступен пользователю в общедоступном каталоге (Environment.DIRECTORY_MOVIES). У Вас есть какие-то предложения?

HB. 09.05.2020 08:41

@HB.: Если вы insert() попадаете в MediaStore или update() содержимое MediaStore, MediaStore немедленно «обновляется», чтобы отразить то, что вы вставили или обновили.

CommonsWare 09.05.2020 12:54

@CommonsWare Но сначала я создаю Uri, как вы сделали выше, а затем передаю FileDescriptor этого Uri (как вы упомянули здесь — stackoverflow.com/a/46897595/5550161) в MediaRecorder. Невозможно узнать, когда MediaRecorder завершит запись файла, не используя FileObserver - Если я хочу продолжать использовать FileObserver, я должен запросить устаревшее хранилище - Если я запрошу устаревшее хранилище, я могу продолжать использовать getExternalStoragePublicDirectory(). Итак, у меня есть эти варианты? - Используйте устаревший API или надейтесь, что MediaRecorder закончит запись файла к тому времени, когда он мне понадобится.

HB. 10.05.2020 08:57

@HB.: Отправьте отчет об ошибке, чтобы получить MediaRecorder обновление с помощью лучшего API. Или посмотрите, есть ли сторонняя библиотека для записи мультимедиа, которая предлагает вам лучший вариант. Или запишите в файл на getFilesDir() и перенесите его в MediaStore только после завершения записи.

CommonsWare 10.05.2020 13:12

Не могли бы вы помочь мне с stackoverflow.com/questions/63543414/…

jazzbpn 24.08.2020 05:44

@CommonsWare, как перенести файл в MediaStore после записи видеофайла в getFilesDir() или getExternalFilesDir()

user924 01.09.2020 10:17

Я не понимаю: если я хочу добраться до любого из распространенных путей (например, «Загрузки»), как мне это сделать вместо Environment.getExternalStoragePublicDirectory(Environment.DI‌​RECTORY_DOWNLOADS)?

android developer 01.05.2021 16:01

@androiddeveloper: для операций чтения вы можете вызвать getDirectory() на StorageVolume на Android 11+. Это примерно соответствует Environment.getExternalStoragePublicDirectory(), но для каждого тома. StorageManager дает вам доступ к списку StorageVolume объектов.

CommonsWare 01.05.2021 16:13

@CommonsWare Где часть альтернативы выбора типа каталога (Environment.DIRECTORY_...)? Вы имеете в виду, что я просто добавляю его в путь тома? Было ли это устаревшим, поскольку у нас может быть несколько томов с одинаковыми папками? Вот как я должен использовать это сейчас: File(storageManager.primaryStorageVolume.directory!!,Environ‌​ment.DIRECTORY_DOWNL‌​OADS) ? Если это так, обновите свой ответ, потому что это новый способ его использования.

android developer 01.05.2021 18:54

@androiddeveloper: «Вы имеете в виду, что я просто добавляю его в путь к тому?» -- предположительно, да. «Было ли это устаревшим, поскольку у нас может быть несколько томов, каждый из которых имеет одинаковые папки?» -- Я не знаю насчет части "у всех одинаковые папки". «пожалуйста, обновите свой ответ» - нет, потому что это не связано с ответом. Вопрос и ответ касаются операций записывать, тогда как «путь любого из общих» в лучшем случае будет иметь значение только для операций читать.

CommonsWare 01.05.2021 19:02

@CommonsWare Хорошо, извините и спасибо. Является ли хорошим предположением, что он не может быть обнулен для основного тома хранилища, а также что те же папки (например, DIRECTORY_DOWNLOADS) являются стандартными и подходят для использования на других томах хранилища?

android developer 01.05.2021 21:53

@androiddeveloper: «Хорошо ли предположение, что он не может быть обнулен для основного тома хранилища» - вероятно, стоит проверить null, но я понятия не имею, что null будет означать в этом контексте. «также, что те же папки (например, DIRECTORY_DOWNLOADS) являются стандартными и подходят для использования на других томах хранилища?» -- Я бы не стал предполагать, что эти каталоги существуют.

CommonsWare 01.05.2021 22:00

@CommonsWare Каталоги могут отсутствовать даже на основном томе. Я просто спрашиваю, безопасно ли предположить, что они стандартны и их стоит использовать таким образом, или, может быть, стандартными считаются только папки (DIRECTORY_DOWNLOADS и остальные) основного тома.

android developer 01.05.2021 22:34

@androiddeveloper: Ах, извините, понятия не имею.

CommonsWare 01.05.2021 22:36

@CommonsWare Это нормально. Вы везде помогаете. Большое спасибо за уделенное время.

android developer 01.05.2021 22:40

Как переместить мои файлы из общедоступного каталога в каталог Android/Media/com.myapp в Android 11, как это сделал WhatsApp, не спрашивая разрешения? как получить путь к общедоступному каталогу, если getExternalStoragePublicDirectory теперь устарел в Q?

Bhavin Patel 05.07.2021 15:10

Ответ @CommonsWare потрясающий. Но для тех, кто хочет это на Java, вам нужно попробовать это:

ContentResolver resolver = context.getContentResolver();
ContentValues contentValues = new ContentValues();
contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, name);
contentValues.put(MediaStore.MediaColumns.MIME_TYPE, mimeType);
contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS);

Uri uri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues);

Согласно предложению @SamChen, код для текстовых файлов должен выглядеть так:

Uri uri = resolver.insert(MediaStore.Files.getContentUri("external"), contentValues);

Потому что мы бы не хотели, чтобы файлы txt оставались в папке с изображениями.

Итак, место, где у меня есть mimeType, вы вводите тип пантомимы, который вы хотите. Например, если вы хотите txt (@Panache), вы должны заменить mimeType этой строкой: "text/plain". Вот список типов пантомимы: https://www.freeformatter.com/mime-types-list.html

Кроме того, там, где у меня есть переменная name, вы заменяете ее именем файла в вашем случае.

Гаурав, ваш ответ хорош для java-парней, но можете ли вы упомянуть, каков синтаксис для txt-файла?

Panache 26.12.2019 05:20

Gaurav я получаю сообщение об ошибке, не могу вставить текст/обычный текст в MediaStore.Images.Media. Я передал имя как file.getName и mimetype = text/plain. пожалуйста, помогите

Panache 28.12.2019 15:30

Гаурав, только вопрос, а где в этом коде путь к исходному файлу, разве это не обязательно? давайте обсудим здесь stackoverflow.com/questions/59511147/…

Panache 28.12.2019 15:55

Для текстового файла запомните это Uri uri = getContentResolver().insert(MediaStore.Files.getContentUri("‌​external"), values);

Sam Chen 23.03.2020 16:11

Не могли бы вы помочь мне с stackoverflow.com/questions/63543414/…

jazzbpn 24.08.2020 05:44

val imgUri = resolver.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, contentValues)

BAIJU SHARMA 13.09.2020 10:58

Как переместить мои файлы из общедоступного каталога в каталог Android/Media/com.myapp в Android 11, как это сделал WhatsApp, не спрашивая разрешения? как получить путь к общедоступному каталогу, если getExternalStoragePublicDirectory теперь устарел в Q?

Bhavin Patel 05.07.2021 15:12

@bdevloper Я не думаю, что вы можете сделать это, не спрашивая разрешения. Вот и весь вопрос, который здесь обсуждается. И в принципе, в моем коде вы можете заменить Environment.DIRECTORY_DOWNLOADS на любой каталог, который хотите.

Gaurav Mall 06.07.2021 13:50

тогда как WhatsApp сделал? Я не давал разрешения на manage_file_access, и все же они переместили всю папку внутрь Android\media\com.whatsapp

Bhavin Patel 07.07.2021 12:02

Я могу переместить свою папку с помощью кода это, но я не уверен, что это сработает после выхода в play store. Надеюсь, что использование сохранитьLegacyExternalStorage может помочь.

Bhavin Patel 07.07.2021 12:08

@SamChen включает ли файл .xls в текстовые файлы?

Pif 13.08.2021 15:09

@Pif Вы можете попробовать установить mimeType на "text/xls", я никогда не пробовал.

Sam Chen 13.08.2021 15:31

@SamChen, я пытаюсь text/xls и application/vnd.ms-excel сохранить сгенерированный файл, но размер файла 0 КБ, есть идеи, что не так?

Pif 13.08.2021 15:47

@Pif Я не знаю, может быть, файл excel не распознается MediaStore как обычный текстовый файл, но у меня есть идея, вы можете преобразовать файл excel в файл json, text/json это приемлемо, я могу гарантировать. Затем вы конвертируете его обратно в excel. Тем временем спросите StackOverflow.

Sam Chen 13.08.2021 16:37

Использование MediaStore совершенно бесполезно, если вам нужно хранить кучу файлов, в которых смешаны распознанные и нераспознанные мимы. Исключения будут выброшены, если вы это сделаете.

Johann 14.12.2021 16:51

Приложения с таргетингом на Android Q — API 29+ отключил доступ к хранилищу по умолчанию из-за проблем с безопасностью. Если вы хотите включить его, добавьте следующий атрибут в AndroidManifest.xml:

<manifest ... >
    <!-- This attribute is "false" by default for Android Q or higher -->
    <application android:requestLegacyExternalStorage = "true" ... >
     ...
    </application>
</manifest>

тогда вы должны использовать getExternalStorageDirectory() вместо getExternalStoragePublicDirectory().

Пример: если вы хотите создать каталог во внутренней памяти, если он не существует.

 File mediaStorageDir = new File(Environment.getExternalStorageDirectory() + "/SampleFolder");

 // Create the storage directory if it does not exist
 if (! mediaStorageDir.exists()){
     if (! mediaStorageDir.mkdirs()){
         Log.d("error", "failed to create directory");
     }
 }

Вам действительно нужно использовать requestLegacyExternalStorage = "true" для хранения с заданной областью? Я думаю, что раз вы используете getExternalStorageDirectory(), это не обязательно. Кроме того, устаревший флаг игнорируется в API 30+.

Micer 02.02.2021 18:20

Это не сработает, если вы пропустили requestLegacyExternalStorage = "true" на версии API целевого устройства выше 28+.

Codemaker 03.02.2021 08:33

Это нехорошо, так как согласно документации это всего лишь временное решение. Используйте MediaStore и ContentValues

Undefined function 26.04.2021 18:02

import android.content.ContentValues
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.provider.MediaStore
import android.util.Log
import android.widget.Button
import android.widget.EditText
import android.widget.TextView
import androidx.annotation.RequiresApi
import androidx.appcompat.app.AppCompatActivity

class MyActivity : AppCompatActivity() {

    @RequiresApi(Build.VERSION_CODES.Q)
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_mine)
        val editText: EditText = findViewById(R.id.edt)
        val write: Button = findViewById(R.id.Output)
        val read: Button = findViewById(R.id.Input)
        val textView: TextView = findViewById(R.id.textView)

        val resolver = this.contentResolver
        val contentValues = ContentValues().apply {
            put(MediaStore.MediaColumns.DISPLAY_NAME, "myDoc1")
            put(MediaStore.MediaColumns.MIME_TYPE, "text/plain")
            put(MediaStore.MediaColumns.RELATIVE_PATH, "Documents")
        }
        val uri: Uri? = resolver.insert(MediaStore.Files.getContentUri("external"), contentValues)
        Log.d("Uri", "$uri")

        write.setOnClickListener {
            val edt : String = editText.text.toString()
            if (uri != null) {
                resolver.openOutputStream(uri).use {
                    it?.write("$edt".toByteArray())
                    it?.close()

                }
            }
        }
        read.setOnClickListener {
            if (uri != null) {
                resolver.openInputStream(uri).use {
                    val data = ByteArray(50)
                    it?.read(data)
                    textView.text = String(data)

                }
            }
        }
    }
}

Здесь я сохраняю текстовый файл в папке «Документ» телефона, записывая текст в текст редактирования и нажимая кнопку «Записать», он сохраняет файл с написанным текстом. При нажатии кнопки «Читать» он выведет текст из этого файла, а затем отобразит его в текстовом представлении.

It will not run on devices that are below android Q or android 10 as RELATIVE_PATH can only be used in these versions.

Как переместить мои файлы из общедоступного каталога в каталог Android/Media/com.myapp в Android 11, как это сделал WhatsApp, не спрашивая разрешения? как получить путь к общедоступному каталогу, если getExternalStoragePublicDirectory теперь устарел в Q?

Bhavin Patel 05.07.2021 15:12

Для Xamarin.Android может помочь следующий код из опубликованного проекта:

    Java.IO.File jFolder;

    if ((int)Android.OS.Build.VERSION.SdkInt >= 29)
    {
        jFolder = new Java.IO.File(Android.App.Application.Context.GetExternalFilesDir(Environment.DirectoryDcim), "Camera");
    }
    else
    {
        jFolder = new Java.IO.File(Environment.GetExternalStoragePublicDirectory(Environment.DirectoryDcim), "Camera");
    }

    if (!jFolder.Exists())
        jFolder.Mkdirs();

    var filename = GenerateJpgFileName();
    var jFile = new Java.IO.File(jFolder, filename);
    var fullFilename = jFile.AbsoluteFile.ToString();

    using (var output = new System.IO.FileStream(fullFilename, System.IO.FileMode.Create))
    {
        outputBitmap.Compress(Bitmap.CompressFormat.Jpeg, 90, output);
        output.Close();
    }

Не используя разрешения в Android, это делается из общего проекта с помощью Xamarin.Essentials.

Поправьте меня, если я ошибаюсь, но Context.GetExternalFilesDir(Environment.DirectoryDcim) не предоставляет вам путь к папке вашего собственного приложения, что означает, что если вы удалите приложение, эти файлы тоже будут удалены? Это в отличие от другого пути, который позволяет файлам, которые вы создали, оставаться...

android developer 01.05.2021 15:59

Как переместить мои файлы из общедоступного каталога в каталог Android/Media/com.myapp в Android 11, как это сделал WhatsApp, не спрашивая разрешения? как получить путь к общедоступному каталогу, если getExternalStoragePublicDirectory теперь устарел в Q?

Bhavin Patel 05.07.2021 15:12

Обычно я использовал этот способ:

     var data: File =Environment.getExternalStoragePublicDirectory
(Environment.DIRECTORY_DOWNLOADS)
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                        data = getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS)!!
                    }

Environment.getExternalStoragePublicDirectory у меня работает на SDK 30. Только на SDK 29 не работает. У вас есть какая-либо информация, если они устарели на SDK 29 и вернули его на 30 или что-то подобное?

Donki 30.07.2021 14:57

@Donki, вы проверяли --- приложение android:requestLegacyExternalStorage = "true" --- в манифесте?!

Mori 04.08.2021 16:43

Если вы хотите сохранить файл во внешнем хранилище приложения, да, вы можете использовать context.getExternalFilesDir(). Многие ответы указывают на это.

Однако это не ответ на этот вопрос, потому что getExternalFilesDir() — это внешнее хранилище для конкретного приложения, а getExternalStoragePublicDirectory() — это общее хранилище.

Например, вы хотите сохранить загруженный файл PDF в «Общий» каталог загрузки. Как ты это делаешь ? Для API 29 и выше вы можете делать это без разрешения.

Для API 28 и ниже вам нужен метод getExternalStoragePublicDirectory(), но он устарел. Что, если вы не хотите использовать этот устаревший метод? Затем вы можете использовать файловый менеджер SAF (Intent#ACTION_OPEN_DOCUMENT). Как сказано в вопросе, для этого пользователь должен выбрать местоположение вручную.

Это именно то, чего хочет Google. Чтобы повысить конфиденциальность пользователей, прямой доступ к общим/внешним устройствам хранения не рекомендуется.

When an app targets Build.VERSION_CODES.Q, the path returned from this method is no longer directly accessible to apps. Apps can continue to access content stored on shared/external storage by migrating to alternatives such as Context#getExternalFilesDir(String), MediaStore, or Intent#ACTION_OPEN_DOCUMENT.

Подробности приведены по следующей ссылке:

https://developer.android.com/reference/android/os/Environment#getExternalStorageDirectory()

Другие вопросы по теме