Ç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 dizinlerde hem kalıcı dosyaları depolamak için özel bir konum hem de önbellek verilerini depolamak için başka bir konum bulunur. Sistem, diğer uygulamaların bu konumlara erişmesini engeller. 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 dizinlerde hem kalıcı dosyaların depolanması için özel bir konum hem de önbellek verilerinin depolanması için başka bir konum bulunur. 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 sizin 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 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 kalıcı olmasını beklediği hiçbir şeyi kaydetmek için bu depolama alanını kullanmamanız gerekir. Ö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şebilmeyi 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 dizinlerdeki 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. Dizinlerden biri uygulamanızın kalıcı dosyaları, diğeri ise uygulamanızın önbelleğe alınmış dosyaları için tasarlanmıştır. Uygulamanızın bu dizinlerdeki dosyaları okumak ve yazmak için sistem izinlerine 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 seçenektir.
Ancak bu dizinlerin küçük olma eğiliminde 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 sıradan, kalıcı dosyaları, bir bağlam nesnesinin filesDir özelliğini kullanarak erişebileceğiniz bir dizinde bulunur. Bu çerçeve, bu dizindeki dosyalara erişmenize ve bunları 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'i, File API'nin nasıl kullanılacağını gösterir:
Kotlin
val file = File(context.filesDir, filename)
Java
File file = new File(context.getFilesDir(), filename);
Akış kullanarak dosya depolama
File API'sini kullanmaya alternatif olarak, filesDir dizinindeki bir dosyaya yazan FileOutputStream elde etmek 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()); }
Diğer uygulamaların, dahili depolama alanındaki bu dizinde depolanan dosyalara erişmesine izin vermek için FLAG_GRANT_READ_URI_PERMISSION özelliğine sahip bir FileProvider kullanın.
Akış kullanarak dosyalara 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() 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
Kotlin tabanlı kodda getDir() yöntemini çağırarak veya Java tabanlı kodda kök dizini ve yeni bir dizin adını File oluşturucusuna ileterek iç içe yerleştirilmiş dizinler oluşturabilir ya da bir iç dizini 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 saklamanız gerekiyorsa verileri kaydetmek için uygulamanın dahili depolama alanındaki belirlenmiş ö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 erken kaldırılabilir.
Önbelleğe alınmış bir dosya oluşturmak için File.createTempFile() yöntemini çağırı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'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ırma
Android bazen önbellek dosyalarını kendi kendine silse de bu dosyaları temizleme konusunda sisteme güvenmemeniz gerekir. Uygulamanızın önbellek dosyalarını her zaman dahili depolama alanında saklamanız gerekir.
Dahili depolama alanındaki önbellek dizininden bir dosyayı kaldırmak için aşağıdaki yöntemlerden birini kullanın:
Dosyayı temsil eden bir
Filenesnesindekidelete()yöntemi:Kotlin
cacheFile.delete()
Java
cacheFile.delete();
Uygulama bağlamının
deleteFile()yöntemini çağırıp dosyanın adını iletin: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, bir uygulamanın yalnızca uygulamanız içinde kullanıcıya 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ğ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 depolama alanıyla ilgili izinler 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. Kısıtlı 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 alanı, kullanıcının çıkarabileceği fiziksel bir birimde bulunduğundan, harici depolama alanından uygulamaya özel verileri okumaya veya bu alana uygulamaya özel veriler yazmaya çalışmadan önce birimin erişilebilir olduğunu doğrulayın.
Environment.getExternalStorageState() işlevini çağırarak birimin durumunu sorgulayabilirsiniz.
Döndürülen 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, aşağıdaki yöntemler depolama alanı kullanılabilirliğini belirlemek için 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 amacıyla sanal bir birimi etkinleştirmek için 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 bir cihazda 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() yöntemini çağırı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ılamaz durumda olmadığı sürece bu birimi kullan.
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() yöntemini ç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'i, getExternalFilesDir() yönteminin nasıl çağrılacağını gösterir:
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ı alın:
Kotlin
val externalCacheFile = File(context.externalCacheDir, filename)
Java
File externalCacheFile = new File(context.getExternalCacheDir(), filename);
Önbellek dosyalarını kaldırma
Harici önbellek dizininden bir dosyayı 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 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 doğru şekilde işlenmesini sağlar.
Önceden tanımlanmış alt dizin adlarından hiçbiri dosyalarınıza uygun değilse getExternalFilesDir() içine null iletebilirsiniz. Bu, harici depolama alanındaki uygulamaya özel kök dizini döndürür.
Boş alan sorgulama
Birçok kullanıcının cihazında fazla depolama alanı olmadığından uygulamanız alanı dikkatli kullanmalıdır.
Ne kadar veri depolayacağınızı önceden biliyorsanız getAllocatableBytes() yöntemini çağırarak cihazın uygulamanıza ne kadar alan sağlayabileceğini öğrenebilirsiniz.
getAllocatableBytes() işlevinin 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 tespit etmesidir.
Uygulamanızın verilerini kaydetmek için yeterli alan varsa allocateBytes() yöntemini ç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 alanı yönetimi etkinliği oluşturma
Uygulamanız, başlatıldığında kullanıcının, uygulamanızın kullanıcının cihazında depoladığı verileri yönetmesine olanak tanıyan özel bir etkinlik tanımlayıp oluşturabilir. Bu özel "alanı yönet" etkinliğini, manifest dosyasındaki android:manageSpaceActivity özelliğini kullanarak bildirirsiniz. Dosya yöneticisi uygulamaları, uygulamanız etkinliği dışa aktarmasa bile (yani etkinliğiniz android:exported değerini false olarak ayarladığında) bu etkinliği çağırabilir.
Kullanıcıdan bazı cihaz dosyalarını kaldırmasını isteyin
Kullanıcıdan cihazda kaldırılacak dosyaları seçmesini istemek için ACTION_MANAGE_STORAGE işlemini içeren bir intent çağırın. Bu intent, kullanıcıya bir istem gösterir. İstenirse bu istem, cihazda kullanılabilir boş alan miktarını gösterebilir. 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ıdan cihazdaki tüm uygulamaların önbellek dosyalarını 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 kaydetme hakkında daha fazla bilgi için aşağıdaki kaynakları inceleyin.