Akses file khusus aplikasi

Dalam banyak kasus, aplikasi Anda membuat file yang tidak perlu diakses oleh aplikasi lain, atau tidak boleh diakses. Sistem ini menyediakan lokasi berikut untuk menyimpan file khusus aplikasi:

  • Direktori penyimpanan internal: Direktori ini mencakup lokasi khusus untuk menyimpan file persisten, dan lokasi lain untuk menyimpan data cache. Sistem mencegah aplikasi lain mengakses lokasi tersebut, dan pada Android 10 (level API 29) dan yang lebih tinggi, lokasi ini dienkripsi. Karakteristik ini menjadikan lokasi tersebut tempat yang bagus untuk menyimpan data sensitif yang hanya dapat diakses oleh aplikasi Anda.

  • Direktori penyimpanan eksternal: Direktori ini mencakup lokasi khusus untuk menyimpan file tetap, dan lokasi lain untuk menyimpan data cache. Meskipun aplikasi lain dapat mengakses direktori ini jika aplikasi tersebut memiliki izin yang tepat, file yang disimpan dalam direktori ini dimaksudkan hanya untuk digunakan oleh aplikasi Anda. Jika Anda secara khusus ingin membuat file yang dapat diakses aplikasi lain, aplikasi Anda harus menyimpan file ini di penyimpanan bersama yang merupakan bagian dari penyimpanan eksternal.

Saat pengguna meng-uninstal aplikasi Anda, file yang disimpan di penyimpanan khusus aplikasi akan dihapus. Karena perilaku ini, Anda tidak boleh menggunakan penyimpanan ini untuk menyimpan apa pun yang pengguna harapkan akan tetap dipertahankan secara independen dari aplikasi Anda. Misalnya, jika aplikasi Anda memungkinkan pengguna mengambil foto, pengguna akan berharap bahwa mereka dapat mengakses foto tersebut bahkan setelah mereka meng-uninstal aplikasi Anda. Jadi, sebaiknya Anda menggunakan penyimpanan bersama untuk menyimpan jenis file tersebut ke koleksi media yang sesuai.

Bagian berikut menjelaskan cara menyimpan dan mengakses file dalam direktori khusus aplikasi.

Mengakses dari penyimpanan internal

Untuk setiap aplikasi, sistem menyediakan direktori dalam penyimpanan internal tempat aplikasi dapat mengatur filenya. Satu direktori didesain untuk file persisten aplikasi Anda, dan direktori lainnya berisi file cache aplikasi Anda. Aplikasi Anda tidak memerlukan izin sistem apa pun untuk membaca dan menulis ke file dalam direktori ini.

Aplikasi lain tidak dapat mengakses file yang disimpan dalam penyimpanan internal. Hal ini menjadikan penyimpanan internal sebagai tempat yang bagus untuk data aplikasi yang tidak dapat diakses oleh aplikasi lain.

Namun, perlu diingat bahwa direktori ini cenderung berukuran kecil. Sebelum menulis file khusus aplikasi ke penyimpanan internal, aplikasi Anda harus meminta ruang kosong di perangkat.

Mengakses file persisten

File persisten biasa aplikasi Anda berada di direktori yang dapat Anda akses menggunakan properti filesDir objek konteks. Framework ini menyediakan beberapa metode untuk membantu Anda mengakses dan menyimpan file di direktori ini.

Mengakses dan menyimpan file

Anda dapat menggunakan File API untuk mengakses dan menyimpan file.

Untuk membantu mempertahankan performa aplikasi Anda, jangan membuka dan menutup file yang sama beberapa kali.

Cuplikan kode berikut menunjukkan cara menggunakan API File:

Kotlin

val file = File(context.filesDir, filename)

Java

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

Menyimpan file menggunakan stream

Sebagai alternatif dari menggunakan File API, Anda dapat memanggil openFileOutput() untuk mendapatkan FileOutputStream yang menulis ke file dalam direktori filesDir.

