Внешнее хранилище

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

Ранее внешним хранилищем назывался раздел диска на съемном носителе, таком как SD-карта (также известном как переносное устройство). Это различие уже неактуально, так как устройства Android существенно изменились и многие устройства Android больше не поддерживают съемные носители. Вместо этого некоторые устройства будут выделять часть внутренней ненезависимой памяти в Android, которая может выполнять ту же функцию, что и съемный носитель. Это называется эмулированным хранилищем и по-прежнему считается внешним. Кроме того, у некоторых устройств Android может быть несколько внешних разделов хранилища. Например, в планшете Android (помимо его внутреннего хранилища) может быть эмулированное хранилище и один или несколько слотов для SD-карты. Все эти разделы рассматриваются Android как внешнее хранилище.

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

В Xamarin.Android чтение и запись в файлы практически идентична этим процессам в любых других приложениях .NET. Приложение Xamarin.Android определяет путь к файлу, который будет обработан, а затем использует стандартные идиомы .NET для доступа к файлам. Так как фактические пути к внутреннему и внешнему хранилищу могут отличаться на разных устройствах или в разных версиях Android, не рекомендуется жестко кодировать путь к файлам. Вместо этого Xamarin.Android предоставляет собственные API Android, которые помогут определить путь к файлам во внутреннем и внешнем хранилище.

В этом руководстве описываются основные понятия и API в Android, относящиеся к внешнему хранилищу.

Общедоступные и частные файлы во внешнем хранилище

Существует два разных типа файлов, которые приложение может хранить во внешнем хранилище:

  • Частные файлы — это файлы, относящиеся к приложению (но по-прежнему доступны для чтения и записи в мире). В Android предполагается, что частные файлы хранятся в определенном каталоге во внешнем хранилище. Несмотря на то, что файлы называются "частными", они по-прежнему видимы и доступны для других приложений на устройстве, Android не предоставляет им специальные меры защиты.

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

Разница между этими файлами в основном концептуальная. Файлы являются частными, так как они считаются частью приложения, в то время как общедоступные файлы — это любые другие файлы, которые существуют во внешнем хранилище. Android предоставляет два разных API-интерфейса для разрешения путей к частным и общедоступным файлам, но в остальном для чтения и записи в эти файлы используются одни и те же интерфейсы API .NET. Это те же API-интерфейсы, которые рассматриваются в разделе о чтении и записи.

Частные внешние файлы

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

Основное расположение для частных внешних файлов определяется путем вызова метода Android.Content.Context.GetExternalFilesDir(string type). Этот метод возвращает объект Java.IO.File, представляющий частный каталог во внешнем хранилище для приложения. Передача null этому методу приведет к возвращению пути к каталогу хранилища пользователя для приложения. Например, для приложения с именем пакета com.companyname.app корневой каталог частных внешних файлов будет таким:

/storage/emulated/0/Android/data/com.companyname.app/files/

Этот документ будет ссылаться на каталог хранилища для частных файлов во внешнем хранилище как PRIVATE_EXTERNAL_STORAGE.

Параметр для GetExternalFilesDir() представляет собой строку, которая указывает каталог приложения. Этот каталог предназначен для стандартного расположения в логической организации файлов. Строковые значения доступны через константы класса Android.OS.Environment:

Android.OS.Environment Directory
DirectoryAlarms PRIVATE_EXTERNAL_STORAGE/будильники
DirectoryDcim PRIVATE_EXTERNAL_STORAGE/DCIM
DirectoryDownloads PRIVATE_EXTERNAL_STORAGE/скачать
DirectoryDocuments PRIVATE_EXTERNAL_STORAGE/Документы
DirectoryMovies PRIVATE_EXTERNAL_STORAGE/Фильмы
DirectoryMusic PRIVATE_EXTERNAL_STORAGE/Музыка
DirectoryNotifications PRIVATE_EXTERNAL_STORAGE/уведомления
DirectoryPodcasts PRIVATE_EXTERNAL_STORAGE/Podcasts
DirectoryRingtones PRIVATE_EXTERNAL_STORAGE/Ringtones
DirectoryPictures PRIVATE_EXTERNAL_STORAGE/Рисунки

