Android persistent file location

Хранение данных и файлов

В целом хранение файлов и данных можно условно разделить на две группы: во внутреннем или внешнем хранилище. Но разница между ними довольна тонка. В целом политика Гугла в отношение данных ужесточается с каждой версии системы.

Android поддерживает различные варианты хранения данных и файлов.

  • Специфичные для приложения файлы. Доступ к файлам имеет только приложение, их создавшее. Файлы могут находиться во внутреннем и внешнем хранилище. У других приложений нет доступа (кроме случаев, когда файлы хранятся на внешнем хранилище). Методы getFilesDir(), getCacheDir(), getExternalFilesDir(), getExternalCacheDir(). Разрешений на доступ не требуется. Файлы удаляются, когда приложение удаляется пользователем.
  • Разделяемое хранилище. Приложение может создавать файлы, которыми готово поделиться с другими приложениями — медиафайлы (картинки, видео, аудио), документы. Для медифайлов требуется разрешение READ_EXTERNAL_STORAGE или WRITE_EXTERNAL_STORAGE.
  • Настройки. Хранение простых данных по принципу ключ-значение. Доступно внутри приложения. Реализовано через Jetpack Preferences. Настройки удаляются, когда приложение удаляется пользователем.
  • Базы данных. Хранение данных в SQLite. На данный момент реализовано через библиотеку Room. Доступ только у родного приложения.

В зависимости от ваших потребностей, нужно выбрать нужный вариант хранения данных.

Следует быть осторожным при работе с внутренним и внешним хранилищем. Внутренне хранилище всегда есть в системе, но оно может быть не слишком большим по объёму. Вдобавок к внутреннему хранилищу, устройство может иметь внешнее хранилище. В старых моделях таким хранилищем выступала съёмная SD-карта. Сейчас чаще используют встроенную и недоступную для извлечения флеш-память. Если ваше приложение слишком большое, можно попросить систему устанавливать программу во внешнее хранилище, указав просьбу в манифесте.

В разных версиях Android требования к разрешению для работы с внешним хранилищем постоянно менялись. На данный момент (Android 10, API 29) требования выглядят следующим образом.

Приложение может иметь доступ к собственным файлам, которые находятся во внешнем хранилище. Также может получить доступ к определённым общим файлам на внешнем хранилище.

Доступ к общим файлам достигается через FileProvider API или контент-провайдеры.

Для просмотра файлов через студию используйте инструмент Device File Explorer.

Внешняя карта памяти

Когда появились первые устройства на Android, то практически у всех были внешние карточки памяти, которые вставлялись в телефон. Обычно там хранили фотки, видео и свои файлы. Всё было понятно — были различные методы для доступа к файловой системе. А потом началась чехарда. В телефонах также была и собственная «внешняя» память. Она вроде как и внешняя, но вставлена на заводе и вытащить её пользователь не мог, т.е. практически внутренняя. Затем пошла мода на телефоны, у которых была только такая внутреннее-внешняя карта. Пользователи поворчали, но привыкли. Сейчас встречаются оба варианта. Как правило, у телефонов с спрятанной картой больше памяти и выше степень водонепроницаемости.

Подобные фокусы с картой породили и другую проблему — Гугл озаботился безопасностью файлов и стала думать, как осложнить жизнь разработчику. С выходом каждой новой версии системы компания то давала добро на полный доступ к карточке, то ограничивала, то давала права с ограничениями, то откатывала свои решения назад. Короче, запутались сами и запутали всех.

Попробуем немного разобраться с этим зоопарком. Но помните, что процесс путаницы продолжается.

При подготовке материала я опирался на письма некоторых читателей сайта, которые присылали свои мысли по этому поводу. Спасибо им за структуризацию материала.

Вот что я (кажется) понял, попытавшись загрузить картинку с внешней SD карточки.

