Bergabunglah bersama kami di ⁠#Android11: The Beta Launch Show pada tanggal 3 Juni!

Mengakses 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 (API level 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 mencopot pemasangan 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 file-nya. 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:

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 dari 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() dari 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 (API level 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 (API level 28) atau yang lebih rendah, aplikasi apa pun dapat mengakses file khusus aplikasi dalam penyimpanan eksternal, asalkan aplikasi lain 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 (API level 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() == Environment.MEDIA_MOUNTED;
    }

    // Checks if a volume containing external storage is available to at least read.
    private boolean isExternalStorageReadable() {
         return Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED ||
                Environment.getExternalStorageState() == 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(), seperti yang ditunjukkan dalam cuplikan kode berikut:

Kotlin

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

Java

    File appSpecificExternalDir = new File(context.getExternalFilesDir(), 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 Anda, 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 terdapat cukup ruang untuk menyimpan data aplikasi Anda, panggil allocateBytes() untuk mengklaim ruang tersebut. Jika tidak, aplikasi Anda dapat memanggil intent yang menyertakan tindakan ACTION_MANAGE_STORAGE. Intent ini menampilkan permintaan kepada pengguna, yang meminta mereka memilih file di perangkat untuk dihapus sehingga aplikasi Anda dapat memiliki ruang yang dibutuhkan. 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()
    

Cuplikan kode berikut menunjukkan contoh bagaimana 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 {
            action = ACTION_MANAGE_STORAGE
        }
        // Display prompt to user, requesting that they choose files to remove.
    }
    

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 {
        Intent storageIntent = new Intent();
        storageIntent.setAction(ACTION_MANAGE_STORAGE);
        // Display prompt to user, requesting that they choose files to remove.
    }
    

Referensi lainnya

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

Video