Mobil Programlama

Android

Dosyaları Kaydetmek

Lisans: Creative Commons 26.11.2020 tarihinde güncellendi
Bakabileceğiniz Etiketler: Eğitmen: Geleceği Yazanlar Ekibi

Android diğer platformlardaki dosya sistemlerine benzer şekilde disk temelli bir dosya sistemi kullanır. Bu eğitim içeriğinde File API'lerini kullanarak Android dosya sisteminde dosya okumayı ve dosyaya yazmayı nasıl yapacağınızı bulabilirsiniz.

Bir File nesnesi başından sonuna büyük boyutlu verileri okumak veya yazmak için ideal metotlar sunar. Örneğin resim dosyaları veya ağ üzerinde taşınabilen her şey için idealdir.

Bu eğitim içeriğinden uygulamanızda dosyalarla ilişkili temel görevleri gerçekleştirmeyi öğrenebilirsiniz. Bu sırada Linux dosya sisteminin temellerine ve java.io kitaplığındaki standart dosya giriş/çıkış API'lerine aşina olduğunuzu varsayıyoruz. Kafanıza takılan bir konu olduğunda soru-cevap bölümünü kullanabilirsiniz.

 

Dâhili ya da harici depolama alanını seçmek

Her Android cihazının iki dosya depolama alanı vardır: "dâhili" ve "harici". Bu isimler Android'in ilk günlerinden, çoğu cihazın yerleşik olarak gelen kalıcı hafızasından (dâhili) ve buna ek olarak çıkarılabilir mikro SD kart (harici) gibi depolama alanlarından geliyor. Bazı cihaz üreticileri, cihazlarının çıkarılabilir depolama alanı olmamasına rağmen kalıcı depolama alanlarını "dâhili" ve "harici" olarak bölümlemeyi tercih ediyor. Böylece gerçekten harici depolama alanı çıkarılmış olsun ya da hiç olmasın, her zaman iki depolama alanı oluyor ve API'nin davranışı da değişmiyor. Şu iki liste her depolama alanı türünü özetlemeye yetecektir:

Dâhili Depolama Alanı Harici Depolama Alanı
Her zaman kullanılabilir. Her zaman kullanılabilir değildir; kullanıcı USB depolama aygıtı şeklinde bilgisayarına bağlamış (mount) olabilir veya bazı durumlarda doğrudan cihazla olan bağlantısını kaldırabilir.
Buraya kaydedilen dosyalar varsayılan olarak sadece uygulamanız tarafından erişilebilir durumdadır. Herkesçe okunabilir. Bir başka deyişle, buraya kaydedilen dosyalar kontrolünüz dışında okunabilir.
Kullanıcı uygulamanızı cihazından kaldırdığında, sistem uygulamanızla ilgili tüm dosyaları dâhili depolama alanından siler.

Kullanıcı uygulamanızı cihazından kaldırdığında, sistem uygulamanızla ilgili dosyaları eğer onları getExternalFilesDir() metodunun döndürdüğü dizine kaydettiyseniz silecektir.

Harici depolama alanı erişim kısıtlaması gerektirmeyen diğer uygulamalarla paylaşmak istediğiniz veya kullanıcının bilgisayarıyla erişmesine izin verdiğiniz dosyalar için en iyi yerdir.

ÖNERİ: Uygulamalar varsayılan olarak dâhili depolama alanına kuruluyor olsa da, manifest dosyanızda <application> elementinin android:installLocation özniteliğine uygun tanımlamayı yaparak uygulamanızın harici depolama alanına kurulmasını da sağlayabiliyorsunuz. Kullanıcılar bu seçeneği uygulamanın APK boyutu çok büyük olduğunda ve harici depolama alanları dâhili olandan daha büyükse takdirle karşılıyorlar.

 

Harici depolama alanı için izinlerin alınması

Harici depolama alanına yazabilmek için WRITE_EXTERNAL_STORAGE iznini manifest dosyanızda istemeniz gerekiyor:

 

<manifest ...>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    ...
</manifest>

 

UYARI: Şu an tüm uygulamalar harici depolama alanını özel bir izin almadan okuma olanağına sahip. Buna rağmen bu durum ileriki sürümlerde değişecektir. Eğer uygulamanız harici depolama alanını okuyacaksa (yazma yapmayacaksa) READ_EXTERNAL_STORAGE iznini beyan etmeniz gerekiyor. Uygulamanızın beklendiği gibi çalışmasına devam ettiğinden emin olmak için bu izni şimdi beyan etmelisiniz:

 