External это не External
«EXTERNAL_STORAGE» называется так не потому, что это внешняя память по отношению к устройству, а потому что она выглядит как внешняя память для компьютера, если устройство подключить кабелем к компьютеру. Причём именно выглядит, потому что обмен идёт по протоколу MTP – устройство только показывает компьютеру список папок и файлов, а при необходимости открыть или скопировать файл он специально загружается на компьютер, в отличие от настоящей флешки, файлы которой становятся файлами в файловой системе самого компьютера. Обмен по MTP позволяет устройству продолжать работать, когда оно подключено к компьютеру.

Читайте также:  Что такое голова для андроид

Emulated это не Emulated
Сначала я пытался прочесть файл с карточки на эмуляторе (из этого так ничего и не вышло). Функция getExternalStorageDirectory() давала мне /storage/emulated/0, и я думал, что «emulated» – это потому что на эмуляторе. Но когда я подцепил реальный планшет, слово «emulated» никуда не исчезло. Я стал рыться в интернете и обнаружил, что «Emulated storage is provided by exposing a portion of internal storage through an emulation layer and has been available since Android 3.0.» – то есть это просто кусок внутренней памяти, которая путём какой-то эмуляции делается доступной для пользователя, в отличие от собственно внутренней памяти.

При этом с точки зрения системы доступная для пользователя папка называется /storage/emulated/0, а при подключении к компьютеру по USB это просто одна из двух главных папок устройства – у меня в Windows Explorer она называется Tablet. Вторая папка у меня называется Card, и это и есть настоящая внешняя карточка.

Нет стандартных средств добраться из приложения до файлов на внешней карточке. Все попытки добраться до настоящей внешней карточки делаются с помощью неких трюков. Самое интересное, что я нашел, это статья на http://futurewithdreams.blogspot.com/2014/01/get-external-sdcard-location-in-android.html — парень читает таблицу смонтированных устройств /proc/mounts, таблицу volume daemons /system/etc/vold.fstab, сравнивает их и выбирает те тома, которые оказываются съёмными (с помощью Environment.isExternalStorageRemovable()).

Оказалось, что несистемным приложениям в принципе запрещено напрямую обращаться к съёмной карточке! Похоже, что это было так всегда, но вот начиная с версии Android 6 Marshmallow написано: внешняя карточка может быть определена как Portable либо Adoptable. Adoptable – это как бы «усыновляемая» память которая может быть «adopted», то есть взята в систему (примерно как кот с улицы в дом – это тоже называется to adopt) и использована как внутренняя. Для этого ее надо особым образом отформатировать и не вынимать, иначе не факт, что система продолжит нормально работать.

Portable – это нормальная съёмная карточка, но несистемным приложениям запрещено обращаться из программ к файлам на ней! Вот что написано в https://source.android.com/devices/storage/traditional.html:

Android 6.0 supports portable storage devices which are only connected to the device for a short period of time, like USB flash drives. When a user inserts a new portable device, the platform shows a notification to let them copy or manage the contents of that device. In Android 6.0, any device that is not adopted is considered portable. Because portable storage is connected for only a short time, the platform avoids heavy operations such as media scanning. Third-party apps must go through the Storage Access Framework to interact with files on portable storage; direct access is explicitly blocked for privacy and security reasons.

Если я правильно понял, этот самый Storage Access Framework позволяет работать с документом на карточке через диалог (открыть файл/сохранить файл), а вот прочитать или записать файл на карточке непосредственно из программы невозможно.

Общий вывод – реально из программы можно работать только с файлами на предоставляемой пользователю части встроенной памяти устройства, а на съёмной карточке – нет.

Это напоминает войну Microsoft с пользователями и разработчиками по поводу диска C:, компания уговаривала не устраивать беспорядок в корне этого диска, а ещё лучше — перенести свои файлы на другой диск. Но явных запретов не было.

Состояние на текущий момент

