Файлы

libGDX приложения работают на различных платформах: Desktop системах (Windows, Linux, Mac OS X), Android и на JavaScript/WebGL возможных браузерах. Каждая из этих платформ работает с файлами немного по-разному.

В libGDX Files (код) модуль предоставляет следующие возможности:

  • Чтение из файла
  • Запись в файл
  • Копирование файла
  • Перемещение файла
  • Удаление файла
  • Выдача списка файлов и директорий
  • Проверка существования файла/директории

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

Файловые системы платформ

Здесь мы рассмотрим обзор парадигм файловых систем платформ, поддерживающихся в libGDX.

Desktop (Windows, Linux, Mac OS X)

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

Android

На Android ситуация немного сложнее. Файлы можно хранить внутри APK приложения, либо как ресурсы или asset. Эти файлы доступны только для чтения. libGDX использует только asset механизм, так как он предоставляет прямой доступ к байтовым потокам и более напоминает традиционную файловую систему. Ресурсы отлично предоставляются для обычных Android приложений, но вызывают проблемы при использовании в играх. Android совершает манипуляции во время их загрузки, например, автоматически изменяет размер.

Asset хранятся в assets директории Android проекта и затем автоматически упаковываются в ваш APK файл при развертывании приложения. Ни одно другое приложение не может получить доступ к этим файлам.

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

Наконец, файлы можно хранить на внешнем накопителе данных, таком как SD карта. Внешние накопители не всегда могут быть доступны, например пользователь может вытащить SD карту. Файлы в этом месте хранения следует считать не всегда доступными. Чтобы иметь возможность записи на внешние накопители, вам нужно будет добавить разрешения в AndroidManifest.xml файл, смотрите разрешения ниже.

iOS

На iOS доступны все типы файлов.

Javascript/WebGL

Обычное Javascript/WebGL приложение не имеет традиционной концепции файловой системы. Вместо этого, assets подобны изображениям, ссылающимся на URL адреса, которые указывают на конкретные фалы одного или нескольких серверов. Современные браузеры поддерживают локальное хранилище данных, которое приближается к традиционным файловым системам с чтением и записью.

libGDX предоставляет абстракцию файловой системы, доступной только для чтения.

Типы (хранения) файлов

Файл в libGDX представлен экземпляром FileHandle класса. FileHandle имеет тип, который определяет где находится файл. Следующая таблица иллюстрирует наличие и местоположение каждого типа файла для каждой платформы.

Тип Описание, путь файла и особенности Desktop Android HTML5 iOS
Classpath Classpath файлы хранятся непосредственно в директории исходников. Они упакованы вместе с jar файлами и всегда доступны только для чтения. Они имеют свое назначение, но по возможности нужно избегать их использования. Да Да Нет Нет
Внутренний Внутренние файлы относятся к корневой или рабочей директории приложения на Desktop, к assets директории на Android, и к war/assets/ директории GWT проекта. Эти файлы доступны только для чтения. Если файл не может быть найдет во внутреннем хранилище, то файловый модуль возвращается к поиску файла в classpath. Это необходимо, если в Eclipse используется механизм ссылки на asset директорию, смотрите настройку проекта. Да Да Да Да
Локальный Локальные файлы хранятся относительно корневой или рабочей директории на Desktop и относятся к внутреннему (частному) хранилищу приложений на Android. Обратите внимание, что локальный и внутренний тип в основном одно и то же для Dekstop. Да Да Нет Да
Внешний Пути к внешним файлом являются относительными корня SD карты на Android и домашней директории текущего пользователя на Desktop системах. Да Да Нет Да
Абсолютный Абсолютные файлы должны иметь полностью указанный путь.
Примечание: Ради портативности, эта опция должна использоваться только в случае крайней необходимости
Да Да Нет Да

Абсолютные и classpath файлы в основном используются для таких инструментов, как редакторы на Desktop, которые имеют более сложные требования файлового ввода/вывода. Для игр их можно спокойно игнорировать. Порядок, в котором вы должны использовать типы файлов выглядит следующим образом:

  • Внутренние файлы: все assets (изображения, аудио файлы и так далее), которые упакованные вместе с приложением являются внутренними файлами. Если вы используете Setup UI, просто положите их в assets директорию Android проекта.
  • Локальные файлы: Если необходимо записывать небольшие файлы, например, сохранить состояние игры, используйте локальные файлы. В общем, они имеют частный доступ для вашего приложения. Если вы хотите использовать ключ/значение хранилище, то вам следует посмотреть настройки предпочтения.
  • Внешние файлы: Если необходимо записывать большие файлы, например снимки экрана или скачивать файлы из интернета, то следует использовать внешние накопители. Обратите внимание, что внешний накопитель является не всегда доступным, пользователь может вынуть его или удалить файлы, которые вы записали.

Проверка наличия хранилища и путей

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

boolean isExtAvailable = Gdx.files.isExternalStorageAvailable();
boolean isLocAvailable = Gdx.files.isLocalStorageAvailable();

Можно также запросить корневой путь для внешнего и локального хранилища:

String extRoot = Gdx.files.getExternalStoragePath();
String locRoot = Gdx.files.getLocalStoragePath();