<manifest ...>
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    ...
</manifest>

 

Yine de uygulamanız WRITE_EXTERNAL_STORAGE iznini kullanırsa aynı zamanda harici depolama alanını da okuyabilirsiniz.

Dâhili depolama alanına dosya kaydetmek için herhangi bir izin almanıza gerek yok. Uygulamanız dâhili depolama alanındaki dizinine istediği zaman dosya yazabilir ve o dosyaları okuyabilir.

 

Bir dosyayı dâhili depolama alanına kaydetmek

Bir dosyayı dâhili depolama alanına kaydederken aşağıdaki metotlardan birini çağırarak uygun dizini File nesnesi şeklinde elde edebilirsiniz:

getFilesDir() - Uygulamanızın dâhili depolama alanındaki dizinini ifade eden bir File nesnesi döndürür.

getCacheDir() - Uygulamanızın dâhili depolama alanındaki geçici dosyalarının saklandığı dizini ifade eden bir File nesnesi döndürür. Buradaki her dosyayı ihtiyacınız kalmadığında sildiğinizden ve 1 MB gibi kabul edilebilir bir boyut sınırıyla kullandığınız yeri sınırladığınızdan emin olmalısınız. Sistemin depolama alanı küçülmeye başladığında sizi uyarmadan buradaki dosyaları silebilir.

Bu dizinlerden birinde yeni bir dosya oluşturmak isterseniz File() yapılandırıcı metodunu kullanabilirsiniz. Bunu yaparken yapılandırıcı metoda dâhili depolama alanını veren yukarıdaki metotlardan birini geçmeniz yeterli. Örneğin:

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

Alternatif olarak openFileOutput() metodunu çağırabilir ve böylece dâhili depolama alanınıza dosya yazabilecek bir FileOutputStream alabilirsiniz. Aşağıdaki örnekte bazı metinlerin bir dosyaya nasıl yazıldığını görebilirsiniz:

String dosyaAdi = "dosyam";
String string = "merhaba herkese!";
FileOutputStream outputStream;

try {
  outputStream = openFileOutput(dosyaAdi, Context.MODE_PRIVATE);
  outputStream.write(string.getBytes());
  outputStream.close();
} catch (Exception e) {
  e.printStackTrace();
}

Bunun yerine geçici olan bir önbellek dosyası oluşturmak istiyorsanız createTempFile() metodunu kullanmalısınız. Örneğin aşağıdaki örnek metod, uygulamanızın dâhili depolama alanındaki kendine has önbellek dizininde, ismini URL'den çıkardığı bir dosya oluşturur:

