Uygulamaya özel dosyalara erişme

Çoğu durumda, uygulamanız diğer uygulamaların erişmesi gerekmeyen veya erişememesi 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, hem kalıcı dosyaları depolamak için özel bir konum hem de önbellek verilerini depolamak 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, bu konumları yalnızca uygulamanızın kendisinin erişebileceği hassas verileri depolamak için iyi bir yer haline getirir.

  • Harici depolama dizinleri: Bu dizinler, hem kalıcı dosyaları depolamak için özel bir konum hem de önbellek verilerini depolamak için başka bir konum içerir. Uygun izinlere sahip olan başka bir uygulama bu dizinlere erişebilir ancak bu dizinlerde depolanan dosyalar, yalnızca uygulamanız tarafından kullanılmak üzere tasarlanmıştır. Özellikle diğer uygulamaların erişebilmesi gereken dosyalar oluşturmak istiyorsanız uygulamanız bu dosyaları harici depolama alanının paylaşılan depolama alanında depolamalıdır.

Kullanıcı uygulamanızı 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 saklamayı beklediği her şeyi kaydetmek için bu depolama alanını kullanmamalısınız. Örneğin, uygulamanız kullanıcıların fotoğraf çekmesine izin veriyorsa, kullanıcılar uygulamanızı kaldırdıktan sonra bile bu fotoğraflara erişebilmelerini beklerler. 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 dizinlerdeki dosyaların nasıl saklanacağı ve bu dosyalara nasıl erişileceği açıklanmaktadır.

Dahili depolamadan erişim

Sistem, her uygulama için dahili depolama içinde uygulamanın dosyalarını düzenleyebileceği dizinler sağlar. Bir dizin uygulamanızın kalıcı dosyaları için tasarlanmıştır, diğeri ise uygulamanızın önbelleğe alınan dosyalarını içerir. Uygulamanız bu dizinlerdeki dosyaları okumak ve bu dosyalara yazmak için sistem iznine ihtiyaç duymuyor.

Diğer uygulamalar, dahili depolamada depolanan dosyalara erişemez. Bu, dahili depolamayı diğer uygulamaların erişmemesi gereken uygulama verileri için iyi bir yer haline getirir.

Ancak, bu dizinlerin küçük olma eğiliminde olduğunu unutmayın. Uygulamaya özel dosyaları dahili depolama alanına yazmadan önce uygulamanızın cihazdaki boş alanı sorgulaması gerekir.

Kalıcı dosyalara erişme

Uygulamanızın normal, kalıcı dosyaları, bağlam nesnesinin filesDir özelliğini kullanarak erişebileceğiniz bir dizinde bulunur. Çerçeve, bu dizindeki dosyalara erişmenize ve bunları depolamanıza yardımcı olacak çeşitli yöntemler sağlar.

Dosyalara erişme ve dosyaları depolama

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

Uygulamanızın performansını korumak için aynı dosyayı birden fazla 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'sine alternatif olarak, filesDir dizini içindeki bir dosyaya yazan bir FileOutputStream almak için openFileOutput() yöntemini çağırabilirsiniz.

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 depolama alanında bu dizinde depolanan diğer uygulamaların dosyalara erişmesine izin vermek için FLAG_GRANT_READ_URI_PERMISSION özelliğine sahip bir FileProvider kullanın.

Dosyalara akış üzerinden 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öster

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

Kotlin

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

Java

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

İç içe dizinler oluşturma

Ayrıca Kotlin tabanlı kodda getDir() yöntemini çağırarak veya kök dizin ve yeni dizin adını Java tabanlı kodda bir File oluşturucuya ileterek iç içe yerleştirilmiş dizinler oluşturabilir veya 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 tanımlı önbellek dizinini kullanmanız gerekir. Uygulamaya özel tüm depolama alanlarında olduğu gibi, bu dizinde depolanan dosyalar kullanıcı uygulamanızı kaldırdığında kaldırılır, ancak bu dizindeki dosyalar daha önce kaldırılabilir.

çağrısı yapın.

Önbelleğe alınmış bir dosya oluşturmak için File.createTempFile() çağrısı yapı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şir:

Kotlin

val cacheFile = File(context.cacheDir, filename)

Java

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

Önbellek dosyalarını kaldır

Android bazen önbellek dosyalarını kendi başına siler, ancak bu dosyaları sizin için temizlemesi için sisteme güvenmemeniz gerekir. Uygulamanızın önbellek dosyalarını her zaman dahili depolama içinde tutmalısınız.

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

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

    Kotlin

    cacheFile.delete()
    

    Java

    cacheFile.delete();
    
  • Uygulama bağlamı için dosyanın adını ileten deleteFile() yöntemi:

    Kotlin

    context.deleteFile(cacheFileName)
    

    Java

    context.deleteFile(cacheFileName);
    

Harici depolama alanından erişim

