Получаем разрешение MANAGE_EXTERNAL_STORAGE для приложения
Безопасность данных в операционной системе является очень важной задачей, и Android здесь не является исключением. Так, Google в Android 10 добавили новый способ обеспечения безопасности, называемый хранилищем с ограниченной областью видимости (Scoped storage).
До Android 10 всё работало достаточно просто: приложение запрашивало доступ к хранилищу, используя одно из разрешений (WRITE_EXTERNAL_STORAGE либо READ_EXTERNAL_STORAGE), и, после того как пользователь предоставлял разрешение, приложение получало возможность прочесть и изменить практически любой файл, хранящийся на устройстве, за исключением системных файлов и папок, а также папок других приложений. Иначе говоря, приложение просто получало доступ ко всей файловой системе. Scoped storage же изменил этот подход. Теперь приложение по умолчанию имеет доступ только к некоторым участкам памяти, где хранятся общедоступные файлы, такие как медиа, загруженные файлы, некоторые документы. При этом полный доступ приложение имеет только к тем файлам, которые находятся непосредственно в папке приложения, расположенной в Android/data/ . Если приложению нужно изменить или удалить файл, находящийся вне этой папки, то приложение должно запросить у пользователя разрешение на конкретную операцию с помощью MediaStore API (для медиа-файлов) или через Storage Access Framework (для всех остальных файлов). Однако этот способ очень неудобен, если мы разрабатываем файловый менеджер или приложение, которое должно работать не с медиа-файлами.
Scoped storage был добавлен в Android 10 как опциональная функция, которую легко можно было отключить, добавив в AndroidManifest.xml приложения строчку android:requestLegacyExternalStorage=»true» внутри элемента . Таким образом, можно было вернуть работу с файлами так, как было раньше. Однако, начиная с Android 11, использование Scoped storage стало обязательным. Безусловно, это улучшило безопасность и сохранность файлов в операционной системе, но и прибавило проблем разработчикам, заставив их пересмотреть принципы работы своих приложений с файловой системой.
Поскольку часть приложений всё же требует для своей работы полный доступ к хранилищу, а не ограниченный, Google добавили новое разрешение MANAGE_EXTERNAL_STORAGE для таких случаев. Это разрешение, как и раньше, позволяет получить доступ ко всей файловой системе, однако для его использования в приложении требуется подтверждение со стороны Google. В этой статье мы разберём, как использовать разрешение в приложении, а также рассмотрим процесс заполнения заявки в консоли Google Play.
Для начала создадим новый проект с пустой активностью. Для начала нам нужно определить в манифесте, что приложению нужны следующие разрешения для работы. Добавим их в файле AndroidManifest.xml.
Поскольку MANAGE_EXTERNAL_STORAGE используется в обязательном порядке только начиная с API 30, то нам всё ещё нужно обрабатывать запрос разрешений на предыдущих версиях так, как мы это делали раньше. Для этого мы добавляем также WRITE_EXTERNAL_STORAGE, но ограничиваем его использование API 29. Таким образом, мы будем каждый раз проверять уровень API устройства и, в зависимости от этого, использовать либо старое разрешение, либо новое.
Как уже упоминалось выше, в случае с Android 10 для работы с файловой системой нам также нужно в манифесте добавить следующий флаг в элемент .
В файле разметки activity_main.xml добавим несколько элементов для тестирования:
Перейдём к написанию кода. Создадим класс PermissionUtils, в котором будет находиться проверка разрешений и их запрос.
Затем в классе активности MainActivity.java определим элементы из разметки.
Текстовое поле будет отображать, если у приложения необходимые разрешения. Для этого после объявления переменных добавим проверку.
Если запустить приложение сейчас, то у нас всегда будет выводиться сообщение о том, что разрешений нет, поскольку мы их пока что не запрашивали.
Добавим обработчик для кнопки, который будет отправлять запрос на предоставление разрешения.
Нам также необходимо задать код запроса, чтобы мы могли после определить, что ответ пришёл именно для этого запроса.
Когда пользователь нажмёт на кнопку, у него откроется новая активность с настройками приложения, в которой будет предложено дать полный доступ данному приложению. Пользователь может как согласиться, так и отклонить запрос.
Поскольку это также работает и для старых версий Android, то в случае с ними пользователю вместо активности будет предложен старый диалог с запросом на предоставление разрешений.
Любой результат действий пользователя в итоге возвращается в нашу активность. Поскольку для API 30 мы запускаем отдельную активность, то результат работы мы должны отслеживать в методе onActivityResult(). Переопределим его и добавим следующий код.
Здесь мы снова проверяем, дал ли пользователь разрешение или нет, и обновляем текстовое поле в соответствии с результатом.
Аналогичным образом проверяем результат операции для старых уровней API, но здесь уже переопределяем метод onRequestPermissionResult().
Теперь у нашего приложения есть полный доступ к файловой системе, и технически этого уже достаточно. Однако если попытаться опубликовать такое приложение в Google Play, то публикацию запретят по причине отсутствия заявки на получение доступа ко всем файлам. Поэтому, после того, как в консоли будет создан выпуск и загружена туда новая версия приложения, нам нужно перейти в раздел Контент приложения и выбрать там появившийся пункт Важные разрешения и API.
В появившейся форме требуется описать подробно, зачем приложению требуется доступ ко всем файлам, относится ли это к основному функционалу приложения, а также объяснить с технической точки зрения почему невозможно использовать в приложении альтернативные способы.
Также, в случае если показать работу приложения проще, чем объяснить, можно также записать демо-ролик работы приложения, загрузить его на Youtube и прикрепить в данной форме ссылку на видео. В некоторых случаях это может ускорить получение доступа.
Итак, заполнив форму и сохранив её, можно вернуться обратно в настройку выпуска и проверить его. Предупреждение об отсутствии заявки должно пропасть и мы может отправить выпуск на публикацию. В процессе публикации Google тщательно проверит приложении, чтобы убедиться, что доступ ко всем файлам действительно необходим приложению. Как показывает практика, для приложений, представляющих собой файловые менеджеры, заявку одобрят достаточно быстро.
Когда Google одобрил доступ для приложения, новая версия успешно публикуется, а в разделе Контент приложения указано разрешение, которое было одобрено.
Однако, Google также могут и не одобрить заявку, если посчитают, что это не является обязательной функцией или если можно ограничиться MediaStore\Storage Access Framework. Здесь может помочь изменение в работе приложения либо переоформление заявки, это нужно учитывать.
Таким образом, с помощью пары строк кода можно обновить своё приложения для работы с файловой системой на новых версиях Android.
Источник
Data and file storage overview
Android uses a file system that’s similar to disk-based file systems on other platforms. The system provides several options for you to save your app data:
- App-specific storage: Store files that are meant for your app’s use only, either in dedicated directories within an internal storage volume or different dedicated directories within external storage. Use the directories within internal storage to save sensitive information that other apps shouldn’t access.
- Shared storage: Store files that your app intends to share with other apps, including media, documents, and other files.
- Preferences: Store private, primitive data in key-value pairs.
- Databases: Store structured data in a private database using the Room persistence library.
The characteristics of these options are summarized in the following table:
Type of content | Access method | Permissions needed | Can other apps access? | Files removed on app uninstall? | |
---|---|---|---|---|---|
App-specific files | Files meant for your app’s use only | From internal storage, getFilesDir() or getCacheDir() From external storage, getExternalFilesDir() or getExternalCacheDir() | Never needed for internal storage Not needed for external storage when your app is used on devices that run Android 4.4 (API level 19) or higher | No | Yes |
Media | Shareable media files (images, audio files, videos) | MediaStore API | READ_EXTERNAL_STORAGE when accessing other apps’ files on Android 11 (API level 30) or higher READ_EXTERNAL_STORAGE or WRITE_EXTERNAL_STORAGE when accessing other apps’ files on Android 10 (API level 29) Permissions are required for all files on Android 9 (API level 28) or lower | Yes, though the other app needs the READ_EXTERNAL_STORAGE permission | No |
Documents and other files | Other types of shareable content, including downloaded files | Storage Access Framework | None | Yes, through the system file picker | No |
App preferences | Key-value pairs | Jetpack Preferences library | None | No | Yes |
Database | Structured data | Room persistence library | None | No | Yes |
The solution you choose depends on your specific needs:
How much space does your data require? Internal storage has limited space for app-specific data. Use other types of storage if you need to save a substantial amount of data. How reliable does data access need to be? If your app’s basic functionality requires certain data, such as when your app is starting up, place the data within internal storage directory or a database. App-specific files that are stored in external storage aren’t always accessible because some devices allow users to remove a physical device that corresponds to external storage. What kind of data do you need to store? If you have data that’s only meaningful for your app, use app-specific storage. For shareable media content, use shared storage so that other apps can access the content. For structured data, use either preferences (for key-value data) or a database (for data that contains more than 2 columns). Should the data be private to your app? When storing sensitive data—data that shouldn’t be accessible from any other app—use internal storage, preferences, or a database. Internal storage has the added benefit of the data being hidden from users.
Categories of storage locations
Android provides two types of physical storage locations: internal storage and external storage. On most devices, internal storage is smaller than external storage. However, internal storage is always available on all devices, making it a more reliable place to put data on which your app depends.
Removable volumes, such as an SD card, appear in the file system as part of external storage. Android represents these devices using a path, such as /sdcard .
Apps themselves are stored within internal storage by default. If your APK size is very large, however, you can indicate a preference within your app’s manifest file to install your app on external storage instead:
Permissions and access to external storage
On earlier versions of Android, apps needed to declare the READ_EXTERNAL_STORAGE permission to access any file outside the app-specific directories on external storage. Also, apps needed to declare the WRITE_EXTERNAL_STORAGE permission to write to any file outside the app-specific directory.
More recent versions of Android rely more on a file’s purpose than its location for determining an app’s ability to access, and write to, a given file. In particular, if your app targets Android 11 (API level 30) or higher, the WRITE_EXTERNAL_STORAGE permission doesn’t have any effect on your app’s access to storage. This purpose-based storage model improves user privacy because apps are given access only to the areas of the device’s file system that they actually use.
Android 11 introduces the MANAGE_EXTERNAL_STORAGE permission, which provides write access to files outside the app-specific directory and MediaStore . To learn more about this permission, and why most apps don’t need to declare it to fulfill their use cases, see the guide on how to manage all files on a storage device.
Scoped storage
To give users more control over their files and to limit file clutter, apps that target Android 10 (API level 29) and higher are given scoped access into external storage, or scoped storage, by default. Such apps have access only to the app-specific directory on external storage, as well as specific types of media that the app has created.
Use scoped storage unless your app needs access to a file that’s stored outside of an app-specific directory and outside of a directory that the MediaStore APIs can access. If you store app-specific files on external storage, you can make it easier to adopt scoped storage by placing these files in an app-specific directory on external storage. That way, your app maintains access to these files when scoped storage is enabled.
To prepare your app for scoped storage, view the storage use cases and best practices guide. If your app has another use case that isn’t covered by scoped storage, file a feature request. You can temporarily opt-out of using scoped storage.
View files on a device
To view the files stored on a device, use Android Studio’s Device File Explorer.
Additional resources
For more information about data storage, consult the following resources.
Videos
Content and code samples on this page are subject to the licenses described in the Content License. Java is a registered trademark of Oracle and/or its affiliates.
Источник