Menu

Beginning Android Development Part 7 - File Saving

Saving to internal and external storage
Beginning Android Development Part 7 - File Saving

File Saving

In the previous post we learned how to save data to the SharedPreferences files in Android, which is great for small bits of data. But things are seldom that simple. Sometimes we need to save larger bits of data, or images, or spreadsheets, or any of a hundred other file types. This is when we dive into good old fashion file saving. Android provides us with the File API in order to work with the native Android file system. A File object is suited to reading or writing large amounts of data in start-to-finish order without skipping around. Android has two types of storage means, Internal and External. We normally recognize internal as the non-volatile built in memory that phones have, and external as SD cards and such. Each has it's own pros and cons

Internal storage:
  • It's always available.
  • Files saved here are accessible by only your app by default.
  • When the user uninstalls your app, the system removes all your app's files from internal storage.
  • Internal storage is best when you want to be sure that neither the user nor other apps can access your files.
External storage:
  • It's not always available, because the user can mount the external storage as USB storage and in some cases remove it from the device.
  • It's world-readable, so files saved here may be read outside of your control.
  • When the user uninstalls your app, the system removes your app's files from here only if you save them in the directory from getExternalFilesDir().
  • External storage is the best place for files that don't require access restrictions and for files that you want to share with other apps or allow the user to access with a computer.

Internal Storage

By default our applications have full access to Internal memory without us having to do anything. Also by default, our applications are saved onto Internal storage, which may not be ideal for very large applications. Mobile games nowadays run into the Gigabytes, and filling up your users storage area for 1 application wouldn't make the best of impressions. Lucky for us however we can tell our application where to install itself, whether internal or external.

We can do that by specifying a storage location in our manifest file, with the android:installLocation attribute. The possible values are as follows:

"internalOnly" The application must be installed on the internal device storage only. If this is set, the application will never be installed on the external storage. If the internal storage is full, then the system will not install the application. This is also the default behavior if you do not define android:installLocation.

"auto" The application may be installed on the external storage, but the system will install the application on the internal storage by default. If the internal storage is full, then the system will install it on the external storage. Once installed, the user can move the application to either internal or external storage through the system settings.

"preferExternal" The application prefers to be installed on the external storage (SD card). There is no guarantee that the system will honor this request. The application might be installed on internal storage if the external media is unavailable or full. Once installed, the user can move the application to either internal or external storage through the system settings.

External Storage

External storage requires permission in order to write to it. We'll explicitly request permission to write to External by adding the following to our manifest file.


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

You current don't need permission in order to read from external storage, but this is expected to change in the future. In order to stay compliant and avoid issues later, you can request external read permission now in your manifest.

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

Save To Internal Storage

As previously stated, you don't need any special permissions in order to read/write to internal storage from your applications. When creating a file we have 2 options on the matter:

You can call getFilesDir() to get a File object representing the file in the path you specified. Or, you can also call getCacheDir() to get a File representing a path in your your app's temporary cache files.


File file = new File(getFilesDir(), filename);
File file2 = new File(getCacheDir(), filename);

Save To External Storage

Unlike the internal storage, with this one you will have to check if it is available. External storage can sometimes be unmounted and sometimes it's just not present, so better safe than sorry.

We can check if an external area is safe to write in with the following functions:


/* Checks if external storage is available for read and write */
public boolean isExternalStorageWritable() {
    String state = Environment.getExternalStorageState();
    if (Environment.MEDIA_MOUNTED.equals(state)) {
        return true;
    }
    return false;
}

/* Checks if external storage is available to at least read */
public boolean isExternalStorageReadable() {
    String state = Environment.getExternalStorageState();
    if (Environment.MEDIA_MOUNTED.equals(state) ||
        Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
        return true;
    }
    return false;
}

These are great to call before working with external storage. If they pass the test, then it is time to save our files.


public File getStorageDir(String fileName) {
    // Get the directory for the user's public pictures directory. 
    File file = new File(Environment.getExternalStoragePublicDirectory(
            Environment.DIRECTORY_PICTURES), fileName);
    if (!file.mkdirs()) {
        Log.e(LOG_TAG, "Directory not created");
    }
    return file;
}

Regardless of whether you use getExternalStoragePublicDirectory() for files that are shared or getExternalFilesDir() for files that are private to your app, it's important that you use directory names provided by API constants like DIRECTORY_PICTURES. These directory names ensure that the files are treated properly by the system. For instance, files saved in DIRECTORY_RINGTONES are categorized by the system media scanner as ringtones instead of music.

Still pretty boring stuff. Where's the flapping of the bird and the breaking of the candiez. They are coming soon. I'll be skipping the database portion for now, in order to speed up the development process. Once you get bored, it's tough to get back into a project. So before that happens, up next will be something more interesting.

Walter G. author of blog post
Walter Guevara is a Computer Scientist, software engineer, startup founder and previous mentor for a coding bootcamp. He has been creating software for the past 20 years.

Get the latest programming news directly in your inbox!

Have a question on this article?

You can leave me a question on this particular article (or any other really).

Ask a question

Community Comments

No comments posted yet

Add a comment