Для устройств с несколькими разделами во внешнем хранилище каждый раздел будет содержать каталог, предназначенный для частных файлов. Метод Android.Content.Context.GetExternalFilesDirs(string type) возвращает массив Java.IO.Files. Каждый объект будет представлять частный каталог приложения для всех совместно используемых устройств с внешними хранилищами, где приложение может размещать принадлежащие ему файлы.

Внимание

Точный путь к частному каталогу внешнего хранилища может отличаться в зависимости от устройства и версии Android. По этой причине приложения не должны жестко задавать путь к этому каталогу. Вместо этого они должны использовать API-интерфейсы Xamarin.Android, например Android.Content.Context.GetExternalFilesDir().

Общедоступные внешние файлы

Общедоступные файлы — это файлы, которые существуют во внешнем хранилище и не хранятся в каталоге, который Android выделяет для частных файлов. Общедоступные файлы не удаляются при удалении приложения. Приложения Android должны получить разрешение, прежде чем они смогут считывать или записывать любые общедоступные файлы. Общедоступные файлы могут существовать везде во внешнем хранилище, но по соглашению в Android предусматривается, что общедоступные файлы будут существовать в каталоге, указанном свойством Android.OS.Environment.ExternalStorageDirectory. Это свойство будет возвращать объект Java.IO.File, который представляет основной каталог во внешнем хранилище. В качестве примера Android.OS.Environment.ExternalStorageDirectory может ссылаться на следующий каталог:

/storage/emulated/0/

Этот документ будет ссылаться на каталог хранилища для общедоступных файлов во внешнем хранилище как PUBLIC_EXTERNAL_STORAGE.

Android также поддерживает концепцию каталогов приложений на PUBLIC_EXTERNAL_STORAGE. Эти каталоги в точности совпадают с каталогами приложений для PRIVATE_EXTERNAL_STORAGE и описаны в таблице в предыдущем разделе. Метод Android.OS.Environment.GetExternalStoragePublicDirectory(string directoryType) возвращает объект Java.IO.File, который соответствует общедоступному каталогу приложения. Параметр directoryType является обязательным и не может иметь значение null.

Например, вызов Environment.GetExternalStoragePublicDirectory(Environment.DirectoryDocuments).AbsolutePath вернет строку, которая будет выглядеть так:

/storage/emulated/0/Documents

Внимание

Точный путь к общедоступному каталогу во внешнем хранилище может отличаться в зависимости от устройства и версии Android. По этой причине приложения не должны жестко задавать путь к этому каталогу. Вместо этого они должны использовать API-интерфейсы Xamarin.Android, например Android.OS.Environment.ExternalStorageDirectory.

Работа с внешним хранилищем

Как только приложение Xamarin.Android получит полный путь к файлу, оно должно использовать любой из стандартных API-интерфейсов .NET для создания, чтения, записи или удаления файлов. Это увеличивает объем кроссплатформенного кода для приложения. Тем не менее, прежде чем пытаться получить доступ к файлу, для приложения Xamarin.Android необходимо убедиться, что к этому файлу можно осуществлять доступ.

  1. Проверьте внешнее хранилище. В зависимости от характера внешнего хранилища возможно, что оно не может быть подключено и доступно приложению. Все приложения должны проверять состояние внешнего хранилища, прежде чем пытаться его использовать.
  2. Выполнение разрешения среды выполнения проверка. Приложение Android должно запрашивать разрешение от пользователя, чтобы получить доступ к внешнему хранилищу. Это означает, что запрос на разрешение во время выполнения должен быть сделан до осуществления любого доступа к файлу. Руководство Разрешения в Xamarin.Android содержит более подробные сведения о разрешениях Android.

