Uygulamaya özel dosyalara erişme

Çoğu durumda uygulamanız, diğer uygulamaların erişmesi gerekmeyen veya erişmemesi gereken dosyalar oluşturur. Sistem, bu tür uygulamaya özel dosyaları depolamak için aşağıdaki konumları sağlar:

  • Dahili depolama dizinleri: Bu dizinler, kalıcı dosyaların depolanması için ayrılmış bir konumun yanı sıra önbellek verilerinin depolanması için başka bir konum içerir. Sistem, diğer uygulamaların bu konumlara erişmesini engeller ve Android 10 (API düzeyi 29) ve sonraki sürümlerde bu konumlar şifrelenir. Bu özellikler, yalnızca uygulamanızın kendisinin erişebileceği hassas verileri depolamak için bu konumları uygun hale getirir.

  • Harici depolama dizinleri: Bu dizinler, kalıcı dosyaların depolanması için ayrılmış bir konumun yanı sıra önbellek verilerinin depolanması için başka bir konum içerir. Başka bir uygulamanın uygun izinlere sahip olması durumunda bu dizinlere erişmesi mümkün olsa da bu dizinlerde depolanan dosyalar yalnızca uygulamanız tarafından kullanılmak üzere tasarlanmıştır. Diğer uygulamaların erişebileceği dosyalar oluşturmak istiyorsanız uygulamanız bu dosyaları harici depolama biriminin paylaşılan depolama bölümünde saklamalıdır.

Kullanıcı uygulamanızın yüklemesini kaldırdığında, uygulamaya özel depolama alanına kaydedilen dosyalar kaldırılır. Bu davranış nedeniyle, kullanıcının uygulamanızdan bağımsız olarak kalıcı olmasını beklediği hiçbir şeyi kaydetmek için bu depolama alanını kullanmamalısınız. Örneğin, uygulamanız kullanıcıların fotoğraf çekmesine olanak tanıyorsa kullanıcı, uygulamanızı kaldırdıktan sonra bile bu fotoğraflara erişebilmeyi bekler. Bu nedenle, bu tür dosyaları uygun medya koleksiyonuna kaydetmek için paylaşılan depolama alanını kullanmalısınız.

Aşağıdaki bölümlerde, uygulamaya özel dizinlerde dosyaların nasıl depolanacağı ve bu dosyalara nasıl erişileceği açıklanmaktadır.

Dahili depolama alanından erişme

Sistem, her uygulama için dahili depolama biriminde, uygulamanın dosyalarını düzenleyebileceği dizinler sağlar. Bir dizin uygulamanızın kalıcı dosyaları için, diğeri ise uygulamanızın önbelleğe alınmış dosyaları için tasarlanmıştır. Uygulamanızın bu dizinlerdeki dosyaları okuyup yazmak için herhangi bir sistem iznine ihtiyacı yoktur.

Diğer uygulamalar, dahili depolama alanında saklanan dosyalara erişemez. Bu nedenle, diğer uygulamaların erişmemesi gereken uygulama verileri için dahili depolama alanı iyi bir yerdir.

Ancak bu dizinlerin genellikle küçük olduğunu unutmayın. Uygulamanız, dahili depolama alanına uygulamaya özel dosyalar yazmadan önce cihazdaki boş alanı sorgulamalıdır.

Kalıcı dosyalara erişme

Uygulamanızın normal ve kalıcı dosyaları, bir bağlam nesnesinin filesDir özelliği kullanılarak erişilebilen bir dizinde bulunur. Bu çerçeve, bu dizindeki dosyalara erişmenize ve dosyaları depolamanıza yardımcı olacak çeşitli yöntemler sunar.

Dosyalara erişme ve dosyaları depolama

Dosyalara erişmek ve dosyaları depolamak için File API'yi kullanabilirsiniz.

Uygulamanızın performansını korumak için aynı dosyayı birden çok kez açıp kapatmayın.

Aşağıdaki kod snippet'inde File API'nin nasıl kullanılacağı gösterilmektedir:

Kotlin

val file = File(context.filesDir, filename)

Java

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

Akış kullanarak dosya depolama

File API'yi kullanmak yerine, openFileOutput() işlevini çağırarak FileOutputStream alabilirsiniz. Bu işlev, filesDir dizinindeki bir dosyaya yazar.

Aşağıdaki kod snippet'inde, bir dosyaya nasıl metin yazılacağı gösterilmektedir:

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());
}

Dahili depolamada bu dizinde depolanan dosyalara diğer uygulamaların erişmesine izin vermek için FLAG_GRANT_READ_URI_PERMISSION özelliğiyle birlikte FileProvider kullanın.

Akış kullanarak dosyaya erişme

Bir dosyayı akış olarak okumak için openFileInput() kullanın:

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();
}

Dosya listesini görüntüleme

Aşağıdaki kod snippet'inde gösterildiği gibi fileList() işlevini çağırarak filesDir dizinindeki tüm dosyaların adlarını içeren bir dizi elde edebilirsiniz:

Kotlin

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

Java

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

İç içe dizinler oluşturma

