Хранение и доступ к файлам с помощью Xamarin.Android

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

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

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

  1. ИНТЕРФЕЙСы API .NET (предоставляемые Mono и упакованные Xamarin.Android), включают вспомогательные средства файловой системы, предоставляемые Xamarin.Essentials. API .NET обеспечивают лучшую совместимость с различными платформами, поэтому в данной статье мы сосредоточимся на этих API.
  2. Собственные API доступа к файлам Java (предоставляемые Java и оболочки Xamarin.Android) — Java предоставляет собственные API для чтения и записи файлов. Это полностью приемлемая альтернатива API .NET, но они относятся только к Android и не подходят для кросс-платформенных приложений.

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

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

Внутреннее и внешнее хранилище

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

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

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

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

Примечание.

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

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

В этом руководстве основное внимание уделяется внутреннему хранилищу. Дополнительные сведения об использовании внешнего хранилища в приложении Xamarin.Android см. в разделе Внешнее хранилище.

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

Каталог внутреннего хранилища для приложения определяется операционной системой и предоставляется приложениям Android с помощью свойства Android.Content.Context.FilesDir. Будет возвращен объект Java.IO.File, представляющий каталог, выделенный Android исключительно для приложения. Например, для приложения с именем пакета com.companyname каталогом внутреннего хранилища может быть:

/data/user/0/com.companyname/files

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

Внимание

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

Чтобы максимально увеличить общий доступ к коду, приложения Xamarin.Android (или приложения Xamarin.Forms для Xamarin.Android) должны использовать метод System.Environment.GetFolderPath(). В Xamarin.Android этот метод возвращает строку для каталога, который находится в том же расположении, что и Android.Content.Context.FilesDir. Этот метод принимает перечисление, System.Environment.SpecialFolder, которое используется для определения набора перечислимых констант, представляющих пути специальных папок, используемых операционной системой. Не все значения System.Environment.SpecialFolder будут сопоставляться с допустимым каталогом в Xamarin.Android. В следующей таблице описано, каким может быть путь для заданного значения System.Environment.SpecialFolder:

System.Environment.SpecialFolder Путь
ApplicationData INTERNAL_STORAGE/.config
Desktop INTERNAL_STORAGE/Desktop
LocalApplicationData INTERNAL_STORAGE/.local/share
MyDocuments INTERNAL_STORAGE
MyMusic INTERNAL_STORAGE/музыка
MyPictures INTERNAL_STORAGE/Рисунки
MyVideos INTERNAL_STORAGE/видео
Personal INTERNAL_STORAGE
Fonts INTERNAL_STORAGE/.fonts
Templates INTERNAL_STORAGE/Шаблоны
CommonApplicationData /usr/share
CommonApplicationData /usr/share

Чтение или запись в файлы во внутреннем хранилище

Можно использовать любой API C# для записи в файл. Достаточно просто получить путь к файлу, который находится в каталоге, выделенном для приложения. Настоятельно рекомендуется использовать асинхронные версии API .NET, чтобы возникало меньше проблем с блокировкой основного потока в связи с доступом к файлу.

Этот фрагмент кода представляет один из примеров записи целого числа в текстовый файл в кодировке UTF-8 в каталоге внутреннего хранилища приложения:

public async Task SaveCountAsync(int count)
{
    var backingFile = Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal), "count.txt");
    using (var writer = File.CreateText(backingFile))
    {
        await writer.WriteLineAsync(count.ToString());
    }
}

Следующий фрагмент кода предоставляет один из способов чтения целочисленного значения, хранящегося в текстовом файле:

public async Task<int> ReadCountAsync()
{
    var backingFile = Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal), "count.txt");

    if (backingFile == null || !File.Exists(backingFile))
    {
        return 0;
    }

    var count = 0;
    using (var reader = new StreamReader(backingFile, true))
    {
        string line;
        while ((line = await reader.ReadLineAsync()) != null)
        {
            if (int.TryParse(line, out var newcount))
            {
                count = newcount;
            }
        }
    }

    return count;
}

Использование Xamarin.Essentials — вспомогательные службы файловой системы

Xamarin.Essentials — это набор API для написания кода, совместимого с разными платформами. Вспомогательные функции файловой системы — это класс, который содержит ряд вспомогательных функций для упрощения поиска каталогов кэша и данных приложения. В этом фрагменте кода приведен пример того, как найти каталог внутреннего хранилища и каталог кэша для приложения:

// Get the path to a file on internal storage
var backingFile = Path.Combine(Xamarin.Essentials.FileSystem.AppDataDirectory, "count.txt");

// Get the path to a file in the cache directory
var cacheFile = Path.Combine(Xamarin.Essentials.FileSystem.CacheDirectory, "count.txt");

Скрытие файлов из MediaStore

MediaStore — это компонент Android, который собирает метаданные о файлах мультимедиа (видео, музыка, изображения) на устройстве Android. Его цель — упростить совместное использование этих файлов во всех приложениях Android на устройстве.

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

Общедоступные файлы будут доступны для MediaStore. Каталоги, имеющие имя файла нулевого размера .NOMEDIA, не будут проверяться средством MediaStore.