Гугл утверждает, что с версии Android 10 Q стандартный доступ к файлам будет прекращён. Ещё в Android 4.4 появился Storage Access Framework, который и должен стать заменой для работы с файлами.

Методы Environment.getExternalStorageDirectory() и Environment.getExternalStoragePublicDirectory() признаны устаревшими и будут недоступны. Даже если они будут возвращать корректные значения, ими вы не сможете воспользоваться.

В Android 7.0 добавили исключение FileUriExposedException, чтобы разработчики перестали использовать схему file://Uri.

Можно создавать файлы в корневой папке карточки при помощи Environment.getExternalStorageDirectory(), а также папки с вложенными файлами. Если папка уже существует, то у вас не будет доступа на запись (если это не ваша папка).

Читайте также:  Ускорить работу системы андроид

Если вы что-то записали, то сможете и прочитать. Чужое читать нельзя.

Кстати, разрешения на чтение и запись файлов не требуются, а READ_EXTERNAL_STORAGE и WRITE_EXTERNAL_STORAGE объявлены устаревшими.

Другие приложения не могут получить доступ к файлам вашего приложения. Файлы, которые вы создали через getExternalFilesDir(), доступны через Storage Access Framework, кроме файлов, созданных в корне карточки (что-то я совсем запутался). Ещё можно дать доступ через FileProvider.

При подключении USB-кабеля через getExternalFilesDir(), вы можете увидеть свои файлы и папки, а также файлы и папки пользователя. При этом файлы и папки пользователя на корневой папке вы не увидите. Вам не поможет даже adb или Device File Explorer студии.

Что делать?

Пользуйтесь методами класса Context, типа getExternalFilesDir(), getExternalCacheDir(), getExternalMediaDirs(), getObbDir() и им подобными, чтобы найти место для записи.

Используйте Storage Access Framework.

Используйте MediaStore для мультимедийных файлов.

Используйте FileProvider, чтобы файлы были видимы другим приложениям через ACTION_VIEW/ACTION_SEND.

Android 10: Появился новый флаг android:allowExternalStorageSandbox=»false» и метод Environment.isExternalStorageSandboxed() для работы с песочницей. Флаг android:requestLegacyExternalStorage=»true» для приложений, которые ещё используют старую модель доступа к файлам.

Как временное решение можно добавить в блок манифеста application атрибут android:requestLegacyExternalStorage=»true», чтобы доступ к файлам был как раньше в Android 4.4-9.0.

Android 11

Если вы создаёте файловый менеджер, то ему нужны возможности для просмотра файлов. Для этого следует установить разрешение MANAGE_EXTERNAL_STORAGE или использовать атрибут android:requestLegacyExternalStorage=»true» (см. выше).

Источник

Android Persistence with preferences and files — Tutorial

File based persistence in Android. This tutorial describes how to save key-value pairs using the preference API in Android. It also explains how to read and write files in Android.

1. File based persistence

1.1. Methods of local data persistence

Android allows to persists application data via the file system. For each application the Android system creates a data/data/[application package] directory.

Android supports the following ways of storing data in the local file system:

Files — You can create and update files

Preferences — Android allows you to save and retrieve persistent key-value pairs of primitive data type.

SQLite database — instances of SQLite databases are also stored on the local file system.

Files are saved in the files folder and application settings are saved as XML files in the shared_prefs folder.

If your application creates an SQLite database this database is saved in the main application directory under the databases folder.

The following screenshot shows a file system which contains file, cache files and preferences.

Only the application can write into its application directory. It can create additional sub-directories in this application directory. For these sub-directories, the application can grant read or write permissions for other applications.

1.2. Internal vs. external storage

Android has internal storage and external storage. External storage is not private and may not always be availale. If, for example, the Android device is connected with a computer, the computer may mount the external system via USB and that makes this external storage not avaiable for Android applications.

1.3. Application on external storage

As of Android 8 SDK level it is possible to define that the application can or should be placed on external storage. For this set the android:installLocation to preferExternal or auto.

