As our first contact with the Libgdx filesystem, we will learn about the types of files available to us. We will also show you how to read from and write to both binary and text files. Moreover, this recipe will show you how to extract information from entries such as whether it is a file or a directory as well as its size, name, and extension. Finally, you will also discover how to traverse a directory tree hierarchy.
This will settle the grounds for you to include features such as save game states and configuration files in your projects.
Once again, make sure the sample projects are in your Eclipse workspace before continuing.
Jumping straight into the topic at hand, everything explained throughout this recipe is illustrated in the FileHandlingSample
class within the samples/core
project.
In order for us to work with a file in Libgdx, we need a FileHandle
instance that points to said file or folder. This class takes care of all the platform-specific details, offering us a clean, simple API to carry out a wide set of operations.
To obtain an appropriate FileHandle
, we can call getFileHandle()
, which is a static method in the Files
interface. Such an interface is accessible through Gdx.files
:
FileHandle getFileHandle(java.lang.String path, Files.FileType type)
FileType
is an enum
listing the different kinds of files available in Libgdx. All of them will be covered in detail in this recipe:
public enum FileType { Absolute, Classpath, External, Internal, Local};
The Files
interface also provides convenient methods to retrieve file handles:
FileHandle handle = Gdx.files.absolute("test.txt"); FileHandle handle = Gdx.files.classpath("test.txt"); FileHandle handle = Gdx.files.external("test.txt"); FileHandle handle = Gdx.files.internal("test.txt"); FileHandle handle = Gdx.files.local("test.txt");
The FileHandle
class provides a few methods to easily retrieve information from the files they point to:
handle.name()
: This returns the name of the file, including its extension but excluding the path to it, for example, sfx_01.wav
handle.nameWithoutExtension()
: This returns the name of the file with neither the extension nor the path to the parent folder, for example, sfx_01
handle.extension()
: This returns the extension of the file without the dot, for example, wav
handle.path()
: This returns the path to the file, for example, data/sfx/sfx_01.wav
handle.pathWithoutExtension()
: The same as the preceding method excluding the extension, for example, data/sfx/sfx_01
handle.lastModified()
: This returns the date and time the file was modified for the last time, in milliseconds, since epochhandle.length()
: This returns the size of the file in byteshandle.isDirectory()
: This indicates whether the handle points to a directory or a plain fileThe FileHandle
class provides a list()
method that returns an array of FileHandle
instances containing its children. Using this, we can walk a tree hierarchy with a very simple recursive function. If the handle points to a regular file and not a directory, list()
will return an empty array:
private void traverseTree(FileHandle handle) { doSomething(handle); for (FileHandle child : handle.list()) { traverseTree(child); } }
Listing a directory is not supported in desktop platforms; take this into consideration when developing your games. It works during development when running from your IDE. However, it will not work from a packed .jar
file. A quick-and-dirty solution will be to call and parse the output of commands, such as ls
or dir
.
File writing can be achieved through any of the write()
method variants the FileHandle
class has; we will only cover a couple of them. Creating a new file and adding a string to it is as easy as is shown in the following code. The second parameter indicates whether to append at the end or write at the beginning (truncating the file):
Gdx.files.external("test.txt").writeString("This is a test file", false);
On the other hand, you might want to treat the file as binary and write a stream of bytes to it:
byte[] bytes = new byte[] {'T', 'e', 's', 't'}; Gdx.files.external("test").writeBytes(bytes, false);
The same options to read from a file are also available. We can put all the contents of a file on a String
object with the following snippet:
String string = Gdx.files.external("test.txt").readString();
We can also get the whole content of the file as an array of bytes by executing this:
String bytes = Gdx.files.external("test.txt").readBytes();
Check out the official API documentation for a comprehensive list of mechanisms for file reading and writing, available at http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/files/FileHandle.html.
Libgdx offers additional file and directory manipulation through the following FileHandle
functions.
To copy a file or folder, use copyTo()
to pass the handle of the destination handle:
void copyTo(FileHandle dest)
You can erase files or directories with delete()
. The operation will fail if the handle points to a non-empty directory or a location where the user cannot delete files. If you want to delete a directory and its contents recursively, call the deleteDirectory()
method instead:
boolean delete() boolean deleteDirectory()
Lastly, you might want to erase the contents of a directory without removing the directory itself. Use the emptyDirectory()
function in this case. The preserveTree
parameter tells the handle whether to delete recursively, or delete only the files at this level:
void emptyDirectory(boolean preserveTree)
We mentioned the different file types briefly earlier in this recipe; it is really important to understand their differences and quirks because there are many platform-specific details to take into account. Here is a more detailed description of each one of them:
assets
folder on Android, and the war/assets
directory in HTML projects. It's available on all platforms, but is also only read only. This is where we typically store all our game files, such as audio, graphics, and gameplay data.As we just mentioned, some kinds of files are not available on all platforms. To check for this, you can make use of the following methods. Keep in mind that it's best not to use absolute or classpath files:
Gdx.files.isExternalStorageAvailable(); Gdx.files.isLocalStorageAvailable();
As you have seen already, the Files
interface is quite powerful. Here are a few quick extra things you can do with it.
We often need disposable files with unique names for certain operations so that they do not collide with other files in the system. We can create a temporary directory or file in the FileHandle
class with the following static methods. The parameter is used as the prefix for the name:
FileHandle tempDir = FileHandle.tempDirectory("temp"); FileHandle tempFile = FileHandle.tempFile("temp");
Another common situation is to iterate over the files in a directory, but only be interested in files that match a certain criteria. Rather than adding an if
condition every time you traverse a tree, why not just pass a FileFilter
or FileNameFilter
instance to the list()
function? This approach is cleaner and avoids code duplication.
FileFilter
and FileNameFilter
are interfaces in the Java standard library. Provide your own implementation with the required criteria in each case:
public interface FileFilter { boolean accept(File pathname); } public interface FileNameFilter { boolean accept(File dir, String name); }
Libgdx supports streaming for large files. Take a look at the read()
and write()
method overloads that take or return streams at http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/files/FileHandle.html.
18.191.54.245