Cuplikan kode berikut menunjukkan cara menulis teks ke file:

Kotlin

val filename = "myfile"
val fileContents = "Hello world!"
context.openFileOutput(filename, Context.MODE_PRIVATE).use {
        it.write(fileContents.toByteArray())
}

Java

String filename = "myfile";
String fileContents = "Hello world!";
try (FileOutputStream fos = context.openFileOutput(filename, Context.MODE_PRIVATE)) {
    fos.write(fileContents.toByteArray());
}

Untuk mengizinkan aplikasi lain mengakses file yang tersimpan di direktori ini dalam penyimpanan internal, gunakan FileProvider dengan atribut FLAG_GRANT_READ_URI_PERMISSION.

Mengakses file menggunakan stream

Untuk membaca file sebagai stream, gunakan openFileInput():

Kotlin

context.openFileInput(filename).bufferedReader().useLines { lines ->
    lines.fold("") { some, text ->
        "$some\n$text"
    }
}

Java

FileInputStream fis = context.openFileInput(filename);
InputStreamReader inputStreamReader =
        new InputStreamReader(fis, StandardCharsets.UTF_8);
StringBuilder stringBuilder = new StringBuilder();
try (BufferedReader reader = new BufferedReader(inputStreamReader)) {
    String line = reader.readLine();
    while (line != null) {
        stringBuilder.append(line).append('\n');
        line = reader.readLine();
    }
} catch (IOException e) {
    // Error occurred when opening raw file for reading.
} finally {
    String contents = stringBuilder.toString();
}

Melihat daftar file

Anda bisa mendapatkan array yang berisi nama semua file dalam direktori filesDir dengan memanggil fileList(), seperti yang ditunjukkan dalam cuplikan kode berikut:

Kotlin

var files: Array<String> = context.fileList()

Java

Array<String> files = context.fileList();

Membuat direktori bertingkat

Anda juga dapat membuat direktori bertingkat, atau membuka direktori dalam, dengan memanggil getDir() dalam kode berbasis Kotlin atau dengan meneruskan direktori utama dan nama direktori baru ke konstruktor File dalam kode berbasis Java:

Kotlin

context.getDir(dirName, Context.MODE_PRIVATE)

Java

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

Membuat file cache

Jika Anda perlu menyimpan data sensitif untuk sementara saja, Anda harus menggunakan direktori cache yang ditetapkan aplikasi dalam penyimpanan internal untuk menyimpan data. Seperti halnya untuk semua penyimpanan khusus aplikasi, file yang disimpan dalam direktori ini akan dihapus saat pengguna meng-uninstal aplikasi Anda, meskipun file dalam direktori ini mungkin akan dihapus lebih awal.

Untuk membuat file cache, panggil File.createTempFile():

Kotlin

File.createTempFile(filename, null, context.cacheDir)

Java

File.createTempFile(filename, null, context.getCacheDir());

Aplikasi Anda mengakses file dalam direktori ini menggunakan properti cacheDir objek konteks dan File API:

Kotlin

val cacheFile = File(context.cacheDir, filename)

Java

File cacheFile = new File(context.getCacheDir(), filename);

Menghapus file cache

Meskipun Android terkadang menghapus file cache dengan sendirinya, sebaiknya jangan mengandalkan sistem untuk membersihkan file ini untuk Anda. Anda harus selalu mempertahankan file cache aplikasi dalam penyimpanan internal.

Untuk menghapus file dari direktori cache dalam penyimpanan internal, gunakan salah satu metode berikut:

  • Metode delete() pada objek File yang mewakili file:

    Kotlin

    cacheFile.delete()

    Java

    cacheFile.delete();
  • Metode deleteFile() konteks aplikasi, yang meneruskan nama file:

    Kotlin

    context.deleteFile(cacheFileName)

    Java

    context.deleteFile(cacheFileName);

