Uygulamaya özel dosyalara erişme

Çoğu durumda uygulamanız, diğer uygulamaların erişmesi veya erişmemesi gereken dosyalar oluşturur. Sistem, bu tür uygulamaya özel dosyaların saklanabilmesi 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 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 başka bir uygulamanın 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. Özellikle diğer uygulamaların erişebileceği dosyalar oluşturmak istiyorsanız uygulamanız bu dosyaları harici depolama alanının paylaşılan depolama bölümünde saklamalı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 kalmasını beklediği şeyleri 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ı, uygulamanızı kaldırdıktan sonra bile bu fotoğraflara erişebilmelerini bekler. Bu nedenle, bu tür dosyaları uygun medya koleksiyonuna kaydetmek için, paylaşılan depolama alanını kullanmanız gerekir.

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şim

Sistem, her uygulama için dahili depolama alanında 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ınmış dosyalarını içerir. Uygulamanız, bu dizinlerdeki dosyaları okumak ve dosyalara yazmak için herhangi bir sistem izni gerektirmez.

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

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

Kalıcı dosyalara erişme

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

Dosyalara erişme ve dosya depolama

Dosyalara erişmek ve dosya 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 kullanmanın alternatifi olarak, filesDir dizininde bir dosyaya yazan bir FileOutputStream almak için openFileOutput()'i çağırabilirsiniz.

Aşağıdaki kod snippet'i bir dosyaya nasıl metin yazılacağını gösterir:

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

Diğer uygulamaların, dahili depolama alanında bu dizinde depolanan dosyalara erişmesine izin vermek için FLAG_GRANT_READ_URI_PERMISSION özelliğiyle bir FileProvider kullanın.

Dosyaya akış üzerinden erişme

Bir dosyayı akış olarak okumak için openFileInput() simgesini 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() işlevini ç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

Kotlin tabanlı kodda getDir()'i çağırarak veya Java tabanlı kodda kök dizini ve yeni bir dizin adını bir File yapıcısına aktararak 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 özel önbellek dizinini kullanmanız gerekir. Uygulamaya özel tüm depolama alanlarında olduğu gibi, kullanıcı uygulamanızı kaldırdığında bu dizinde depolanan dosyalar kaldırılır. Ancak, bu dizindeki dosyalar daha erken kaldırılabilir.

Önbelleğe alınan 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, bağlam nesnesinin cacheDir mülkünü ve File API'yi 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ır

Android bazen önbellek dosyalarını kendi kendine silese de bu dosyaların sizin için temizlenmesini sisteme bırakmamalısınız. Uygulamanızın önbelleğe alınmış dosyalarını her zaman dahili depolama alanında tutmanız gerekir.

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

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

    Kotlin

    cacheFile.delete()
    

    Java

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

    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ı kullanmayı düşünün. Sistem, harici depolama alanında bir uygulamanın kullanıcıya yalnızca uygulamanızda değer sağlayan dosyaları 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ınmış dosyalarını içerir.

Android 4.4 (API düzeyi 19) veya sonraki sürümlerde uygulamanızın, harici depolama içindeki uygulamaya özel dizinlere erişmek için depolama alanıyla ilgili herhangi bir izin 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ın uygun depolama izinlerine sahip olması koşuluyla uygulamanız diğer uygulamalara ait uygulamaya özgü dosyalara erişebilir. Kullanıcılara dosyaları üzerinde daha fazla kontrol vermek ve dosya karmaşası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 kısıtlı depolamaya kapsamlı erişim verilir. Kapsamlı depolama etkinleştirildiğinde uygulamalar diğer uygulamalara ait uygulamaya özel dizinlere erişemez.

Kullanılabilir depolama alanı olduğunu doğrulama

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

Environment.getExternalStorageState() çağrısı yaparak ses biriminin durumunu sorgulayabilirsiniz. Döndürülen durum MEDIA_MOUNTED ise harici depolamada uygulamaya özel dosyaları okuyabilir ve 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 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 alanı kullanılabilirlik mantığınızı test etmek için sanal bir birim etkinleştirmek üzere aşağıdaki komutu kullanın:

adb shell sm set-virtual-disk true

Fiziksel depolama alanı seçin

Bazen dahili belleğinin bir bölümünü harici depolama alanı olarak ayıran cihazlarda SD kart yuvası da bulunur. Bu durum, cihazın harici depolama içerebilecek birden fazla fiziksel birimi olduğu anlamına gelir. Bu nedenle, uygulamaya özel depolamanız için hangisinin kullanılacağını 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 veya kullanılamıyorsa bu depolama alanını 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ından uygulamaya özel dosyalara erişmek için getExternalFilesDir() numaralı telefonu arayı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ını inceleyin:

Kotlin

val externalCacheFile = File(context.externalCacheDir, filename)

Java

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

Önbelleğe alınan dosyaları kaldırma

Bir dosyayı harici önbellek dizininden kaldırmak için dosyayı temsil eden 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 bu dosyaları, aşağıdaki kod snippet'inde gösterildiği gibi harici depolama içindeki 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 sabit değerleri tarafından sağlanan dizin adlarını kullanmanız önemlidir. Bu dizin adları, dosyaların sistem tarafından doğru şekilde işlenmesini sağlar. Önceden tanımlanmış alt dizin adlarından hiçbiri dosyalarınıza uygun değilse bunun yerine null öğesini getExternalFilesDir() dizinine aktarabilirsiniz. Bu komut, harici depolama alanındaki uygulamaya özel kök dizini döndürür.

Boş alan sorgusu

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

Ne kadar veri depoladığınızı önceden biliyorsanız getAllocatableBytes() numaralı telefonu arayarak cihazın uygulamanıza ne kadar alan sağlayabileceğini öğrenebilirsiniz. getAllocatableBytes() işlevinin döndürülen 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ı tespit etmiş 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'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 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 edip oluşturabilir. Bu özel "alanı yönetme" etkinliğini, manifest dosyasında android:manageSpaceActivity özelliğini kullanarak bildirirsiniz. Uygulamanız etkinliği dışa aktarmadığında, yani etkinliğiniz android:exported öğesini false olarak ayarlandığında bile dosya yöneticisi uygulamaları bu etkinliği çağırabilir.

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

Kullanıcıdan kaldırılacak dosyaları seçmesini istemek için ACTION_MANAGE_STORAGE işlemini içeren bir intent çağırın. Bu niyet, kullanıcıya bir istem gösterir. İsterseniz bu istemde cihazda mevcut 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ı iste

Alternatif olarak, kullanıcıdan cihazdaki tüm uygulamalardaki önbelleğe alınmış dosyaları temizlemesini isteyebilirsiniz. Bunu yapmak için ACTION_CLEAR_APP_CACHE intent işlemini içeren bir intent çağırın.

Ek kaynaklar

Dosyaları cihazın depolama alanına kaydetmeyle ilgili daha fazla bilgi için aşağıdaki kaynaklara bakın.

Videolar