Каждая из этих двух задач будет описана ниже.

Проверка доступности внешнего хранилища

Первым действием перед записью во внешнее хранилище является проверка его доступности для чтения или записи. Свойство Android.OS.Environment.ExternalStorageState содержит строку для определения состояния внешнего хранилища. Это свойство будет возвращать строку, которая представляет состояние. Эта таблица представляет собой список значений ExternalStorageState, которые могут быть возвращены Environment.ExternalStorageState:

ExternalStorageState Description
MediaBadRemoval Носитель внезапно удален без отключения надлежащим образом.
MediaChecking Носитель присутствует, но проходит проверку диска.
MediaEjecting Носитель пребывает в процессе отключения и извлечения.
MediaMounted Носитель подключен, в нем можно выполнять операции чтения и записи.
MediaMountedReadOnly Носитель подключен, но в нем можно выполнять только операции чтения.
MediaNofs Носитель присутствует, но не содержит файловой системы, подходящей для Android.
MediaRemoved Носитель отсутствует.
MediaShared Носитель присутствует, но не подключен. Его использует через USB-порт другое устройство.
MediaUnknown Состояние носителя не распознано Android.
MediaUnmountable Носитель присутствует, но его не удалось подключить к Android.
MediaUnmounted Носитель присутствует, но отключен.

Большинству приложений Android нужно будет только проверить, подключено ли внешнее хранилище. В следующем фрагменте кода показано, как проверить, подключено внешнее хранилище только для чтения или для чтения и записи:

bool isReadonly = Environment.MediaMountedReadOnly.Equals(Environment.ExternalStorageState);
bool isWriteable = Environment.MediaMounted.Equals(Environment.ExternalStorageState);

Разрешения внешнего хранилища

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

Все приложения Android должны объявить одно из двух разрешений для внешнего хранилища в AndroidManifest.xml. Чтобы определить разрешения, один из следующих двух элементов uses-permission должен быть добавлен в AndroidManifest.xml:

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

Примечание.

Если пользователь предоставил разрешение WRITE_EXTERNAL_STORAGE, то также неявно предоставляется и READ_EXTERNAL_STORAGE. Нет необходимости запрашивать оба разрешения в AndroidManifest.xml.

Разрешения также можно добавить на вкладке Манифест Android в разделе свойств решения:

Solution Explorer - Required Permissions for Visual Studio

В общем, все опасные разрешения подлежат одобрению пользователем. Разрешения для внешнего хранилища являются аномалией в том смысле, что есть исключения из этого правила в зависимости от версии Android, на которой работает приложение:

Flowchart of external storage permission checks

Дополнительные сведения о запросах на разрешения во время выполнения см. в руководстве Разрешения в Xamarin.Android. Monodroid-sample LocalFiles также демонстрирует один из способов выполнения разрешений среды выполнения проверка.

Предоставление и отмена разрешений с помощью adb

Во время разработки приложения Android может потребоваться предоставить и отменить разрешения для проверки различных рабочих процессов, связанных с проверкой разрешений во время выполнения. Для этого можно использовать командную строку и adb. В следующих фрагментах командной строки показано, как предоставлять или отменять разрешения с помощью adb для приложения Android, имя пакета которого — com.companyname.app:

$ adb shell pm grant com.companyname.app android.permission.WRITE_EXTERNAL_STORAGE

$ adb shell pm revoke com.companyname.app android.permission.WRITE_EXTERNAL_STORAGE

Удаление файлов

Любой из стандартных API-интерфейсов C# можно использовать для удаления файла из внешнего хранилища, например System.IO.File.Delete. Для этого также можно использовать API-интерфейсы Java за счет переносимости кода. Например:

System.IO.File.Delete("/storage/emulated/0/Android/data/com.companyname.app/files/count.txt");