Ayrıca, Kotlin tabanlı kodda getDir() işlevini çağırarak veya Java tabanlı kodda kök dizini ve yeni bir dizin adını File oluşturucusuna ileterek iç içe dizinler oluşturabilir ya da iç dizin açabilirsiniz:

Kotlin

context.getDir(dirName, Context.MODE_PRIVATE)

Java

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

Önbellek dosyaları oluşturma

Hassas verileri yalnızca geçici olarak depolamanız gerekiyorsa verileri kaydetmek için uygulamanın dahili depolama alanındaki belirlenmiş önbellek dizinini kullanmanız gerekir. Tüm uygulamaya özel depolama alanlarında olduğu gibi, bu dizinde depolanan dosyalar da kullanıcı uygulamanızı kaldırdığında kaldırılır. Ancak bu dizindeki dosyalar daha erken kaldırılabilir.

Önbelleğe alınmış bir dosya oluşturmak için File.createTempFile() numaralı telefonu arayın:

Kotlin

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

Java

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

Uygulamanız, bir bağlam nesnesinin cacheDir özelliğini ve File API'sini kullanarak bu dizindeki bir dosyaya erişiyor:

Kotlin

val cacheFile = File(context.cacheDir, filename)

Java

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

Önbellek dosyalarını kaldırma

Android bazen önbellek dosyalarını kendi kendine silse de bu dosyaları sizin için temizlemesi konusunda sisteme güvenmemelisiniz. Uygulamanızın önbellek dosyalarını her zaman dahili depolama alanında tutmanız gerekir.

Bir dosyayı dahili depolama birimindeki önbellek dizininden kaldırmak için aşağıdaki yöntemlerden birini kullanın:

  • Dosyayı temsil eden bir File nesnesindeki delete() yöntemi:

    Kotlin

    cacheFile.delete()

    Java

    cacheFile.delete();
  • Dosyanın adını ileterek uygulamanın bağlamının deleteFile() yöntemini kullanın:

    Kotlin

    context.deleteFile(cacheFileName)

    Java

    context.deleteFile(cacheFileName);

Harici depolama alanından erişim

Dahili depolama alanı, uygulamaya özel dosyaları depolamak için yeterli alan sağlamıyorsa bunun yerine harici depolama alanını kullanabilirsiniz. Sistem, harici depolama alanında uygulamaların yalnızca kendi içinde kullanıcıya değer sağlayan dosyaları düzenleyebileceği dizinler sunar. Bir dizin uygulamanızın kalıcı dosyaları için tasarlanmıştır, diğerinde ise uygulamanızın önbelleğe alınmış dosyaları bulunur.

Android 4.4 (API düzeyi 19) veya sonraki sürümlerde, uygulamanızın harici depolama alanındaki uygulamaya özel dizinlere erişmek için depolamayla ilgili herhangi bir izin istemesi gerekmez. Bu dizinlerde depolanan dosyalar, uygulamanızın yüklemesi kaldırıldığında silinir.

Android 9 (API düzeyi 28) veya önceki sürümlerin yüklü olduğu cihazlarda, uygulamanız uygun depolama izinlerine sahip olduğu sürece diğer uygulamalara ait uygulamaya özel dosyalara erişebilir. Kullanıcılara dosyaları üzerinde daha fazla kontrol olanağı sunmak ve dosya karmaşasını sınırlamak için Android 10 (API düzeyi 29) ve sonraki sürümleri hedefleyen uygulamalara varsayılan olarak harici depolama alanına kısıtlı erişim (kısıtlı depolama) verilir. Kapsamlı depolama etkinleştirildiğinde uygulamalar, diğer uygulamalara ait uygulamaya özel dizinlere erişemez.

Depolama alanının kullanılabilir olduğunu doğrulayın

Harici depolama alanı, kullanıcının kaldırabileceği fiziksel bir birimde bulunduğundan, uygulamaya özgü verileri harici depolama alanından okumaya veya harici depolama alanına yazmaya çalışmadan önce birime erişilebildiğini doğrulayın.

Environment.getExternalStorageState() işlevini çağırarak birimin durumunu sorgulayabilirsiniz. Geri verilen durum MEDIA_MOUNTED ise harici depolama alanında uygulamaya özel dosyaları okuyup yazabilirsiniz. MEDIA_MOUNTED_READ_ONLY ise bu dosyaları yalnızca okuyabilirsiniz.

Örneğin, depolama alanı kullanılabilirliğini belirlemek için aşağıdaki yöntemler kullanışlıdır:

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);
}

Çıkarılabilir harici depolama alanı olmayan cihazlarda, harici depolama alanı kullanılabilirlik mantığınızı test etmek için sanal bir birimi etkinleştirmek üzere aşağıdaki komutu kullanın:

adb shell sm set-virtual-disk true

Fiziksel depolama konumu seçme

Bazen dahili belleğinin bir bölümünü harici depolama alanı olarak ayıran cihazlarda SD kart yuvası da bulunur. Bu, cihazda harici depolama alanı içerebilecek birden fazla fiziksel birim olduğu anlamına gelir. Bu nedenle, uygulamaya özel depolama alanınız için hangisini kullanacağınızı seçmeniz gerekir.