In this case certain application components may be stored on an encrypted external mount point. Database and other private data will still be stored in the internal storage system.

2. Preferences

2.1. Storing key-value pairs

The SharedPreferences class allows to persists key-value pairs of primitive data types in the Android file system.

The PreferenceManager class provides methods to get access to these preferences. The following code shows how to access preferences from a certain file

Preferences should be created private for the application. They can be accessed via all application components.

A default store for preferences can be accessed via the PreferenceManager.getDefaultSharedPreferences(this) method call. Preference value are accessed via the key and the instance of the SharedPreferences class, as demonstrated in the following listing.

Читайте также:  Управление экраном android adb

To create or change preferences you have to call the edit() method on the SharedPreferences object. Once you have changed the value you have to call the apply() method to apply your asynchronously to the file system.

The usage of the commit() method is discouraged, as it write the changes synchronously to the file system.

2.2. Preference Listener

You can listen to changes in the preferences via the registerOnSharedPreferenceChangeListener() method on SharedPreferences .

One watch out is that SharedPreferences keeps listeners in a WeakHashMap hence listener may be recycled if your code does not hold a reference to it.

2.3. User interface for preferences

Android allows you to create XML resource files which describes preference key-values. An instance of PreferenceActivity or PreferenceFragment can generate an user interface for these files. The user interfaces takes care of persisting the key-value pairs.

To create a preference resource file of type XML.

Android provides the PreferenceFragment class which simplifies the creation of an user interface for maintaining preference values. This fragment can load an XML preference definition file via the method addPreferencesFromResource() .

3. Exercise: Prerequisites

The following exercise assumes that you have created an Android project with the top-level package com.example.android.rssfeed. This application has at least one entry in the toolbar with the R.id.action_settings id. Once this toolbar entry is selected, an existing fragment is replaced.

4. Exercise: Preference setting for the RSS feed

In this exercise you allow the user to enter his preferred URL for an RSS feed via another fragment. This fragment uses a preference xml file to define the user interface.

4.1. Create preference file

Create an Android XML resource called mypreferences.xml in the xml folder. Add entries to this file similar to the following listing.

4.2. Create settings fragment

Create the class SettingsFragment . It should extends PreferenceFragment . This fragment loads the preference file and allows the user to change the values.

4.3. Connect your settings fragment

Ensure that you open the preference fragment via the onOptionsItemSelected() method. The relevant code is demonstrated in the following listing.

4.4. Use the preference value to load the RSS feed

The following code snippet demonstrates how you can access the preference value in a method.

4.5. Validate

Run your application. Select from your action bar the Settings action. You should be able to enter a URL. If you press the back button and press the refresh button, ensure that the value of the url preference is used in your activity.

4.6. Optional: Show the current value in the settings

The following code snippet demonstrates how to show the current value in the preference screen.

5. Android File API

5.1. Using the file API

Access to the file system is performed via the standard java.io classes. Android provides also helper classes for creating and accessing new files and directories. For example the getDir(String, int) method would create or access a directory. The openFileInput(String s) method provides access to an FileInputStream for the file. The openFileOutput(String s, Context.MODE_PRIVATE) method provides access to an FileOutputStream for the file.

All modes except Context.MODE_PRIVATE are deprecated, files should be private to the application.

The following example shows the API usage.

5.2. External storage

Android supports also access to an external storage system, e.g., the SD card. All files and directories on the external storage system are readable for all applications with the correct permission.

To read from external storage your application need to have the android.permission.READ_EXTERNAL_STORAGE permission.

To write to the external storage system your application needs the android.permission.WRITE_EXTERNAL_STORAGE permission. You get the path to the external storage system via the Environment.getExternalStorageDirectory() method.

Via the following method call you can check the state of the external storage system. If the Android device is connected via USB to a computer, external storage might not be available.

The following shows an example for reading from the external storage system.

Источник

Оцените статью