Mengakses dari penyimpanan eksternal

Jika penyimpanan internal tidak menyediakan cukup ruang untuk menyimpan file khusus aplikasi, pertimbangkan untuk menggunakan penyimpanan eksternal. Sistem ini menyediakan direktori dalam penyimpanan eksternal tempat aplikasi dapat mengelola file yang hanya memberikan nilai kepada pengguna dalam aplikasi Anda. Satu direktori didesain untuk file persisten aplikasi Anda, dan direktori lainnya berisi file cache aplikasi Anda.

Pada Android 4.4 (level API 19) atau yang lebih tinggi, aplikasi Anda tidak perlu meminta izin terkait penyimpanan untuk mengakses direktori khusus aplikasi dalam penyimpanan eksternal. File yang disimpan di direktori ini akan dihapus saat aplikasi Anda di-uninstal.

Pada perangkat yang menjalankan Android 9 (level API 28) atau yang lebih rendah, aplikasi Anda dapat mengakses file khusus aplikasi milik aplikasi lain, asalkan aplikasi Anda memiliki izin penyimpanan yang sesuai. Untuk memberi pengguna kontrol yang lebih besar atas file mereka dan untuk membatasi ketidakrapian file, aplikasi yang menargetkan Android 10 (level API 29) dan yang lebih tinggi akan diberi akses terbatas ke penyimpanan eksternal, atau penyimpanan terbatas, secara default. Jika penyimpanan terbatas diaktifkan, aplikasi tidak dapat mengakses direktori khusus aplikasi milik aplikasi lain.

Memverifikasi bahwa penyimpanan tersedia

Karena penyimpanan eksternal berada pada volume fisik yang mungkin dapat dihapus oleh pengguna, verifikasikan bahwa volume dapat diakses sebelum mencoba membaca data spesifik aplikasi dari, atau menulis data khusus aplikasi ke, penyimpanan eksternal.

Anda dapat mengkueri status volume dengan memanggil Environment.getExternalStorageState(). Jika status yang ditampilkan adalah MEDIA_MOUNTED, Anda dapat membaca dan menulis file khusus aplikasi dalam penyimpanan eksternal. Jika statusnya adalah MEDIA_MOUNTED_READ_ONLY, Anda hanya dapat membaca file ini.

Misalnya, metode berikut berguna untuk menentukan ketersediaan penyimpanan:

Kotlin

// Checks if a volume containing external storage is available
// for read and write.
fun isExternalStorageWritable(): Boolean {
    return Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED
}

// Checks if a volume containing external storage is available to at least read.
fun isExternalStorageReadable(): Boolean {
     return Environment.getExternalStorageState() in
        setOf(Environment.MEDIA_MOUNTED, Environment.MEDIA_MOUNTED_READ_ONLY)
}

Java

// Checks if a volume containing external storage is available
// for read and write.
private boolean isExternalStorageWritable() {
    return Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED);
}

// Checks if a volume containing external storage is available to at least read.
private boolean isExternalStorageReadable() {
     return Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED) ||
            Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED_READ_ONLY);
}

Pada perangkat tanpa penyimpanan eksternal yang dapat dilepas, gunakan perintah berikut untuk mengaktifkan volume virtual untuk menguji logika ketersediaan penyimpanan eksternal Anda:

adb shell sm set-virtual-disk true

Memilih lokasi penyimpanan fisik

Terkadang, perangkat yang mengalokasikan partisi dari memori internal sebagai penyimpanan eksternal juga menyediakan slot kartu SD. Ini berarti bahwa perangkat memiliki beberapa volume fisik yang dapat berisi penyimpanan eksternal, sehingga Anda perlu memilih mana yang akan digunakan untuk penyimpanan khusus aplikasi.