Farklı konumlara erişmek için ContextCompat.getExternalFilesDirs() numaralı telefonu arayın. Kod snippet'inde gösterildiği gibi, döndürülen dizideki ilk öğe birincil harici depolama birimi olarak kabul edilir. Bu birimi dolu veya kullanılamaz durumda olmadığı sürece kullanın.

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];

Kalıcı dosyalara erişme

Harici depolama alanındaki uygulamaya özel dosyalara erişmek için getExternalFilesDir() işlevini çağırın.

Uygulamanızın performansını korumak için aynı dosyayı birden çok kez açıp kapatmayın.

Aşağıdaki kod snippet'inde getExternalFilesDir() işlevinin nasıl çağrılacağı gösterilmektedir:

Kotlin

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

Java

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

Önbellek dosyaları oluşturma

Harici depolama alanındaki önbelleğe uygulamaya özel bir dosya eklemek için externalCacheDir referansı alın:

Kotlin

val externalCacheFile = File(context.externalCacheDir, filename)

Java

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

Önbellek dosyalarını kaldırma

Bir dosyayı harici önbellek dizininden kaldırmak için dosyayı temsil eden bir File nesnesinde delete() yöntemini kullanın:

Kotlin

externalCacheFile.delete()

Java

externalCacheFile.delete();

Medya içeriği

Uygulamanız, kullanıcıya yalnızca uygulamanızda değer sağlayan medya dosyalarıyla çalışıyorsa bu dosyaları, aşağıdaki kod snippet'inde gösterildiği gibi harici depolama alanındaki uygulamaya özel dizinlerde saklamanız en iyisidir:

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;
}

DIRECTORY_PICTURES gibi API sabitleri tarafından sağlanan dizin adlarını kullanmanız önemlidir. Bu dizin adları, dosyaların sistem tarafından düzgün şekilde işlenmesini sağlar. Önceden tanımlanmış alt dizin adlarından hiçbiri dosyalarınıza uygun değilse bunun yerine null değerini getExternalFilesDir()'a iletebilirsiniz. Bu, harici depolamadaki kök uygulamaya özel dizini döndürür.

Sorgu boş alanı

Birçok kullanıcının cihazında fazla depolama alanı bulunmaz. Bu nedenle uygulamanız alanı dikkatli bir şekilde kullanmalıdır.

Ne kadar veri depolayacağınızı önceden biliyorsanız getAllocatableBytes() işlevini çağırarak cihazın uygulamanıza ne kadar alan sağlayabileceğini öğrenebilirsiniz. getAllocatableBytes() değerinin döndürdüğü değer, cihazdaki mevcut boş alan miktarından daha büyük olabilir. Bunun nedeni, sistemin diğer uygulamaların önbellek dizinlerinden kaldırabileceği dosyaları tanımlamış olmasıdır.

Uygulamanızın verilerini kaydetmek için yeterli alan varsa allocateBytes() işlevini çağırın. Aksi takdirde uygulamanız, kullanıcıdan cihazdaki bazı dosyaları kaldırmasını veya cihazdaki tüm önbellek dosyalarını kaldırmasını isteyebilir.

Aşağıdaki kod snippet'inde, uygulamanızın cihazdaki boş alanı nasıl sorgulayabileceğine dair bir örnek gösterilmektedir:

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);
}

Depolama yönetimi etkinliği oluşturma

Uygulamanız, başlatıldığında kullanıcının uygulamanızın cihazında depoladığı verileri yönetmesine olanak tanıyan özel bir etkinlik bildirebilir ve oluşturabilir. Bu özel "alan yönetme" etkinliğini, manifest dosyasındaki android:manageSpaceActivity özelliğiyle bildirirsiniz. Dosya yöneticisi uygulamaları, uygulamanız etkinliği dışa aktarmadığında bile bu etkinliği çağırabilir. Yani etkinliğiniz android:exported değerini false olarak ayarladığında bu durum geçerlidir.

Kullanıcıdan bazı cihaz dosyalarını kaldırmasını isteyin.

Kullanıcıdan cihazdaki hangi dosyaların kaldırılacağını seçmesini istemek için ACTION_MANAGE_STORAGE işlemini içeren bir amaç çağırın. Bu amaç, kullanıcıya bir istem gösterir. İstenirse bu istemde cihazda kullanılabilir boş alan miktarı gösterilebilir. Bu kullanıcı dostu bilgileri göstermek için aşağıdaki hesaplamanın sonucunu kullanın:

StorageStatsManager.getFreeBytes() / StorageStatsManager.getTotalBytes()

Kullanıcıdan tüm önbellek dosyalarını kaldırmasını isteyin.

Alternatif olarak, kullanıcının cihazdaki tüm uygulamalarda önbellek dosyalarını temizlemesini isteyebilirsiniz. Bunu yapmak için ACTION_CLEAR_APP_CACHE intent işlemini içeren bir amaç çağırın.

Ek kaynaklar

Dosyaları cihazın depolama alanına kaydetme hakkında daha fazla bilgi için aşağıdaki kaynaklara göz atın.

Videolar