Select internal or external storage

All Android devices have two file stores: “internal” and “external” storage. These names come from the early days of Android, when most devices offered built-in non-volatile memory (internal storage), as well as removable storage media such as microSD cards (external storage). Many devices now divide permanent storage into separate “internal” and “external” partitions. Thus, even if there is no removable storage medium, the two storage Spaces always exist, and the API behavior is the same whether the external storage is removable or not.

Since external storage can be removable, there are some differences between the two options, as shown below.

Internal memory:

  • It’s always available.
  • Internal storage holds files that can only be accessed by your application.
  • When a user uninstalls your application, the system deletes all of the application’s files from internal storage.
  • Internal storage is best used when you want to ensure that neither users nor other applications can access your files.

External storage:

  • External storage is not always available because users can load external storage as USB storage and in some cases remove it from the device.
  • External storage is globally readable, so files saved here may be read outside of your control.
  • When a user uninstalls your application, only the application’s files are saved through usegetExternalFilesDir()The system will delete the application’s files from this directory.
  • External storage is the best place for files that do not require access restrictions and files that are to be shared with other applications or accessible to users using a computer.

Save files on internal storage

Your application’s internal storage directory is specified by your application package name in a special location in the Android file system, which can be accessed using the following API.

Note: Unlike external storage directories, your application does not need any system permissions to read and write to the internal directories returned by these methods.

Write a file

To save the File to internal storage, you can call the following two methods to obtain the corresponding directory and further operate on File:

  • getFilesDir(): Returns File representing the internal directory of the application.
  • getCacheDir(): returns File representing the internal directory of the application’s temporary cache files.

To create a new File in one of these directories, you use the File() constructor, which you pass to the method File provided above that specifies the internal storage directory.

Such as:

File file = new File(context.getFilesDir(), filename);
Copy the code

Alternatively, you can call openFileOutput() to get what FileOutputStream writes to a file in an internal directory.

For example, here’s how to write some text to a file:

String filename = "myfile";
String fileContents = "Hello world!";
FileOutputStream outputStream;

try {
    outputStream = openFileOutput(filename, Context.MODE_PRIVATE);
    outputStream.write(fileContents.getBytes());
    outputStream.close();
} catch (Exception e) {
    e.printStackTrace();
}
Copy the code

MODE_PRIVATE will create a file (or replace a file with the same name) and make it the application’s private file. Other available modes include MODE_APPEND, MODE_WORLD_READABLE, and MODE_WORLD_WRITEABLE.

Note that the openFileOutput() method requires a file mode parameter. Passing MODE_PRIVATE creates a file (or replaces a file with the same name) and sets it to the application’s private file. Other available modes include MODE_APPEND, MODE_WORLD_READABLE, and MODE_WORLD_WRITEABLE.

The constants MODE_WORLD_READABLE and MODE_WORLD_WRITEABLE have been deprecated since API level 17. Starting with Android N(7.0,API24), using these constants will cause a SecurityException to be raised. This means that apps for Android N and later cannot share private files by name, and attempting to share the “file://” URI will cause a FileUriExposedException. If your application needs to share private files with other applications, you can use FileProvider in conjunction with FLAG_GRANT_READ_URI_PERMISSION

On Android 6.0 (API level 23) and below, if you set file mode to globally readable, other applications can read your internal files. However, other applications must know your package name and file name. Unless you explicitly make files readable or writable, other applications cannot browse your internal directories and do not have read or write permissions · Therefore, as long as you mark your files with MODE_PRIVATE on internal storage, other applications will never be able to access them.

Write a cache file

If you need to cache some files, you should use createTempFile().

For example, the following method extracts the file name from the URL and creates a file with that name in the application’s internal cache directory:

private File getTempFile(Context context, String url) {
    File file;
    try {
        String fileName = Uri.parse(url).getLastPathSegment();
        file = File.createTempFile(fileName, null, context.getCacheDir());
    } catch (IOException e) {
        // Error while creating file
    }
    return file;
}
Copy the code