Untuk mengakses lokasi yang berbeda, panggil ContextCompat.getExternalFilesDirs(). Seperti yang ditunjukkan dalam cuplikan kode, elemen pertama dalam array yang ditampilkan dianggap sebagai volume penyimpanan eksternal utama. Gunakan volume ini kecuali penuh atau tidak tersedia.

Kotlin

val externalStorageVolumes: Array<out File> =
        ContextCompat.getExternalFilesDirs(applicationContext, null)
val primaryExternalStorage = externalStorageVolumes[0]

Java

File[] externalStorageVolumes =
        ContextCompat.getExternalFilesDirs(getApplicationContext(), null);
File primaryExternalStorage = externalStorageVolumes[0];

Mengakses file persisten

Untuk mengakses file khusus aplikasi dari penyimpanan eksternal, panggil getExternalFilesDir().

Untuk membantu mempertahankan performa aplikasi Anda, jangan membuka dan menutup file yang sama beberapa kali.

Cuplikan kode berikut menunjukkan cara memanggil getExternalFilesDir():

Kotlin

val appSpecificExternalDir = File(context.getExternalFilesDir(null), filename)

Java

File appSpecificExternalDir = new File(context.getExternalFilesDir(null), filename);

Membuat file cache

Untuk menambahkan file khusus aplikasi ke cache dalam penyimpanan eksternal, dapatkan referensi ke externalCacheDir:

Kotlin

val externalCacheFile = File(context.externalCacheDir, filename)

Java

File externalCacheFile = new File(context.getExternalCacheDir(), filename);

Menghapus file cache

Untuk menghapus file dari direktori cache eksternal, gunakan metode delete() pada objek File yang mewakili file:

Kotlin

externalCacheFile.delete()

Java

externalCacheFile.delete();

Konten media

Jika aplikasi Anda berfungsi dengan file media yang memberikan nilai kepada pengguna hanya dalam aplikasi, sebaiknya simpan file tersebut di direktori khusus aplikasi dalam penyimpanan kode eksternal, seperti yang ditunjukkan dalam cuplikan kode berikut:

Kotlin

fun getAppSpecificAlbumStorageDir(context: Context, albumName: String): File? {
    // Get the pictures directory that's inside the app-specific directory on
    // external storage.
    val file = File(context.getExternalFilesDir(
            Environment.DIRECTORY_PICTURES), albumName)
    if (!file?.mkdirs()) {
        Log.e(LOG_TAG, "Directory not created")
    }
    return file
}

Java

@Nullable
File getAppSpecificAlbumStorageDir(Context context, String albumName) {
    // Get the pictures directory that's inside the app-specific directory on
    // external storage.
    File file = new File(context.getExternalFilesDir(
            Environment.DIRECTORY_PICTURES), albumName);
    if (file == null || !file.mkdirs()) {
        Log.e(LOG_TAG, "Directory not created");
    }
    return file;
}

Anda harus menggunakan nama direktori yang disediakan oleh konstanta API seperti DIRECTORY_PICTURES. Nama direktori ini memastikan agar file diperlakukan dengan benar oleh sistem. Jika tidak satu pun dari nama sub-direktori yang telah ditentukan sebelumnya sesuai dengan file Anda, sebagai gantinya Anda dapat meneruskan null ke getExternalFilesDir(). Tindakan ini akan menampilkan direktori khusus aplikasi utama dalam penyimpanan eksternal.

Meminta ruang kosong

Banyak pengguna tidak memiliki banyak ruang penyimpanan yang tersedia di perangkat mereka, sehingga aplikasi Anda harus menggunakan ruang dengan efisien.

Jika Anda sudah mengetahui berapa banyak data yang disimpan, Anda dapat mengetahui berapa banyak ruang yang dapat disediakan oleh perangkat untuk aplikasi Anda dengan memanggil getAllocatableBytes(). Nilai yang ditampilkan getAllocatableBytes() mungkin lebih besar dari jumlah ruang kosong saat ini di perangkat. Hal ini karena sistem telah mengidentifikasi file yang dapat dihapus dari direktori cache aplikasi lain.