Dahili depolama, uygulamaya özel dosyaları depolamak için yeterli alan sağlamıyorsa bunun yerine harici depolama alanı kullanmayı düşünün. Sistem, harici depolama alanındaki dizinler sağlar. Burada, uygulamalar kullanıcıya değer sağlayan dosyaları yalnızca uygulamanızın içinde düzenleyebilir. Dizinlerden biri uygulamanızın kalıcı dosyaları için tasarlanmıştır, diğeri ise uygulamanızın önbelleğe alınan dosyalarını içerir.

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 depolama alanıyla ilgili izinler istemesine gerek yoktur. Bu dizinlerde depolanan dosyalar, uygulamanız kaldırıldığında kaldırılır.

Android 9 (API düzeyi 28) veya önceki sürümleri çalıştıran 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 sahibi olmak ve dosya yığınını sınırlandırmak için Android 10 (API düzeyi 29) ve sonraki sürümleri hedefleyen uygulamalara, varsayılan olarak harici depolamaya veya kapsamlı depolama alanına kapsamlı erişim 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ğrulama

Harici depolama, kullanıcının kaldırabileceği fiziksel bir birimde bulunduğundan, harici depolama alanından uygulamaya özel verileri okumaya veya uygulamaya özel verileri yazmaya çalışmadan önce birimin erişilebilir olduğunu doğrulayın.

Environment.getExternalStorageState() yöntemini çağırarak birimin durumunu sorgulayabilirsiniz. Döndürülen durum MEDIA_MOUNTED ise harici depolama alanındaki uygulamaya özel dosyaları okuyup yazabilirsiniz. MEDIA_MOUNTED_READ_ONLY ise bu dosyaları yalnızca okuyabilirsiniz.

Örneğin, aşağıdaki yöntemler depolama alanı kullanılabilirliğini belirlemek açısından yararlı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 kullanılabilirliği mantığınızı test etmek amacıyla bir sanal birimi etkinleştirmek için aşağıdaki komutu kullanın:

adb shell sm set-virtual-disk true

Fiziksel depolama konumu seçin

Bazen, dahili belleğinin bir bölümünü harici depolama olarak ayıran bir cihaz aynı zamanda bir SD kart yuvası da sağlar. Bu, cihazda harici depolama içerebilen birden fazla fiziksel birim olduğu anlamına gelir. Bu nedenle, uygulamaya özel depolama alanı 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. Dolu olmadığı veya kullanılamadığı sürece bu birimi 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

Uygulamaya özel dosyalara harici depolama alanından erişmek için getExternalFilesDir() numaralı telefonu arayın.

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

Aşağıdaki kod snippet'inde getExternalFilesDir() çağrısının nasıl yapı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ına bakın:

Kotlin

val externalCacheFile = File(context.externalCacheDir, filename)

Java

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

Önbellek dosyalarını kaldır

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ızın içinde değer sağlayan medya dosyalarıyla çalışıyorsa bunları aşağıdaki kod snippet'inde gösterildiği gibi harici depolama alanındaki uygulamaya özel dizinlerde depolamak 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 doğru bir şekilde işlenmesini sağlar. Önceden tanımlanmış alt dizin adlarından hiçbiri dosyalarınıza uygun değilse null öğesini getExternalFilesDir() içine aktarabilirsiniz. Bu işlem, harici depolama içindeki kök uygulamaya özel dizini döndürür.

Boş alanı sorgula

Birçok kullanıcının cihazlarında yeterli depolama alanı olmadığından uygulamanız alanı dikkatli bir şekilde tüketmelidir.

Ne kadar veri depoladığınızı önceden biliyorsanız getAllocatableBytes() numarasını arayarak cihazın uygulamanıza ne kadar yer açabileceğini öğrenebilirsiniz. Döndürülen getAllocatableBytes() değeri, 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() numaralı telefonu arayın. Aksi takdirde, uygulamanız kullanıcıdan bazı dosyaları veya cihazdan tüm önbellek dosyalarını kaldırmasını isteyebilir.

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

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 alanı yönetimi etkinliği oluşturma

Uygulamanız, başlatıldığında kullanıcının cihazında depoladığı verileri yönetmesine olanak tanıyan özel bir etkinlik beyan edebilir ve oluşturabilir. Bu özel "alanı yönetme" etkinliğini, manifest dosyasındaki android:manageSpaceActivity özelliğini kullanarak beyan edersiniz. Uygulamanız etkinliği dışa aktarmadığında (etkinlikler android:exported değerini false olarak ayarladığında) bile, dosya yöneticisi uygulamaları bu etkinliği çağırabilir.

Kullanıcıdan bazı cihaz dosyalarını kaldırmasını iste

Kullanıcının cihazdaki kaldırılacak dosyaları seçmesini istemek için ACTION_MANAGE_STORAGE işlemini içeren bir amaç çağırın. Bu intent, kullanıcıya bir istem gösterir. İstenirse bu istem, cihazdaki kullanılabilir boş alan miktarını gösterebilir. Bu kullanıcı dostu bilgiyi 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ı iste

Alternatif olarak, kullanıcıdan önbellek dosyalarını cihazdaki tüm uygulamalardan 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 bakın.

Videolar