Files created with createTempFile() are placed in an application-specific cache directory. You should periodically delete files that are no longer needed.

Note: Cache files may be deleted without warning if the system is running low on storage space, so be sure to check for their existence before reading.

Open an existing file

To read an existing file, call openFileInput(name), passing the filename.

You can call an array fileList() that gets all application file names.

Note: If you need to package a file in your application that can be accessed at installation time, save this file in the res/ RAW/directory of your project. You can open these files using openRawResource(), passing the resource ID. This method returns a method that can be used to read the file. You cannot write to the original file.

Open a directory

You can open a directory on an internal file system using the following methods:

  • getFilesDir(): Returns File representing the directory on the File system that is uniquely associated with your application.
  • getDir(name, mode): Creates a new directory (or opens an existing directory) in the application’s unique file system directory. This new directory is displayed in the supplied directorygetFilesDir().
  • getCacheDir(): Returns File representing the cache directory on the File system uniquely associated with your application. This directory is for temporary files and should be cleaned regularly. If the disk space is insufficient, the system may delete the files there, so be sure to check if the cache file exists before reading.

To create a new File in one of these directories, you can use the File() constructor to pass an object that specifies the internal storage directory provided by one of the File methods.

Such as:

File directory = context.getFilesDir();
File file = new File(directory, filename);
Copy the code

Save the file on external storage

Using external storage is ideal for files that you want to share with other applications or allow users to access using your computer.

After requesting storage permission and verifying that the storage is available, you can save two different types of files:

  • Common file: files that should be freely available to other applications and users. These files should still be available to users when they uninstall your application. For example, photos captured by an application or other downloaded file should be saved as public files.
  • Personal files: Files that legally belong to your application will be deleted when a user uninstalls your application. Although these files are technically accessible by users and other applications because they reside on external storage, they do not provide value to users outside the application.

Note: External storage may become unavailable if the user removes the SD card or connects the device to a computer. These files are still visible to users and other applications with the READ_EXTERNAL_STORAGE permission. Therefore, if the functionality of your application depends on these files, or if you need to restrict access completely, you should write the files to internal storage.

Request external storage permission

To write to the public external storage, you must request the WRITE_EXTERNAL_STORAGE permission in the manifest file:

<uses-permission android: name = "android.permission.WRITE_EXTERNAL_STORAGE" />Copy the code

If your application uses this WRITE_EXTERNAL_STORAGE permission, it also implicitly has permission to read external storage.

If your application only needs to read external storage (but not write to it), you need to declare the READ_EXTERNAL_STORAGE permission:

<uses-permission android: name = "android.permission.READ_EXTERNAL_STORAGE" />Copy the code

Starting with Android 4.4 (API level 19), Read or write files to the application’s private external storage directory – accessed using getExternalFilesDir(), without READ_EXTERNAL_STORAGE or WRITE_EXTERNAL_STORAGE permissions. Therefore, if your application supports Android 4.3 (API level 18) or lower, and you only want to access dedicated external storage directories, you should declare that you will only request permissions on lower versions of Android by adding the maxSdkVersion attribute:

<uses-permission android: name = "android.permission.WRITE_EXTERNAL_STORAGE" Android: maxSdkVersion = "18" />Copy the code

Verify that the external storage is available

Because external storage may not be available – for example when a user installs a storage device on a PC or has removed an SD card that provides external storage – you should always verify that the volume is available before accessing it. You can query the state of the external storage by calling getExternalStorageState(). If the return status is MEDIA_MOUNTED, the file can be read and written. If MEDIA_MOUNTED_READ_ONLY is used, only files can be read.

For example, the following methods can be used to determine storage availability:

/* 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;
}
Copy the code

Save to an external public directory

If you would have a common File stored in the external storage, please use the getExternalStoragePublicDirectory () method to obtain the File said corresponding directory on the external storage. This method takes an argument specifying the type of file to save so that it can be logically organized DIRECTORY_PICTURES using other public files, such as DIRECTORY_MUSIC or DIRECTORY_PICTURES. Such as:

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

To hide files from Media Scanner, include an empty file named.nomedia in the external file directory (note the dot prefix in the file name). This prevents a media scanner from reading your media files and making them available to other applications through the MediaStore content provider.

Save to an external private directory

If you want to keep files on MediaStore on external storage that is application-specific and not accessible to external providers, you can get a directory that is used only by your application by calling getExternalFilesDir() and passing it a name that indicates the type of directory you want. Each directory created this way is added to the parent directory, which encapsulates all of the application’s external storage files and is removed by the system when the user uninstalls the application.

public File getPrivateAlbumStorageDir(Context context, String albumName) {
    // Get the directory for the app's private pictures directory. File file = new File(context.getExternalFilesDir( Environment.DIRECTORY_PICTURES), albumName); if (! file.mkdirs()) { Log.e(LOG_TAG, "Directory not created"); } return file; }Copy the code

If no predefined subdirectory name is appropriate for your file, call getExternalFilesDir() and pass NULL. This returns the root of the application’s private directory on external storage.

Remember that getExternalFilesDir() creates a directory that is deleted when the user uninstalls the application. If the files you saved are still available after the user uninstalls the application – for example, when your application captured photos and the user should keep them – you should save the files to a public directory.

Choose between multiple storage locations

Sometimes devices that allocate internal memory partitions for use as external memory also provide SD card slots. This means that the device has two different external storage directories, so you need to choose which directory to use when writing “private” files to the external storage.

Starting with Android 4.4 (API level 19), you can access both locations through a call to getExternalFilesDirs(), which returns a File array containing entries for each storage location. The first entry in the array is considered the primary external storage, and you should use that location unless it is full or unavailable.

If your application support Android 4.3 and lower version, you should use the support library static methods ContextCompat. GetExternalFilesDirs (). This always returns an array of files, but if the device is running Android 4.3 or lower, it contains only one main external storage entry (if there is a second storage location, it cannot be accessed on Android 4.3 or lower).

Delete the file

You should always delete files that your application no longer needs. The most direct way to delete a File is to call the File object delete() method.

myFile.delete();
Copy the code

If the file is stored on internal storage, you can also find and delete the file by calling Context’s deleteFile() :

Mycontext.deletefile (fileName);Copy the code

Note: When a user uninstalls your app, the Android system deletes the following:

  • All files that you save on internal storage.
  • usegetExternalFilesDir()All files saved in external storage
  • However, you should manually delete all cache files that getCacheDir() periodically creates, and periodically delete other files that are no longer needed.

Android Directory Summary

($rootDir)
+- /data                -> Environment.getDataDirectory()
|   |
|   |   ($appDataDir)
|   +- data/com.srain.cube.sample
|       |
|       |   ($filesDir)
|       +- files            -> Context.getFilesDir() / Context.getFileStreamPath("")
|       |       |
|       |       +- file1    -> Context.getFileStreamPath("file1")
|       |   ($cacheDir)
|       +- cache            -> Context.getCacheDir()
|       |
|       +- app_$name        ->(Context.getDir(String name, int mode)
|
|   ($rootDir)
+- /storage/sdcard0     -> Environment.getExternalStorageDirectory()
    |                       / Environment.getExternalStoragePublicDirectory("")
    |
    +- dir1             -> Environment.getExternalStoragePublicDirectory("dir1")
    |
    |   ($appDataDir)
    +- Andorid/data/com.srain.cube.sample
        |
        |   ($filesDir)
        +- files        -> Context.getExternalFilesDir("")
        |   |
        |   +- file1    -> Context.getExternalFilesDir("file1") | +- Music -> Context.getExternalFilesDir(Environment.Music); | +- Picture -> ... Environment.Picture | +- ... | | -$cacheDir)
        +- cache        -> Context.getExternalCacheDir()
        |
        +- ???
Copy the code