Jika ada cukup ruang untuk menyimpan data aplikasi Anda, panggil allocateBytes(). Jika tidak, aplikasi Anda dapat meminta pengguna untuk menghapus beberapa file dari perangkat atau menghapus semua file cache dari perangkat.

Cuplikan kode berikut menunjukkan contoh cara aplikasi Anda dapat meminta ruang kosong di perangkat:

Kotlin

// App needs 10 MB within internal storage.
const val NUM_BYTES_NEEDED_FOR_MY_APP = 1024 * 1024 * 10L;

val storageManager = applicationContext.getSystemService<StorageManager>()!!
val appSpecificInternalDirUuid: UUID = storageManager.getUuidForPath(filesDir)
val availableBytes: Long =
        storageManager.getAllocatableBytes(appSpecificInternalDirUuid)
if (availableBytes >= NUM_BYTES_NEEDED_FOR_MY_APP) {
    storageManager.allocateBytes(
        appSpecificInternalDirUuid, NUM_BYTES_NEEDED_FOR_MY_APP)
} else {
    val storageIntent = Intent().apply {
        // To request that the user remove all app cache files instead, set
        // "action" to ACTION_CLEAR_APP_CACHE.
        action = ACTION_MANAGE_STORAGE
    }
}

Java

// App needs 10 MB within internal storage.
private static final long NUM_BYTES_NEEDED_FOR_MY_APP = 1024 * 1024 * 10L;

StorageManager storageManager =
        getApplicationContext().getSystemService(StorageManager.class);
UUID appSpecificInternalDirUuid = storageManager.getUuidForPath(getFilesDir());
long availableBytes =
        storageManager.getAllocatableBytes(appSpecificInternalDirUuid);
if (availableBytes >= NUM_BYTES_NEEDED_FOR_MY_APP) {
    storageManager.allocateBytes(
            appSpecificInternalDirUuid, NUM_BYTES_NEEDED_FOR_MY_APP);
} else {
    // To request that the user remove all app cache files instead, set
    // "action" to ACTION_CLEAR_APP_CACHE.
    Intent storageIntent = new Intent();
    storageIntent.setAction(ACTION_MANAGE_STORAGE);
}

Membuat aktivitas pengelolaan penyimpanan

Aplikasi dapat mendeklarasikan dan membuat aktivitas kustom, yang saat diluncurkan akan memungkinkan pengguna mengelola data yang telah disimpan oleh aplikasi di perangkat pengguna. Aplikasi mendeklarasikan aktivitas "kelola ruang" kustom ini menggunakan atribut android:manageSpaceActivity dalam file manifes. Aplikasi file manager dapat memanggil aktivitas ini meskipun aplikasi Anda tidak mengekspor aktivitas tersebut; yaitu, saat aktivitas Anda menetapkan android:exported ke false.

Meminta pengguna untuk menghapus beberapa file perangkat

Untuk meminta pengguna memilih file pada perangkat yang akan dihapus, panggil intent yang menyertakan tindakan ACTION_MANAGE_STORAGE . Intent ini menampilkan perintah kepada pengguna. Jika diinginkan, permintaan ini dapat menampilkan jumlah ruang kosong yang tersedia di perangkat. Untuk menampilkan informasi yang mudah dipahami ini, gunakan hasil penghitungan berikut:

StorageStatsManager.getFreeBytes() / StorageStatsManager.getTotalBytes()

Meminta pengguna untuk menghapus semua file cache

Atau, Anda dapat meminta pengguna untuk menghapus file cache dari semua aplikasi di perangkat. Untuk melakukannya, aktifkan intent yang berisi tindakan intent ACTION_CLEAR_APP_CACHE .

Referensi lainnya

Untuk informasi selengkapnya tentang cara menyimpan file ke penyimpanan perangkat, lihat referensi berikut.

Video