public File getTempFile(Context context, String url) {
    File file;
    try {
        String dosyaAdi = Uri.parse(url).getLastPathSegment();
        file = File.createTempFile(dosyaAdi, null, context.getCacheDir());
    catch (IOException e) {
        // Error while creating file
    }
    return file;
}

NOT: Uygulamanızın dâhili depolama alanındaki dizini, uygulamanızın paket adına özel bir şekilde Android dosya sisteminin özel bir yerinde bulunur. Teknik olarak, eğer dosya modunu "okunabilir" yaptıysanız diğer uygulamalar sizin dâhili dosyalarını okuyabilir. Yine de bunu yapabilmek için paket adınızı ve dosya isimlerini bilmeleri gerekir. Aynı şekilde diğer uygulamalar sizin dâhili dizinlerinize göz atamaz ve siz dosyaları okunabilir veya yazılabilir olarak açıkça belirtmediyseniz okuma ya da yazma yapamazlar. Bundan dolayı dâhili depolama alanındaki dosyalarınız için kullanabildiğiniz kadar MODE_PRIVATE modunu kullanın ki diğer uygulamalar tarafından hiçbir zaman erişilemesinler.

 

Bir dosyayı harici depolama alanına kaydetmek

Harici depolama alanları her zaman kullanılabilir olamayabileceğinden dolayı -kullanıcı tarafından cihaz bilgisayara bağlanmış olabilir veya SD kart çıkartılmış olabilir- erişmeye çalışmadan önce o bölümün kullanılabilir olduğunu doğrulamanız gerekir.

Harici depolama alanının durumunu getExternalStorageState() metoduyla sorgulayabilirsiniz. Metodun döndürdüğü sonuç MEDIA_MOUNTED değeri olursa bu dosya okuma ve yazma yapabileceğiniz anlamına gelir. Aşağıdaki örnek depolama alanının müsait olup olmadığını sorguluyor:

/* harici depolama alanının okuma/yazmaya müsaitliğine bakar */
public boolean isExternalStorageWritable() {
    String state = Environment.getExternalStorageState();
    if (Environment.MEDIA_MOUNTED.equals(state)) {
        return true;
    }
    return false;
}
 
/* harici depolama alanının sadece okumaya müsaitliğe bakar */
public boolean isExternalStorageReadable() {
    String state = Environment.getExternalStorageState();
    if (Environment.MEDIA_MOUNTED.equals(state) ||
        Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
        return true;
    }
    return false;
}

Her ne kadar harici depolama alanı kullanıcılar ve diğer uygulamalar tarafından değiştirilebilir durumda olsa da şu iki kategorideki dosyalarınızı buraya kaydedebilirsiniz:

  • Genel dosyalar: Kullanıcı ve diğer uygulamalar tarafından özgürce kullanılabilecek dosyalar. Kullanıcı uygulamanızı cihazından kaldırsa bile bu dosyalar kullanılmaya devam edilebilir. Örneğin uygulamanız tarafından çekilen fotoğraflar veya İnternet'ten indirilmiş dosyalar bu şekilde sınıflandırılabilir.
  • Özel dosyalar: Uygulamanıza ait ve uygulamanız kaldırıldığında silinmiş olması gereken dosyalar. Bu dosyalar harici depolama alanında olduğundan teknik olarak kullanıcı ve diğer uygulamalar tarafından erişilebilir olsa da uygulamanız dışında kullanıcı için bir değer ifade etmeyen dosyalardır. Kullanıcı uygulamanızı kaldırdığında sistem harici depolama alanındaki tüm özel dosyaları da siler. Örneğin uygulamanız tarafından indirilmiş uygulamanıza has ek dosyalar veya geçici medya dosyalar.

Genel dosyalarınızı harici depolama alanına kaydetmek isterseniz, getExternalStoragePublicDirectory() metodunu çağırarak uygun dizini ifade eden bir File nesnesi elde edebilirsiniz. Bu metod kaydetmeyi istediğiniz dosyanın tipiyle ilgili bir parametre alır. Böylece diğer genel dosyalarla birlikte mantığa uygun bir şekilde (DIRECTORY_MUSIC veya DIRECTORY_PICTURES gibi) organize edilirler. Örnek:

public File getAlbumStorageDir(String albumName) {
    // kullanıcının genel resimler dizini için kullanılan dizini alalım
    File file = new File(Environment.getExternalStoragePublicDirectory(
            Environment.DIRECTORY_PICTURES), albumName);
    if (!file.mkdirs()) {
        Log.e(LOG_TAG, "dizin oluşturulamadı");
    }
    return file;
}

Uygulamanız için özel dosyaları kaydetmek istiyorsanız getExternalFilesDir() metodunu çağırarak uygun dizini alabilir ve istediğiniz dizinin tipini belirten bir ismi de parametre olarak geçebilirsiniz. Bu yolla oluşturulan her dizin, kullanıcı uygulamayı kaldırdığında silinen ve uygulamanın harici depolama alanındaki tüm dosyalarını taşıyan bir tepe dizine eklenir.

Aşağıdaki örnek metodu bireysel fotoğraf albümü için kullanılabilecek bir dizin oluşturmakta kullanabilirsiniz:

public File getAlbumStorageDir(Context context, String albumName) {
    // uygulamamızda kullanmak üzere özel resim dizini için bir dizin alalım
    File file = new File(context.getExternalFilesDir(
            Environment.DIRECTORY_PICTURES), albumName);
    if (!file.mkdirs()) {
        Log.e(LOG_TAG, "dizin oluşturalamadı");
    }
    return file;
}

Eğer dosyalarınız için önceden tanımlı uygun bir alt dizin ismi yoksa getExternalFilesDir() metodunu "null" değer geçerek çağırabilirsiniz. Bu işlem size uygulamanızın harici depolama alanında yer alan özel dizinini döndürecektir.

Şunu unutmamalısınız: getExternalFilesDir() metodu, bir dizin oluşturur ki o dizin kullanıcı uygulamayı kaldırdığında silinecek bir dizinin içinde oluşturulur. Eğer buradaki dosyaların kullanıcı uygulamanızı kaldırdığında bile kalmasını istiyorsanız (örneğin, uygulamanız bir kamera uygulamasıdır ve çekilen fotoğrafların kalması gerekir), bu metod yerine getExternalStoragePublicDirectory() metodunu kullanmalısınız.

İster ortak dosyalar için getExternalStoragePublicDirectory() metodunu kullanmış olun, ister uygulamanıza özel dosyalar için getExternalFilesDir() metodunu kullanmış olun, kullanacağınız dizin isimlerinin DIRECTORY_PICTURES gibi API sabitleri tarafından sağlanacağını unutmayın. Bu dizin isimleri, içindeki dosyaların sistem tarafından doğru şekilde muamele görmesini sağlar. Örneğin DIRECTORY_RINGTONES dizinine kaydedilen dosyalar, sistemin medya tarayıcısı tarafından müzik yerine zil sesi olarak kategorize edilir. Dolayısıyla buraya kaydedeceğiniz dosyalar zil sesi dosyası olmalıdır ki kullanıcı cihazını amacına uygun dosyalarla kullanabilsin.

 

Boş alanı sorgulamak

Kaydedeceğiniz verilerin ne kadar yer tuttuğunu erkenden öğrenmek isterseniz, hiç IOException oluşmasına meydan vermeden getFreeSpace() veya getTotalSpace() metotlarını kullanarak yeterli alan olup olmadığına bakabilirsiniz. Bu metotlar sırasıyla, geçerli depolama biriminde ne kadar boş alan kaldığı bilgisini ve alanın toplam boyutu bilgisini verir. Bu bilgiler depolama alanını belli bir eşiğin üstünde tıka basa doldurmaktan kaçınmak için de yardımcıdırlar.

Ancak şunu unutmamak gerekir ki getFreeSpace() metodunun size döndürdüğü yer kadar byte'ı yazmak için kullanabileceğinizin garantisini sistem veremez. Eğer döndürülen MB değeri kaydetmek istediğinizden fazlaysa veya dosya sisteminin doluluk oranı %90'dan azsa muhtemelen işlem sorunsuzca gerçekleşir. Öteki türlü muhtemelen depolama alanına yazamayacaksınız.

NOT: Dosya kaydetmeden önce ne kadar boş alan kaldığını denetlemeniz gerekmiyor. Bunun yerine dosyanızı hemen kaydetmeyi deneyin ve bir sorun çıkarsa bir IOException hatası fırlatın. Eğer gerçekten ne kadar alan kaldığınızı bilmenize gerek yoksa bunu böyle yapın. Örneğin bir resim dosyanızın olduğunu varsayın ve kodlamasını PNG'den JPEG'e çevireceksiniz. Böyle bir işlemde önceden dosyanın boyutunu bilmenize gerek olmayacaktır.

 

Bir dosyayı silmek

İhtiyacınız kalmayan dosyaları silmeye daima özen göstermelisiniz. Bir dosyayı silmenin en pratik yolu, açılan dosyanın referans değişkeni üzerinden delete() metodunu çağırmaktır.

dosyaAdi.delete();

Silinecek dosya dâhili depolama alanına kaydedilmişse, Context sınıfından yararlanarak yerini öğrenebilir ve deleteFile() metodunu kullanarak silebilirsiniz.

mContext.deleteFile(dosyaAdi);

NOT: Kullanıcı uygulamanızı cihazından kaldırdığında, sistem şunları silecektir:

  • Dâhili depolama alanına kaydettiğiniz tüm dosyalar
  • Harici depolama alanına getExternalFilesDir() metodu kullanarak kaydettiğiniz tüm dosyalar

Son olarak şunu ekleyelim: getCacheDir() metoduyla oluşturulmuş önbellekteki tüm dosyaları bir düzen temelinde elle silmelisiniz. Aynı şekilde ihtiyacınız olmayan diğer dosyaları da.

 

Bu sayfadaki parçalar Android Open Source Project kapsamında oluşturulmuş ve paylaşılmış içeriğin küçük değişiklikler yapılmış hâlidir ve Creative Commons 2.5 Attribution License'ta belirlenen koşullara göre kullanılmıştır.

Bu eğitim içeriğinin orijinal hâline buradan ulaşabilirsiniz: Saving Files