Получение дескриптора файла (FileHandles)

Получение FileHandle используется одним из вышеупомянутых типов напрямую из Files модуля. Следующий код получает дескриптор для внутреннего myfile.txt файла.

FileHandle handle = Gdx.files.internal("data/myfile.txt");

Если вы использовали Setup UI, то этот файл будет содержаться в asset директории Android проекта, если точно, то в $ANDROID_PROJECT/assets/data. Ваши Desktop и HTML проекты в Eclipse ссылаются на эту директорию, и автоматически имеют к ней доступ.

FileHandle handle = Gdx.files.classpath("myfile.txt");

Файл "Myfile.txt" находится в каталоге, там же где скомпилированные классы или включенные jar файлы.

FileHandle handle = Gdx.files.external("myfile.txt");

В этом случае необходимо чтобы myfile.txt файл был в домашней директории пользователя (/home/<пользователь>/myfile.txt на Linux или \Users\<пользователь>\myfile.txt на Windows и Mac OS) на Desktop и в корне SD-карты на Android.

FileHandle handle = Gdx.files.absolute("/some_dir/subdir/myfile.txt");

В случае абсолютного дескриптора файла, файл должен быть именно там, куда указывает путь. В /некоторая_директория/поддиректория/ текущего диска в Windows или точный путь для Linux, Mac OS и Android.

Экземпляры FileHandle передаются методам соответствующих классов отвечающих за чтение и запись данных. Например, нужно указать FileHandle для загрузки изображение с помощью Texture класса или для загрузки аудио файла с помощью Audio модуля.

Список файлов и проверка свойств

Иногда необходимо проверить конкретный файл на его наличие или просмотреть содержимое директории. FileHandle предоставляет методы, чтобы сделать этот в простой форме.

Вот пример, который проверяет существование конкретного файла и проверяет, является ли он директорией или нет:

boolean exists = Gdx.files.external("doitexist.txt").exists();
boolean isDirectory = Gdx.files.external("test/").isDirectory();

Просмотр списка директории столь же прост:

FileHandle[] files = Gdx.files.local("mylocaldir/").list();
for(FileHandle file: files) {
   // делаем что-нибудь интересное с файлом
}
Просмотр внутренних каталогов

Просмотр списка внутренних каталогов не поддерживается на Desktop.

Можно также запросить родительскую директорию файла или создать FileHandle для файла в родительской директории.

FileHandle parent = Gdx.files.internal("data/graphics/myimage.png").parent();
FileHandle child = Gdx.files.internal("data/sounds/").child("myaudiofile.mp3");

parent будет указывать на data/graphics/, child будет указывать на data/sounds/myaudiofile.mp3.

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

В HTML5 функции в основном нереализованные

На текущий момент функции в основном нереализованные на HTML5 back-end. Постарайтесь не полагаться на них, если целевым приложением будет HTML5.

Обработка ошибок

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

Чтение из файла

После получения FileHandle, мы можем либо передать его в класс, который знает, как загружать содержимое из файла (например, изображение) либо читать его сами. Последнее делается через любой из методов ввода в FileHandle классе. В следующем примере показано, как загрузить текст из внутреннего файла:

FileHandle file = Gdx.files.internal("myfile.txt");
String text = file.readString();

Если у вас есть двоичные данные, вы можете легко загрузить файл в байтовый массив:

FileHandle file = Gdx.files.internal("myblob.bin");
byte[] bytes = file.readBytes();

Класс FileHandle имеет гораздо больше методов чтения. Смотрите дополнительные сведения в Javadoc.

Запись в файл

Как и для чтения файлов, FileHandle предоставляет методы для записи в файл. Заметьте, что только локальные, внешние и абсолютные типы файлов поддерживаются для записи в файл. Запись строки в файл работает следующим образом:

FileHandle file = Gdx.files.local("myfile.txt");
file.writeString("My god, it's full of stars", false);

Второй параметр метода FileHandle.WriteString() указывает, должно ли содержимое быть добавлено к файлу. Если установлено значение false, то текущее содержимое файла будут перезаписано.

Можно, конечно и записывать двоичные данные в файл:

FileHandle file = Gdx.files.local("myblob.bin");
file.writeBytes(new byte[] { 20, 3, -2, 10 }, false);

Есть много других методов в FileHandle, которые облегчают запись различными способами, например использование OutputStream. Смотрите дополнительные сведения в Javadoc.

Удаление, копирование, переименование и перемещение файлов/директорий

Эти операции возможны только для типов файлов доступных для записи (локальных, внешних и абсолютных). Однако заметьте, что источником операции копирования может быть FileHandle доступный только для чтения. Несколько примеров:

FileHandle from = Gdx.files.internal("myresource.txt");
from.copyTo(Gdx.files.external("myexternalcopy.txt");

Gdx.files.external("myexternalcopy.txt").rename("mycopy.txt");
Gdx.files.external("mycopy.txt").moveTo(Gdx.files.local("mylocalcopy.txt"));

Gdx.files.local("mylocalcopy.txt").delete();

Обратите внимание, что источником и приемником могут быть файлы или директории.

Более подробную информацию о доступных методах смотрите в FileHandle Javadoc.