APK Genişletme Dosyaları

Google Play, kullanıcıların indirdiği sıkıştırılmış APK'nın 100 MB'tan büyük olmamasını zorunlu kılar. Çoğu uygulama için bu alan, uygulamanın tüm kodu ve öğeleri için geniş bir alan sunar. Ancak bazı uygulamalar yüksek kaliteli grafikler, medya dosyaları veya diğer büyük öğeler için daha fazla alana ihtiyaç duyar. Önceden, uygulamanızın sıkıştırılmış indirme boyutu 100 MB'ı aştığında, kullanıcı uygulamayı açtığında ek kaynakları barındırmanız ve indirmeniz gerekiyordu. Ek dosyaları barındırmak ve sunmak maliyetli olabilir ve kullanıcı deneyimi genellikle idealin altında olur. Google Play, bu süreci sizin için kolaylaştırmak ve kullanıcılar için daha keyifli hale getirmek amacıyla, APK'nızı tamamlayan iki büyük genişletme dosyası eklemenize olanak tanır.

Google Play, uygulamanız için genişletme dosyalarını barındırır ve ücretsiz olarak cihaza sunar. Genişletme dosyaları, uygulamanızın bunlara erişebileceği cihazın paylaşılan depolama konumuna ("SD kart veya USB monte edilebilir bölüm, "harici" depolama alanı olarak da bilinir) kaydedilir. Çoğu cihazda, Google Play genişletme dosyalarını APK'yı indirirken de indirir. Böylece, kullanıcı uygulamayı ilk kez açtığında uygulamanız gereken her şeye sahip olur. Ancak bazı durumlarda, uygulamanız başladığında uygulamanızın Google Play'den dosyaları indirmesi gerekir.

Genişletme dosyası kullanmak istemiyorsanız ve uygulamanızın sıkıştırılmış indirme boyutu 100 MB'tan büyükse uygulamanızı Android App Bundle'ı kullanarak yüklemeniz gerekir. Bu pakette, sıkıştırılmış indirme boyutu en fazla 200 MB olabilir. Ayrıca, uygulama paketlerinin kullanılması APK'ların oluşturulmasını ve Google Play'de imzalanmasını geciktirdiği için kullanıcılar optimize edilmiş APK'ları yalnızca uygulamanızı çalıştırmak için ihtiyaç duydukları kod ve kaynaklarla indirirler. Birden fazla APK veya genişletme dosyası oluşturmak, imzalamak ve yönetmek zorunda kalmazsınız. Kullanıcılar daha küçük ve daha optimize edilmiş indirmeler elde ederler.

Genel bakış

Google Play Console'u kullanarak her APK yüklediğinizde, APK'ya bir veya iki genişletme dosyası ekleyebilirsiniz. Her dosya en fazla 2 GB olabilir ve istediğiniz herhangi bir biçimde olabilir. Ancak indirme işlemi sırasında bant genişliğini korumak için sıkıştırılmış bir dosya kullanmanızı öneririz. Kavramsal olarak her genişletme dosyası farklı bir rol oynar:

  • Ana genişletme dosyası, uygulamanızın gerektirdiği ek kaynaklar için birincil genişletme dosyasıdır.
  • Yama genişletme dosyası isteğe bağlıdır ve ana genişletme dosyasında yapılan küçük güncellemeler için tasarlanmıştır.

İki genişletme dosyasını istediğiniz gibi kullanabilirsiniz, ancak ana genişletme dosyasının birincil öğeleri yayınlamasını ve çok az güncellenirse bu dosyanın nadiren güncellenmesini öneririz. Yama genişletme dosyası daha küçük olmalı ve "yama operatörü" olarak işlev görmelidir. Bu dosyanın her ana sürümle veya gerektiğinde güncellenmesi gerekir.

Ancak, uygulama güncellemeniz için yalnızca yeni bir yama genişletme dosyası gerektirse bile manifest'e güncellenmiş versionCode içeren yeni bir APK yüklemeniz gerekir. (Play Console, mevcut bir APK'ya genişletme dosyası yüklemenize izin vermez.)

Not: Yama genişletme dosyası, anlam açısından ana genişletme dosyasıyla aynıdır. Her bir dosyayı istediğiniz şekilde kullanabilirsiniz.

Dosya adı biçimi

Yüklediğiniz her bir genişletme dosyası, seçtiğiniz herhangi bir biçimde (ZIP, PDF, MP4 vb.) olabilir. Ayrıca, JOBB aracını kullanarak bir dizi kaynak dosyasını ve bu kümeye ait sonraki yamaları kapsülleyebilir ve şifreleyebilirsiniz. Dosya türünden bağımsız olarak, Google Play bunları opak ikili blob olarak kabul eder ve dosyaları aşağıdaki şemayı kullanarak yeniden adlandırır:

[main|patch].<expansion-version>.<package-name>.obb

Bu şemanın üç bileşeni vardır:

main veya patch
Dosyanın ana dosya mı yoksa yama genişletme dosyası mı olduğunu belirtir. Her APK için yalnızca bir ana dosya ve bir yama dosyası olabilir.
<expansion-version>
Bu, genişletmenin ilk olarak ilişkilendirilmiş olduğu APK'nın sürüm koduyla eşleşen bir tam sayıdır (uygulamanın android:versionCode değeriyle eşleşir).

Play Console, yüklenen bir genişletme dosyasını yeni bir APK ile yeniden kullanmanıza izin verse de genişletme dosyasının adı değişmez, dosya ilk kez yüklendiğinde kendisine uygulanan sürümü korur.

<package-name>
Uygulamanızın Java stili paket adı.

Örneğin, APK sürümünüzün 314159 ve paket adınızın com.example.app olduğunu varsayalım. Bir ana genişletme dosyası yüklerseniz dosya şu şekilde yeniden adlandırılır:

main.314159.com.example.app.obb

Depolama alanı konumu

Google Play genişletme dosyalarınızı bir cihaza indirdiğinde, bu dosyaları sistemin paylaşılan depolama konumuna kaydeder. Uygun davranışı sağlamak için genişletme dosyalarını silmemeniz, taşımamanız veya yeniden adlandırmamanız gerekir. Uygulamanızın Google Play'den indirme işlemini kendisi yapması gerekirse dosyaları tam olarak aynı konuma kaydetmeniz gerekir.

getObbDir() yöntemi, genişletme dosyalarınızın belirli konumunu aşağıdaki biçimde döndürür:

<shared-storage>/Android/obb/<package-name>/
  • <shared-storage>, getExternalStorageDirectory() tarihinden itibaren kullanılabilen paylaşılan depolama alanına giden yoldur.
  • <package-name>, uygulamanızın getPackageName() adresinden kullanılabilen Java stili paket adıdır.

Her uygulama için bu dizinde hiçbir zaman ikiden fazla genişletme dosyası bulunamaz. Biri ana genişletme dosyası, diğeri ise yama genişletme dosyasıdır (gerekiyorsa). Uygulamanızı yeni genişletme dosyalarıyla güncellediğinizde önceki sürümlerin üzerine yazılır. Android 4.4 (API düzeyi 19) sürümünden itibaren uygulamalar, OBB genişletme dosyalarını harici depolama izni olmadan okuyabilir. Ancak Android 6.0 (API düzeyi 23) ve sonraki sürümlerin bazı uygulamaları hâlâ izin gerektirir. Bu nedenle, uygulama manifest dosyasında READ_EXTERNAL_STORAGE iznini beyan etmeniz ve çalışma zamanında aşağıdaki gibi izin istemeniz gerekir:

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

Android 6 ve sonraki sürümler için çalışma zamanında harici depolama izni istenmelidir. Ancak, Android'in bazı uygulamalarında OBB dosyalarının okunması için izin gerekmez. Aşağıdaki kod snippet'i, harici depolama izni istemeden önce okuma erişiminin nasıl kontrol edileceğini gösterir:

Kotlin

val obb = File(obb_filename)
var open_failed = false

try {
    BufferedReader(FileReader(obb)).also { br ->
        ReadObbFile(br)
    }
} catch (e: IOException) {
    open_failed = true
}

if (open_failed) {
    // request READ_EXTERNAL_STORAGE permission before reading OBB file
    ReadObbFileWithPermission()
}

Java

File obb = new File(obb_filename);
 boolean open_failed = false;

 try {
     BufferedReader br = new BufferedReader(new FileReader(obb));
     open_failed = false;
     ReadObbFile(br);
 } catch (IOException e) {
     open_failed = true;
 }

 if (open_failed) {
     // request READ_EXTERNAL_STORAGE permission before reading OBB file
     ReadObbFileWithPermission();
 }

Genişletme dosyalarınızın içeriğini açmanız gerekiyorsa OBB genişletme dosyalarını daha sonra silmeyin ve paketlenmemiş verileri aynı dizine kaydetmeyin. Paketlenmemiş dosyalarınızı getExternalFilesDir() tarafından belirtilen dizine kaydetmeniz gerekir. Ancak, mümkünse verileri paketinden çıkarmanızı gerektirmek yerine, doğrudan dosyadan okumanıza olanak tanıyan bir genişletme dosyası biçimi kullanmanız önerilir. Örneğin, verilerinizi doğrudan ZIP dosyasından okuyan APK Genişletme Zip Kitaplığı adlı bir kitaplık projesi sunduk.

Dikkat: APK dosyalarından farklı olarak, paylaşılan depolama alanına kaydedilen dosyalar kullanıcı ve diğer uygulamalar tarafından okunabilir.

İpucu: Medya dosyalarını ZIP olarak paketliyorsanız, medya oynatma çağrılarını ofset ve uzunluk kontrolleriyle (MediaPlayer.setDataSource() ve SoundPool.load() gibi) ZIP'i paketinden çıkarmanıza gerek kalmadan kullanabilirsiniz. Bu işlemin çalışması için, ZIP paketlerini oluştururken medya dosyaları üzerinde ek sıkıştırma yapmamanız gerekir. Örneğin, zip aracını kullanırken, sıkıştırılmaması gereken dosya son eklerini belirtmek için -n seçeneğini kullanmanız gerekir:
zip -n .mp4;.ogg main_expansion media_files

İndirme işlemi

Google Play çoğu zaman APK'yı cihaza indirirken genişletme dosyalarınızı da indirir ve kaydeder. Ancak bazı durumlarda, Google Play genişletme dosyalarını indiremez veya kullanıcı daha önce indirilmiş genişletme dosyalarını silmiş olabilir. Bu tür durumların üstesinden gelebilmek için uygulamanızın, ana etkinlik başladığında Google Play tarafından sağlanan URL'yi kullanarak dosyaları kendi kendine indirebilmesi gerekir.

İndirme işlemi genel hatlarıyla şu şekildedir:

  1. Kullanıcı, uygulamanızı Google Play'den yüklemeyi seçer.
  2. Google Play genişletme dosyalarını indirebiliyorsa (çoğu cihaz için durum budur), bunları APK ile birlikte indirir.

    Google Play genişletme dosyalarını indiremezse yalnızca APK'yı indirir.

  3. Kullanıcı uygulamanızı başlattığında uygulamanız, genişletme dosyalarının cihazda halihazırda kayıtlı olup olmadığını kontrol etmelidir.
    1. Yanıtınız evet ise uygulamanız kullanıma hazırdır.
    2. Görünmüyorsa uygulamanız, genişletme dosyalarını Google Play'den HTTP üzerinden indirmelidir. Uygulamanız, Google Play'in uygulama Lisanslama hizmetini kullanarak Google Play istemcisine bir istek göndermelidir. Bu hizmet her genişletme dosyasının adına, dosya boyutuna ve URL'sine yanıt verir. Bu bilgileri kullanarak dosyaları indirip uygun depolama konumuna kaydedebilirsiniz.

Dikkat: Uygulamanız başlatıldığında dosyaların halihazırda cihazda olmaması durumunda, genişletme dosyalarını Google Play'den indirmek için gereken kodu eklemeniz çok önemlidir. Aşağıdaki Genişletme Dosyalarını İndirme bölümünde açıklandığı gibi, bu işlemi büyük ölçüde basitleştiren ve bir hizmetten indirme işlemini sizden minimum miktarda kodla gerçekleştiren bir kitaplık hazırladık.

Geliştirme kontrol listesi

Uygulamanızla genişletme dosyalarını kullanmak için gerçekleştirmeniz gereken görevlerin bir özetini burada bulabilirsiniz:

  1. Öncelikle, uygulamanızın sıkıştırılmış indirme boyutunun 100 MB'tan büyük olması gerekip gerekmediğini belirleyin. Alan değerlidir ve toplam indirme boyutunuzu mümkün olduğunca küçük tutmalısınız. Uygulamanız farklı ekran yoğunluklarında grafik öğelerinizin birden fazla sürümünü sağlamak için 100 MB'tan fazla kullanıyorsa, her APK'nın yalnızca hedeflediği ekranlar için gerekli öğeleri içerdiği birden fazla APK yayınlamayı düşünebilirsiniz. Google Play'de yayınlarken en iyi sonuçları elde etmek için uygulamanızın derlenmiş tüm kod ve kaynaklarını içeren ancak APK oluşturma ve Google Play'de imzalamayı erteleyen bir Android App Bundle yükleyin.
  2. Hangi uygulama kaynaklarının APK'nızdan ayrılacağını belirleyin ve ana genişletme dosyası olarak kullanılacak bir dosyada paketleyin.

    Normalde, ana genişletme dosyasında güncelleme gerçekleştirirken yalnızca ikinci yama genişletme dosyasını kullanmanız gerekir. Ancak kaynaklarınız ana genişletme dosyası için 2 GB sınırını aşıyorsa diğer öğeleriniz için yama dosyasını kullanabilirsiniz.

  3. Uygulamanızı, cihazın paylaşılan depolama alanındaki genişletme dosyalarınızdaki kaynakları kullanacak şekilde geliştirin.

    Genişletme dosyalarını silmemeniz, taşımamanız veya yeniden adlandırmamanız gerektiğini unutmayın.

    Uygulamanız belirli bir biçim gerektirmiyorsa genişletme dosyalarınız için ZIP dosyaları oluşturmanızı, ardından bunları APK Genişletme Zip Kitaplığı'nı kullanarak okumanızı öneririz.

  4. Başlatma sırasında genişletme dosyalarının cihazda olup olmadığını kontrol eden uygulama ana etkinliğine bir mantık ekleyin. Dosyalar cihazda yoksa genişletme dosyalarının URL'lerini istemek için Google Play'in Uygulama Lisanslama hizmetini kullanın, ardından bunları indirip kaydedin.

    Yazmanız gereken kod miktarını önemli ölçüde azaltmak ve indirme sırasında iyi bir kullanıcı deneyimi sağlamak için, indirme davranışını uygulamak amacıyla İndirme Aracı Kitaplığı'nı kullanmanızı öneririz.

    Kitaplığı kullanmak yerine kendi indirme hizmetinizi derliyorsanız genişletme dosyalarının adını değiştirmemeniz ve doğru depolama konumuna kaydetmeniz gerektiğini unutmayın.

Uygulama geliştirmenizi tamamladıktan sonra Genişletme Dosyalarınızı Test Etme kılavuzunu takip edin.

Kurallar ve Sınırlamalar

APK genişletme dosyaları ekleme, uygulamanızı Play Console'u kullanarak yüklediğinizde kullanılabilen bir özelliktir. Uygulamanızı ilk kez yüklerken veya genişletme dosyaları kullanan bir uygulamayı güncellerken aşağıdaki kuralları ve sınırlamaları göz önünde bulundurmanız gerekir:

  1. Her bir genişletme dosyası en fazla 2 GB boyutunda olabilir.
  2. Genişletme dosyalarınızı Google Play'den indirebilmek için kullanıcının uygulamanızı Google Play'den edinmiş olması gerekir. Uygulama başka yollarla yüklenmişse Google Play, genişletme dosyalarınızın URL'lerini sağlamaz.
  3. Uygulamanızın içinden indirme işlemi gerçekleştirilirken, Google Play'in her dosya için sağladığı URL her indirme için benzersizdir ve her birinin süresi, uygulamanıza verildikten kısa bir süre sonra dolar.
  4. Uygulamanızı yeni bir APK ile güncellerseniz veya aynı uygulama için birden fazla APK yüklerseniz önceki bir APK için yüklediğiniz genişletme dosyalarını seçebilirsiniz. Genişletme dosyasının adı değişmez. Dosya, orijinal olarak ilişkilendirildiği APK tarafından alınan sürümü korur.
  5. Farklı cihazlar için farklı genişletme dosyaları sağlamak üzere birden fazla APK ile birlikte genişletme dosyaları kullanırsanız, benzersiz bir versionCode değeri sağlamak ve her APK için farklı filtreler beyan etmek üzere yine de her cihaza ayrı APK'lar yüklemeniz gerekir.
  6. Genişletme dosyalarını tek başına değiştirerek uygulamanız için bir güncelleme yayınlayamazsınız. Uygulamanızı güncellemek için yeni bir APK yüklemeniz gerekir. Değişiklikleriniz yalnızca genişletme dosyalarınızdaki öğelerle ilgiliyse versionCode (ve belki de versionName) değiştirerek APK'nızı güncelleyebilirsiniz.

  7. Diğer verileri obb/ dizininize kaydetmeyin. Bazı verileri paketinden çıkarmanız gerekiyorsa bunları getExternalFilesDir() tarafından belirtilen konuma kaydedin.
  8. Güncelleme gerçekleştirmiyorsanız .obb genişletme dosyasını silmeyin veya yeniden adlandırmayın. Bu işlem, genişletme dosyasını Google Play'in (veya uygulamanızın) tekrar tekrar indirmesine neden olur.
  9. Bir genişletme dosyasını manuel olarak güncellerken önceki genişletme dosyasını silmeniz gerekir.

Genişletme Dosyalarını İndirme

Çoğu durumda Google Play, APK'yı yüklediği veya güncellediği sırada genişletme dosyalarınızı cihaza indirir ve kaydeder. Böylece, genişletme dosyaları, uygulamanız ilk kez başlatıldığında kullanılabilir. Ancak bazı durumlarda, uygulamanızın Google Play'in Uygulama Lisanslama hizmetinden gelen bir yanıtta size sağlanan URL'den istekte bulunarak genişletme dosyalarını kendisini indirmesi gerekir.

Genişletme dosyalarınızı indirmek için ihtiyacınız olan temel mantık şu şekildedir:

  1. Uygulamanız başladığında genişletme dosyalarını paylaşılan depolama konumunda (Android/obb/<package-name>/ dizininde) arayın.
    1. Genişletme dosyaları varsa hazırsınız demektir ve uygulamanız devam edebilir.
    2. Genişletme dosyaları burada yoksa:
      1. Uygulamanızın genişletme dosyası adlarını, boyutlarını ve URL'lerini almak için Google Play'in Uygulama Lisanslama özelliğini kullanarak bir istek gerçekleştirin.
      2. Genişletme dosyalarını indirmek ve genişletme dosyalarını kaydetmek için Google Play tarafından sağlanan URL'leri kullanın. Dosyaları paylaşılan depolama konumuna (Android/obb/<package-name>/) kaydetmeniz ve Google Play'in yanıtında sağlanan dosya adını olduğu gibi kullanmanız gerekir.

        Not: Google Play'in genişletme dosyalarınız için sağladığı URL her indirme için benzersizdir ve her bir URL, uygulamanıza verildikten kısa bir süre sonra sona erer.

Uygulamanız ücretsizse (ücretli bir uygulama değilse) muhtemelen uygulama Lisanslama hizmetini kullanmamışsınızdır. Temel olarak, uygulamanızla ilgili lisanslama politikalarını zorunlu kılmak ve kullanıcının, uygulamanızı kullanma hakkına (uygulamanın ücretini Google Play'de haklı olarak ödemiş olması) sağlamak için tasarlanmıştır. Lisanslama hizmeti, genişletme dosyası işlevini kolaylaştırmak amacıyla, uygulamanıza ait Google Play'de barındırılan genişletme dosyalarının URL'sini de içeren bir yanıt sağlayacak şekilde iyileştirilmiştir. Dolayısıyla, uygulamanız kullanıcılar için ücretsiz olsa bile, APK genişletme dosyalarını kullanmak için Lisans Doğrulama Kitaplığı'nı (LVL) eklemeniz gerekir. Elbette, uygulamanız ücretsizse lisans doğrulamasını zorunlu kılmanız gerekmez. Yalnızca genişletme dosyalarınızın URL'sini döndüren isteği gerçekleştirmek için kitaplığa ihtiyacınız vardır.

Not: Uygulamanız ücretsiz olup olmadığından bağımsız olarak Google Play, yalnızca kullanıcı uygulamanızı Google Play'den edindiyse genişletme dosyası URL'lerini döndürür.

LVL'ye ek olarak, genişletme dosyalarını bir HTTP bağlantısı üzerinden indiren ve cihazın paylaşılan depolama alanında doğru konuma kaydeden bir kod setine ihtiyacınız vardır. Uygulamanızda bu prosedürü oluştururken göz önünde bulundurmanız gereken birkaç sorun vardır:

  • Cihazda, genişletme dosyaları için yeterli alan olmayabilir. Bu nedenle, indirme işlemine başlamadan önce kontrol etmeniz ve yeterli alan yoksa kullanıcıyı uyarmanız gerekir.
  • Dosya indirmeleri, kullanıcı etkileşimini engellememek ve indirme işlemi tamamlanırken kullanıcının uygulamanızdan ayrılmasına olanak tanımak için bir arka plan hizmetinde gerçekleştirilmelidir.
  • İstek ve indirme sırasında sorunsuz bir şekilde ele almanız gereken çeşitli hatalar oluşabilir.
  • İndirme işlemi sırasında ağ bağlantısı değişebilir. Bu nedenle, bu tür değişikliklerle ilgilenmeniz ve kesintiye uğrarsa mümkün olduğunda indirme işlemine devam etmeniz gerekir.
  • İndirme işlemi arka planda gerçekleşirken indirme işleminin ilerlemesini gösteren, işlem tamamlandığında kullanıcıya bilgi veren ve seçildiğinde kullanıcıyı tekrar uygulamanıza yönlendiren bir bildirim sağlamanız gerekir.

Bu çalışmayı kolaylaştırmak için, genişletme dosyası URL'lerini lisanslama hizmeti aracılığıyla isteyen, genişletme dosyalarını indiren, yukarıda listelenen tüm görevleri gerçekleştiren ve hatta etkinliğinizin indirme işlemini duraklatıp devam ettirmesine olanak tanıyan İndirme Aracı Kitaplığı'nı oluşturduk. İndirme Aracı Kitaplığı ve uygulamanıza birkaç kod kancası eklediğinizde, genişletme dosyalarını indirmeyle ilgili neredeyse tüm iş sizin için önceden kodlanmıştır. Bu nedenle, minimum çabayla en iyi kullanıcı deneyimini sağlamak için genişletme dosyalarınızı indirmek için İndirme Kitaplığı'nı kullanmanızı öneririz. Aşağıdaki bölümlerde yer alan bilgilerde, kitaplığın uygulamanıza nasıl entegre edileceği açıklanmaktadır.

Genişletme dosyalarını Google Play URL'lerini kullanarak indirmek için kendi çözümünüzü geliştirmeyi tercih ederseniz lisans isteği gerçekleştirmek için uygulama Lisanslama dokümanlarındaki talimatları uygulamanız ve ardından yanıt ekstralarından genişletme dosyası adlarını, boyutlarını ve URL'lerini almanız gerekir. Lisanslama politikanız olarak APKExpansionPolicy sınıfını (Lisans Doğrulama Kitaplığı'na dahildir) kullanmalısınız. Bu sınıf, lisanslama hizmetinden alınan genişletme dosya adlarını, boyutlarını ve URL'lerini yakalayan bir politikadır.

İndirme Aracı Kitaplığı hakkında

Uygulamanızla APK genişletme dosyalarını kullanmak ve en az çabayla en iyi kullanıcı deneyimini sağlamak için Google Play APK Genişletme Kitaplığı paketinde bulunan İndirme Kitaplığı'nı kullanmanızı öneririz. Bu kitaplık, genişletme dosyalarınızı arka plan hizmetinde indirir, indirme durumuyla ilgili bir kullanıcı bildirimi gösterir, ağ bağlantı kaybını yönetir, mümkün olduğunda indirme işlemini devam ettirir ve daha pek çok işlem yapar.

Genişletme dosyalarını İndirme Kitaplığı'nı kullanarak uygulamak için tüm yapmanız gereken:

  • Her biri yalnızca birkaç satır kod gerektiren özel bir Service alt sınıfı ve BroadcastReceiver alt sınıfını genişletin.
  • Ana etkinliğinize, genişletme dosyalarının önceden indirilip indirilmediğini kontrol eden, indirilmemişse indirme işlemini çağıran ve ilerleme durumu kullanıcı arayüzü görüntüleyen bir mantık ekleyin.
  • Ana etkinliğinizde, indirme işleminin ilerleme durumuyla ilgili güncellemeleri alan birkaç yöntemle bir geri çağırma arayüzü uygulayın.

Aşağıdaki bölümlerde, İndirme Aracı Kitaplığı'nı kullanarak uygulamanızı nasıl ayarlayacağınız açıklanmaktadır.

İndirme Aracı Kitaplığı'nı kullanmaya hazırlanma

İndirme Aracı Kitaplığı'nı kullanmak için SDK Yöneticisi'nden iki paket indirmeniz ve uygun kitaplıkları uygulamanıza eklemeniz gerekir.

Öncelikle Android SDK Yöneticisi'ni (Araçlar > SDK Yöneticisi) açın ve Görünüm ve Davranış > Sistem Ayarları > Android SDK bölümünden SDK Araçları sekmesini seçerek seçip indirin:

  • Google Play Lisanslama Kitaplığı paketi
  • Google Play APK Genişletme Kitaplığı paketi

Lisans Doğrulama Kitaplığı ve İndirme Kitaplığı için yeni bir kitaplık modülü oluşturun. Her kitaplık için:

  1. File > New > New Module (Dosya > Yeni > Yeni Modül) seçeneğini belirleyin.
  2. Yeni Modül Oluştur penceresinde Android Kitaplığı'nı ve ardından İleri'yi seçin.
  3. "Google Play Lisans Kitaplığı" ve "Google Play İndirme Aracı Kitaplığı" gibi bir uygulama/Kitaplık adı belirleyin, Minimum SDK düzeyi'ni ve ardından Son'u seçin.
  4. Dosya > Proje Yapısı'nı seçin.
  5. Özellikler sekmesini seçin ve Kitaplık Kod Deposu'nda <sdk>/extras/google/ dizininden kitaplığı girin (Lisans Doğrulama Kitaplığı için play_licensing/ veya İndirme Kitaplığı için play_apk_expansion/downloader_library/).
  6. Yeni modülü oluşturmak için Tamam'ı seçin.

Not: İndirme Aracı Kitaplığı, Lisans Doğrulama Kitaplığı'na bağlıdır. Lisans Doğrulama Kitaplığı'nı İndirme Aracı Kitaplığı'nın proje özelliklerine eklediğinizden emin olun.

Alternatif olarak, komut satırından projenizi kitaplıkları içerecek şekilde güncelleyebilirsiniz:

  1. Dizinleri <sdk>/tools/ dizini olacak şekilde değiştirin.
  2. Projenize hem LVL hem de İndirme Kitaplığı'nı eklemek için --library seçeneğiyle android update project yürütün. Örneğin:
    android update project --path ~/Android/MyApp \
    --library ~/android_sdk/extras/google/market_licensing \
    --library ~/android_sdk/extras/google/market_apk_expansion/downloader_library
    

Uygulamanıza hem Lisans Doğrulama Kitaplığı hem de İndirme Kitaplığı'nı ekleyerek, genişletme dosyalarını Google Play'den indirme özelliğini hızlı bir şekilde entegre edebilirsiniz. Genişletme dosyaları için seçtiğiniz biçim ve bunları paylaşılan depolama alanından nasıl okuyacağınız, uygulamanızın ihtiyaçlarına göre göz önünde bulundurmanız gereken ayrı bir uygulamadır.

İpucu: APK Genişletme paketi, bir uygulamada İndirme Aracı Kitaplığı'nın nasıl kullanılacağını gösteren örnek bir uygulama içerir. Örnek, APK Genişletme paketinde bulunan, APK Genişletme Zip Kitaplığı adlı üçüncü bir kitaplık kullanır. Genişletme dosyalarınız için ZIP dosyaları kullanmayı planlıyorsanız uygulamanıza APK Genişletme Zip Kitaplığı'nı da eklemenizi öneririz. Daha fazla bilgi için aşağıdaki APK Genişletme Zip Kitaplığı'nı kullanma bölümüne bakın.

Kullanıcı izinlerini bildirme

Genişletme dosyalarını indirmek için İndirme Aracı Kitaplığı, uygulamanızın manifest dosyasında beyan etmeniz gereken çeşitli izinler gerektirir. Bunlar:

<manifest ...>
    <!-- Required to access Google Play Licensing -->
    <uses-permission android:name="com.android.vending.CHECK_LICENSE" />

    <!-- Required to download files from Google Play -->
    <uses-permission android:name="android.permission.INTERNET" />

    <!-- Required to keep CPU alive while downloading files
        (NOT to keep screen awake) -->
    <uses-permission android:name="android.permission.WAKE_LOCK" />

    <!-- Required to poll the state of the network connection
        and respond to changes -->
    <uses-permission
        android:name="android.permission.ACCESS_NETWORK_STATE" />

    <!-- Required to check whether Wi-Fi is enabled -->
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>

    <!-- Required to read and write the expansion files on shared storage -->
    <uses-permission
        android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    ...
</manifest>

Not: İndirme Aracı Kitaplığı varsayılan olarak API düzeyi 4'ü gerektirir ancak APK Genişletme Zip Kitaplığı için API düzeyi 5 gerekir.

İndirme aracını uygulama

İndirme işlemlerini arka planda gerçekleştirmek için İndirme Aracı Kitaplığı, DownloaderService adlı kendi Service alt sınıfını sağlar. Bu alt sınıfı genişletmeniz gerekir. DownloaderService, genişletme dosyalarını sizin için indirmenin yanı sıra şunları da yapar:

  • Gerektiğinde indirme işlemini duraklatmak (örneğin bağlantı kaybı nedeniyle) ve mümkün olduğunda indirme işlemini devam ettirmek (bağlantı sağlandığında) için cihazın ağ bağlantısındaki (CONNECTIVITY_ACTION yayını) değişiklikleri dinleyen bir BroadcastReceiver kaydeder.
  • Hizmetin sonlandırıldığı durumlarda indirme işlemini yeniden denemek için RTC_WAKEUP alarmı planlar.
  • İndirme ilerleme durumunu ve hataları veya durum değişikliklerini gösteren özel bir Notification oluşturur.
  • Uygulamanızın, indirme işlemini manuel olarak duraklatmasına ve devam ettirmesine izin verir.
  • Paylaşılan depolama biriminin eklenip kullanılabilir olduğunu, dosyaların mevcut olmadığını ve genişletme dosyaları indirilmeden önce yeterli alan bulunduğunu doğrular. Ardından bunlar doğru değilse kullanıcıyı bilgilendirir.

Tek yapmanız gereken, uygulamanızda DownloaderService sınıfını genişleten bir sınıf oluşturmak ve belirli uygulama ayrıntılarını sağlamak için üç yöntemi geçersiz kılmaktır:

getPublicKey()
Bu işlem, yayıncı hesabınız için Base64 kodlu RSA ortak anahtarı olan bir dize döndürmelidir. Bu anahtara Play Console'daki profil sayfasından ulaşabilirsiniz (Lisanslama Ayarlama bölümüne bakın).
getSALT()
Bu işlem, Policy lisansının Obfuscator oluşturmak için kullandığı rastgele bayt dizisi döndürmelidir. Takviye değer, lisanslama verilerinizin kaydedildiği karartılmış SharedPreferences dosyanızın benzersiz olmasını ve bulunabilir olmamasını sağlar.
getAlarmReceiverClassName()
Bu işlem, uygulamanızda indirme işleminin yeniden başlatılması gerektiğini belirten alarmı alması gereken BroadcastReceiver sınıf adını döndürmelidir (indirme hizmeti beklenmedik bir şekilde durursa olabilir).

Örneğin, DownloaderService uygulamasının eksiksiz bir uygulamasını burada görebilirsiniz:

Kotlin

// You must use the public key belonging to your publisher account
const val BASE64_PUBLIC_KEY = "YourLVLKey"
// You should also modify this salt
val SALT = byteArrayOf(
        1, 42, -12, -1, 54, 98, -100, -12, 43, 2,
        -8, -4, 9, 5, -106, -107, -33, 45, -1, 84
)

class SampleDownloaderService : DownloaderService() {

    override fun getPublicKey(): String = BASE64_PUBLIC_KEY

    override fun getSALT(): ByteArray = SALT

    override fun getAlarmReceiverClassName(): String = SampleAlarmReceiver::class.java.name
}

Java

public class SampleDownloaderService extends DownloaderService {
    // You must use the public key belonging to your publisher account
    public static final String BASE64_PUBLIC_KEY = "YourLVLKey";
    // You should also modify this salt
    public static final byte[] SALT = new byte[] { 1, 42, -12, -1, 54, 98,
            -100, -12, 43, 2, -8, -4, 9, 5, -106, -107, -33, 45, -1, 84
    };

    @Override
    public String getPublicKey() {
        return BASE64_PUBLIC_KEY;
    }

    @Override
    public byte[] getSALT() {
        return SALT;
    }

    @Override
    public String getAlarmReceiverClassName() {
        return SampleAlarmReceiver.class.getName();
    }
}

Uyarı: BASE64_PUBLIC_KEY değerini, yayıncı hesabınıza ait ortak anahtar olacak şekilde güncellemeniz gerekir. Anahtarı, Geliştirici Konsolu'nda, profil bilgilerinizin altında bulabilirsiniz. İndirilen öğelerinizi test ederken bile bu gereklidir.

Manifest dosyanızda hizmeti tanımlamayı unutmayın:

<app ...>
    <service android:name=".SampleDownloaderService" />
    ...
</app>

Alarm alıcısını uygulama

DownloaderService, indirme işleminin ilerlemesini izlemek ve gerekirse indirme işlemini yeniden başlatmak için uygulamanızdaki bir BroadcastReceiver öğesine Intent gönderen bir RTC_WAKEUP alarmı planlar. İndirme durumunu kontrol eden ve gerekirse indirme işlemini yeniden başlatan bir API çağırmak için BroadcastReceiver öğesini tanımlamanız gerekir.

DownloaderClientMarshaller.startDownloadServiceIfRequired() yöntemini çağırmak için onReceive() yöntemini geçersiz kılmanız yeterlidir.

Örneğin:

Kotlin

class SampleAlarmReceiver : BroadcastReceiver() {

    override fun onReceive(context: Context, intent: Intent) {
        try {
            DownloaderClientMarshaller.startDownloadServiceIfRequired(
                    context,
                    intent,
                    SampleDownloaderService::class.java
            )
        } catch (e: PackageManager.NameNotFoundException) {
            e.printStackTrace()
        }
    }
}

Java

public class SampleAlarmReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        try {
            DownloaderClientMarshaller.startDownloadServiceIfRequired(context,
                intent, SampleDownloaderService.class);
        } catch (NameNotFoundException e) {
            e.printStackTrace();
        }
    }
}

Bunun, hizmetinizin getAlarmReceiverClassName() yönteminde adını döndürmeniz gereken sınıf olduğuna dikkat edin (önceki bölüme bakın).

Manifest dosyanızda alıcıyı beyan etmeyi unutmayın:

<app ...>
    <receiver android:name=".SampleAlarmReceiver" />
    ...
</app>

İndirme işlemi başlatılıyor

Uygulamanızdaki ana etkinlik (başlatıcı simgenizle başlatılan etkinlik), genişletme dosyalarının zaten cihazda olup olmadığını doğrulamaktan ve değilse indirme işlemini başlatmaktan sorumludur.

İndirmeyi İndirme Kitaplığı'nı kullanarak başlatmak için aşağıdaki prosedürler gerekir:

  1. Dosyaların indirilip indirilmediğini kontrol edin.

    İndirme Aracı Kitaplığı, bu işleme yardımcı olması için Helper sınıfındaki bazı API'leri içerir:

    • getExpansionAPKFileName(Context, c, boolean mainFile, int versionCode)
    • doesFileExist(Context c, String fileName, long fileSize)

    Örneğin, APK Genişletme paketinde sağlanan örnek uygulama, genişletme dosyalarının cihazda zaten mevcut olup olmadığını kontrol etmek için etkinliğin onCreate() yönteminde aşağıdaki yöntemi çağırır:

    Kotlin

    fun expansionFilesDelivered(): Boolean {
        xAPKS.forEach { xf ->
            Helpers.getExpansionAPKFileName(this, xf.isBase, xf.fileVersion).also { fileName ->
                if (!Helpers.doesFileExist(this, fileName, xf.fileSize, false))
                    return false
            }
        }
        return true
    }
    

    Java

    boolean expansionFilesDelivered() {
        for (XAPKFile xf : xAPKS) {
            String fileName = Helpers.getExpansionAPKFileName(this, xf.isBase,
                xf.fileVersion);
            if (!Helpers.doesFileExist(this, fileName, xf.fileSize, false))
                return false;
        }
        return true;
    }
    

    Bu durumda her XAPKFile nesnesi, bilinen bir genişletme dosyasının sürüm numarasını ve dosya boyutunu ve ana genişletme dosyası olup olmadığına dair bir boole içerir. (Ayrıntılar için örnek uygulamanın SampleDownloaderActivity sınıfına bakın.)

    Bu yöntem "false" değerini döndürürse uygulamanın indirme işlemine başlaması gerekir.

  2. DownloaderClientMarshaller.startDownloadServiceIfRequired(Context c, PendingIntent notificationClient, Class<?> serviceClass) statik yöntemini çağırarak indirme işlemini başlatın.

    Yöntem aşağıdaki parametreleri alır:

    • context: Uygulamanızın Context.
    • notificationClient: Ana etkinliğinizi başlatmak için PendingIntent. Bu boyut, indirme işleminin ilerleme durumunu göstermek için DownloaderService tarafından oluşturulan Notification içinde kullanılır. Kullanıcı bildirimi seçtiğinde, sistem burada sağladığınız PendingIntent öğesini çağırır ve indirme durumunu gösteren etkinliği (genellikle indirme işlemini başlatan etkinlik) açar.
    • serviceClass: Hizmeti başlatmak ve gerekirse indirme işlemini başlatmak için gereken DownloaderService uygulamanız için Class nesnesi.

    Yöntem, indirmenin gerekli olup olmadığını belirten bir tam sayı döndürür. Olası değerler:

    • NO_DOWNLOAD_REQUIRED: Dosyalar zaten varsa veya devam eden bir indirme işlemi varsa döndürülür.
    • LVL_CHECK_REQUIRED: Genişletme dosyası URL'lerini edinmek için lisans doğrulaması gerekiyorsa döndürülür.
    • DOWNLOAD_REQUIRED: Genişletme dosyası URL'leri zaten biliniyorsa ancak indirilmemişse döndürülür.

    LVL_CHECK_REQUIRED ve DOWNLOAD_REQUIRED davranışı temelde aynıdır ve normalde bunlarla ilgili endişe duymanız gerekmez. startDownloadServiceIfRequired() çağıran ana etkinliğinizde, yanıtın NO_DOWNLOAD_REQUIRED olup olmadığını kontrol edebilirsiniz. Yanıt NO_DOWNLOAD_REQUIRED dışında bir yanıt olursa İndirme Aracı Kitaplığı indirme işlemine başlar ve indirme ilerleme durumunu görüntülemek için etkinlik kullanıcı arayüzünüzü güncellemeniz gerekir (sonraki adıma bakın). Yanıt NO_DOWNLOAD_REQUIRED ise dosyalar kullanılabilir ve uygulamanız başlayabilir.

    Örneğin:

    Kotlin

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
    
        // Check if expansion files are available before going any further
        if (!expansionFilesDelivered()) {
            val pendingIntent =
                    // Build an Intent to start this activity from the Notification
                    Intent(this, MainActivity::class.java).apply {
                        flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
                    }.let { notifierIntent ->
                        PendingIntent.getActivity(
                                this,
                                0,
                                notifierIntent,
                                PendingIntent.FLAG_UPDATE_CURRENT
                        )
                    }
    
    
            // Start the download service (if required)
            val startResult: Int = DownloaderClientMarshaller.startDownloadServiceIfRequired(
                    this,
                    pendingIntent,
                    SampleDownloaderService::class.java
            )
            // If download has started, initialize this activity to show
            // download progress
            if (startResult != DownloaderClientMarshaller.NO_DOWNLOAD_REQUIRED) {
                // This is where you do set up to display the download
                // progress (next step)
                ...
                return
            } // If the download wasn't necessary, fall through to start the app
        }
        startApp() // Expansion files are available, start the app
    }
    

    Java

    @Override
    public void onCreate(Bundle savedInstanceState) {
        // Check if expansion files are available before going any further
        if (!expansionFilesDelivered()) {
            // Build an Intent to start this activity from the Notification
            Intent notifierIntent = new Intent(this, MainActivity.getClass());
            notifierIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
                                    Intent.FLAG_ACTIVITY_CLEAR_TOP);
            ...
            PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,
                    notifierIntent, PendingIntent.FLAG_UPDATE_CURRENT);
    
            // Start the download service (if required)
            int startResult =
                DownloaderClientMarshaller.startDownloadServiceIfRequired(this,
                            pendingIntent, SampleDownloaderService.class);
            // If download has started, initialize this activity to show
            // download progress
            if (startResult != DownloaderClientMarshaller.NO_DOWNLOAD_REQUIRED) {
                // This is where you do set up to display the download
                // progress (next step)
                ...
                return;
            } // If the download wasn't necessary, fall through to start the app
        }
        startApp(); // Expansion files are available, start the app
    }
    
  3. startDownloadServiceIfRequired() yöntemi, NO_DOWNLOAD_REQUIRED dışında bir değer döndürürse DownloaderClientMarshaller.CreateStub(IDownloaderClient client, Class<?> downloaderService) yöntemini çağırarak IStub örneği oluşturun. IStub, etkinliğiniz ile indirme hizmeti arasında bir bağlantı sağlar. Böylece etkinliğiniz, indirme işleminin ilerleme durumu hakkında geri aramalar alır.

    IStub öğenizi CreateStub() yöntemini çağırarak örneklendirmek için IDownloaderClient arayüzünün uygulamasını ve DownloaderService uygulamanızı iletmeniz gerekir. İndirme durumunu alma ile ilgili bir sonraki bölümde IDownloaderClient arayüzü ele alınmaktadır. İndirme durumu değiştiğinde etkinlik kullanıcı arayüzünü güncelleyebilmeniz için bu arayüzü genellikle Activity sınıfınızda uygulamanız gerekir.

    startDownloadServiceIfRequired() indirme işlemini başlattıktan sonra, etkinliğinizin onCreate() yöntemi sırasında IStub örneklendirmek için CreateStub() yöntemini çağırmanızı öneririz.

    Örneğin, onCreate() için önceki kod örneğinde, startDownloadServiceIfRequired() sonucuna şu şekilde yanıt verebilirsiniz:

    Kotlin

            // Start the download service (if required)
            val startResult = DownloaderClientMarshaller.startDownloadServiceIfRequired(
                    this@MainActivity,
                    pendingIntent,
                    SampleDownloaderService::class.java
            )
            // If download has started, initialize activity to show progress
            if (startResult != DownloaderClientMarshaller.NO_DOWNLOAD_REQUIRED) {
                // Instantiate a member instance of IStub
                downloaderClientStub =
                        DownloaderClientMarshaller.CreateStub(this, SampleDownloaderService::class.java)
                // Inflate layout that shows download progress
                setContentView(R.layout.downloader_ui)
                return
            }
    

    Java

            // Start the download service (if required)
            int startResult =
                DownloaderClientMarshaller.startDownloadServiceIfRequired(this,
                            pendingIntent, SampleDownloaderService.class);
            // If download has started, initialize activity to show progress
            if (startResult != DownloaderClientMarshaller.NO_DOWNLOAD_REQUIRED) {
                // Instantiate a member instance of IStub
                downloaderClientStub = DownloaderClientMarshaller.CreateStub(this,
                        SampleDownloaderService.class);
                // Inflate layout that shows download progress
                setContentView(R.layout.downloader_ui);
                return;
            }
    

    onCreate() yöntemi geri döndüğünde, etkinliğiniz onResume() çağrısı alır. Burada, IStub üzerinde connect() yöntemini çağırarak uygulamanızın Context kodunu iletmeniz gerekir. Buna karşılık, etkinliğinizin onStop() geri çağırmasında disconnect() komutunu çağırmanız gerekir.

    Kotlin

    override fun onResume() {
        downloaderClientStub?.connect(this)
        super.onResume()
    }
    
    override fun onStop() {
        downloaderClientStub?.disconnect(this)
        super.onStop()
    }
    

    Java

    @Override
    protected void onResume() {
        if (null != downloaderClientStub) {
            downloaderClientStub.connect(this);
        }
        super.onResume();
    }
    
    @Override
    protected void onStop() {
        if (null != downloaderClientStub) {
            downloaderClientStub.disconnect(this);
        }
        super.onStop();
    }
    

    IStub üzerinden connect() çağrısı yapıldığında etkinliğiniz DownloaderService ile bağlanır. Böylece etkinliğiniz, IDownloaderClient arayüzü üzerinden indirme durumundaki değişikliklerle ilgili geri çağırmalar alır.

İndirme işleminin ilerleme durumu alınıyor

İndirme işleminin ilerleme durumuyla ilgili güncellemeleri almak ve DownloaderService ile etkileşimde bulunmak için İndirme Kitaplığı'nın IDownloaderClient arayüzünü uygulamanız gerekir. Genellikle, indirme işlemini başlatmak için kullandığınız etkinlik, indirme işleminin ilerleme durumunu görüntülemek ve hizmete istek göndermek için bu arayüzü uygulamalıdır.

IDownloaderClient için gerekli arayüz yöntemleri şunlardır:

onServiceConnected(Messenger m)
Etkinliğinizde IStub örneğini örnekledikten sonra bu yönteme bir çağrı alacaksınız. Bu çağrı, DownloaderService örneğinize bağlı bir Messenger nesnesini iletir. Hizmete istek göndermek (ör. indirme işlemlerini duraklatmak veya devam ettirmek) için DownloaderServiceMarshaller.CreateProxy() çağrısı yaparak hizmete bağlı IDownloaderService arayüzünü almanız gerekir.

Önerilen bir uygulama şu şekilde görünür:

Kotlin

private var remoteService: IDownloaderService? = null
...

override fun onServiceConnected(m: Messenger) {
    remoteService = DownloaderServiceMarshaller.CreateProxy(m).apply {
        downloaderClientStub?.messenger?.also { messenger ->
            onClientUpdated(messenger)
        }
    }
}

Java

private IDownloaderService remoteService;
...

@Override
public void onServiceConnected(Messenger m) {
    remoteService = DownloaderServiceMarshaller.CreateProxy(m);
    remoteService.onClientUpdated(downloaderClientStub.getMessenger());
}

IDownloaderService nesnesi başlatıldıktan sonra indirme hizmetine indirme işlemini duraklatmak ve devam ettirmek (requestPauseDownload() ve requestContinueDownload()) gibi komutlar gönderebilirsiniz.

onDownloadStateChanged(int newState)
İndirme durumunda, indirme işleminin başlaması veya tamamlanması gibi bir değişiklik olduğunda indirme hizmeti bunu çağırır.

newState değeri, IDownloaderClient sınıfının STATE_* sabitlerinden birinde belirtilen birkaç olası değerden biri olacaktır.

Kullanıcılarınıza faydalı bir mesaj sağlamak için Helpers.getDownloaderStringResourceIDFromState() yöntemini çağırarak her duruma karşılık gelen bir dize isteyebilirsiniz. Bu işlem, Downloader Library ile paketlenmiş dizelerden birinin kaynak kimliğini döndürür. Örneğin, "Dolaşımda olduğunuz için indirme duraklatıldı" dizesi STATE_PAUSED_ROAMING değerine karşılık gelir.

onDownloadProgress(DownloadProgressInfo progress)
İndirme hizmeti bunu çağıran DownloadProgressInfo nesnesi sunar. Bu nesne, indirme işleminin ilerleme durumu hakkında tahmini süre, mevcut hız, genel ilerleme durumu ve toplam gibi çeşitli bilgileri açıklar. Böylece indirme ilerleme durumu kullanıcı arayüzünü güncelleyebilirsiniz.

İpucu: İndirme ilerleme durumu kullanıcı arayüzünü güncelleyen bu geri çağırma örnekleri için APK Genişletme paketiyle birlikte sağlanan örnek uygulamada SampleDownloaderActivity bölümüne bakın.

IDownloaderService arayüzü için yararlı bulabileceğiniz bazı herkese açık yöntemler şunlardır:

requestPauseDownload()
İndirme işlemini duraklatır.
requestContinueDownload()
Duraklatılmış bir indirme işlemini devam ettirir.
setDownloadFlags(int flags)
Dosyaların indirilebileceği ağ türleri için kullanıcı tercihlerini ayarlar. Mevcut uygulama, bir işareti (FLAGS_DOWNLOAD_OVER_CELLULAR) destekliyor ancak başka işaretleri ekleyebilirsiniz. Varsayılan olarak bu işaret etkin değildir. Dolayısıyla, kullanıcının genişletme dosyalarını indirmek için kablosuz ağa bağlı olması gerekir. Hücresel ağ üzerinden indirmeleri etkinleştirmek için bir kullanıcı tercihi belirtebilirsiniz. Bu durumda şunları arayabilirsiniz:

Kotlin

remoteService = DownloaderServiceMarshaller.CreateProxy(m).apply {
    ...
    setDownloadFlags(IDownloaderService.FLAGS_DOWNLOAD_OVER_CELLULAR)
}

Java

remoteService
    .setDownloadFlags(IDownloaderService.FLAGS_DOWNLOAD_OVER_CELLULAR);

APKExpansionPolicy'yi kullanma

Google Play İndirme Aracı Kitaplığı'nı kullanmak yerine kendi indirme hizmetinizi oluşturmaya karar verirseniz yine de Lisans Doğrulama Kitaplığı'nda sağlanan APKExpansionPolicy'yi kullanmanız gerekir. APKExpansionPolicy sınıfı ServerManagedPolicy ile neredeyse aynıdır (Google Play Lisans Doğrulama Kitaplığı'nda mevcuttur) ancak APK genişletme dosyası yanıtı ekstraları için ek işlemler içerir.

Not: Önceki bölümde açıklandığı gibi İndirme Aracı Kitaplığı'nı kullanırsanız kitaplık, APKExpansionPolicy ile tüm etkileşimi gerçekleştirir. Böylece bu sınıfı doğrudan kullanmak zorunda kalmazsınız.

Sınıf, mevcut genişletme dosyaları hakkında gerekli bilgileri almanıza yardımcı olacak yöntemleri içerir:

  • getExpansionURLCount()
  • getExpansionURL(int index)
  • getExpansionFileName(int index)
  • getExpansionFileSize(int index)

İndirme Aracı Kitaplığı'nı kullanmıyorsanız APKExpansionPolicy aracını nasıl kullanacağınız hakkında daha fazla bilgi edinmek için Uygulamanıza Lisanslama Ekleme belgelerine bakın. Bu belgedeki gibi bir lisans politikasını nasıl uygulayacağınızı buradan öğrenebilirsiniz.

Genişletme Dosyasını Okuma

APK genişletme dosyalarınız cihaza kaydedildikten sonra, dosyalarınızı nasıl okuyacağınız, kullandığınız dosyanın türüne bağlı olarak farklılık gösterir. Genel bakış bölümünde de açıklandığı gibi, genişletme dosyalarınız istediğiniz herhangi bir dosya olabilir ancak belirli bir dosya adı biçimi kullanılarak yeniden adlandırılır ve <shared-storage>/Android/obb/<package-name>/ klasörüne kaydedilir.

Dosyalarınızı nasıl okuduğunuzdan bağımsız olarak, ilk önce her zaman harici depolama alanının okumaya uygun olup olmadığını kontrol etmeniz gerekir. Kullanıcının depolama birimini USB üzerinden bir bilgisayara eklemiş veya SD kartı çıkarmış olma ihtimali vardır.

Not: Uygulamanız başladığında, getExternalStorageState() numaralı telefonu arayarak harici depolama alanının kullanılabilir ve okunabilir olup olmadığını her zaman kontrol etmeniz gerekir. Bu, harici depolamanın durumunu temsil eden birkaç olası dizeden birini döndürür. Uygulamanız tarafından okunabilmesi için döndürülen değer MEDIA_MOUNTED olmalıdır.

Dosya adları alınıyor

Genel bakış bölümünde açıklandığı gibi, APK genişletme dosyalarınız belirli bir dosya adı biçimi kullanılarak kaydedilir:

[main|patch].<expansion-version>.<package-name>.obb

Genişletme dosyalarınızın konumunu ve adlarını almak için dosyalarınızın yolunu oluştururken getExternalStorageDirectory() ve getPackageName() yöntemlerini kullanmanız gerekir.

Aşağıda, her iki genişletme dosyanızın tam yolunu içeren bir dizi almak için uygulamanızda kullanabileceğiniz bir yöntem sunulmuştur:

Kotlin

fun getAPKExpansionFiles(ctx: Context, mainVersion: Int, patchVersion: Int): Array<String> {
    val packageName = ctx.packageName
    val ret = mutableListOf<String>()
    if (Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED) {
        // Build the full path to the app's expansion files
        val root = Environment.getExternalStorageDirectory()
        val expPath = File(root.toString() + EXP_PATH + packageName)

        // Check that expansion file path exists
        if (expPath.exists()) {
            if (mainVersion > 0) {
                val strMainPath = "$expPath${File.separator}main.$mainVersion.$packageName.obb"
                val main = File(strMainPath)
                if (main.isFile) {
                    ret += strMainPath
                }
            }
            if (patchVersion > 0) {
                val strPatchPath = "$expPath${File.separator}patch.$mainVersion.$packageName.obb"
                val main = File(strPatchPath)
                if (main.isFile) {
                    ret += strPatchPath
                }
            }
        }
    }
    return ret.toTypedArray()
}

Java

// The shared path to all app expansion files
private final static String EXP_PATH = "/Android/obb/";

static String[] getAPKExpansionFiles(Context ctx, int mainVersion,
      int patchVersion) {
    String packageName = ctx.getPackageName();
    Vector<String> ret = new Vector<String>();
    if (Environment.getExternalStorageState()
          .equals(Environment.MEDIA_MOUNTED)) {
        // Build the full path to the app's expansion files
        File root = Environment.getExternalStorageDirectory();
        File expPath = new File(root.toString() + EXP_PATH + packageName);

        // Check that expansion file path exists
        if (expPath.exists()) {
            if ( mainVersion > 0 ) {
                String strMainPath = expPath + File.separator + "main." +
                        mainVersion + "." + packageName + ".obb";
                File main = new File(strMainPath);
                if ( main.isFile() ) {
                        ret.add(strMainPath);
                }
            }
            if ( patchVersion > 0 ) {
                String strPatchPath = expPath + File.separator + "patch." +
                        mainVersion + "." + packageName + ".obb";
                File main = new File(strPatchPath);
                if ( main.isFile() ) {
                        ret.add(strPatchPath);
                }
            }
        }
    }
    String[] retArray = new String[ret.size()];
    ret.toArray(retArray);
    return retArray;
}

Bu yöntemi, uygulamanıza Context ve istediğiniz genişletme dosyasının sürümünü ileterek çağırabilirsiniz.

Genişletme dosyası sürüm numarasını belirlemenin birçok yolu vardır. Bunun basit bir yolu, genişletme dosyası adını APKExpansionPolicy sınıfının getExpansionFileName(int index) yöntemiyle sorgulayarak, sürümü indirme işlemi başladığında bir SharedPreferences dosyasına kaydetmektir. Daha sonra, genişletme dosyasına erişmek istediğinizde SharedPreferences dosyasını okuyarak sürüm kodunu alabilirsiniz.

Paylaşılan depolama alanından okuma hakkında daha fazla bilgi için Veri Depolama belgelerine bakın.

APK Genişletme Zip Kitaplığını Kullanma

Google Market Apk Genişletme paketinde, APK Genişletme Zip Kitaplığı adlı bir kitaplık bulunur (<sdk>/extras/google/google_market_apk_expansion/zip_file/ konumunda bulunur). Bu, ZIP dosyası olarak kaydedilen genişletme dosyalarınızı okumanıza yardımcı olan isteğe bağlı bir kitaplıktır. Bu kitaplığı kullanmak, ZIP genişletme dosyalarınızdaki kaynakları sanal bir dosya sistemi olarak kolayca okumanıza olanak tanır.

APK Genişletme Zip Kitaplığı aşağıdaki sınıfları ve API'leri içerir:

APKExpansionSupport
Genişletme dosyası adlarına ve ZIP dosyalarına erişmek için bazı yöntemler sunar:
getAPKExpansionFiles()
Her iki genişletme dosyasına da tam dosya yolunu döndüren, yukarıda gösterilen yöntemin aynısı.
getAPKExpansionZipFile(Context ctx, int mainVersion, int patchVersion)
Hem ana dosyanın hem de yama dosyasının toplamını temsil eden bir ZipResourceFile döndürür. Yani hem mainVersion hem de patchVersion belirtirseniz tüm verilere okuma erişimi sağlayan bir ZipResourceFile döndürülür ve yama dosyası verileri ana dosyanın üzerinde birleştirilir.
ZipResourceFile
Paylaşılan depolama alanındaki bir ZIP dosyasını temsil eder ve ZIP dosyalarınıza dayalı bir sanal dosya sistemi sağlamak için gereken tüm işleri yapar. APKExpansionSupport.getAPKExpansionZipFile() kullanarak veya ZipResourceFile ile genişletme dosyanıza yönlendiren bir örnek alabilirsiniz. Bu sınıfta yararlanabileceğiniz çeşitli yöntemler olsa da genellikle bu yöntemlerin çoğuna erişmeniz gerekmez. Birkaç önemli yöntem aşağıda verilmiştir:
getInputStream(String assetPath)
ZIP dosyasındaki bir dosyayı okuması için InputStream sağlar. assetPath, ZIP dosyası içeriklerinin köküne göre istenen dosyanın yolu olmalıdır.
getAssetFileDescriptor(String assetPath)
ZIP dosyasındaki bir dosya için AssetFileDescriptor sağlar. assetPath, ZIP dosyası içeriklerinin köküne göre istenen dosyanın yolu olmalıdır. Bu, bazı MediaPlayer API'leri gibi AssetFileDescriptor gerektiren belirli Android API'leri için yararlıdır.
APEZProvider
Çoğu uygulamanın bu sınıfı kullanması gerekmez. Bu sınıf, medya dosyalarına Uri erişimi bekleyen belirli Android API'lerine dosya erişimi sağlamak için ZIP dosyalarındaki verileri bir içerik sağlayıcısı Uri aracılığıyla birleştiren bir ContentProvider tanımlar. Örneğin, VideoView.setVideoURI() ile video oynatmak istediğinizde bu işlev yararlı olur.

Medya dosyalarının ZIP sıkıştırması atlanıyor

Medya dosyalarını depolamak için genişletme dosyalarınızı kullanıyorsanız, bir ZIP dosyası, ofset ve uzunluk kontrolleri (MediaPlayer.setDataSource() ve SoundPool.load() gibi) sağlayan Android medya oynatma çağrılarını kullanmanıza izin verir. Bunun işe yaraması için ZIP paketlerini oluştururken medya dosyaları üzerinde ek sıkıştırma yapmamanız gerekir. Örneğin, zip aracını kullanırken sıkıştırılmaması gereken dosya son eklerini belirtmek için -n seçeneğini kullanmanız gerekir:

zip -n .mp4;.ogg main_expansion media_files

ZIP dosyasından okunuyor

APK Genişletme Zip Kitaplığı'nı kullanırken ZIP dosyanızdan bir dosyayı okumak genellikle aşağıdakileri gerektirir:

Kotlin

// Get a ZipResourceFile representing a merger of both the main and patch files
val expansionFile =
        APKExpansionSupport.getAPKExpansionZipFile(appContext, mainVersion, patchVersion)

// Get an input stream for a known file inside the expansion file ZIPs
expansionFile.getInputStream(pathToFileInsideZip).use {
    ...
}

Java

// Get a ZipResourceFile representing a merger of both the main and patch files
ZipResourceFile expansionFile =
    APKExpansionSupport.getAPKExpansionZipFile(appContext,
        mainVersion, patchVersion);

// Get an input stream for a known file inside the expansion file ZIPs
InputStream fileStream = expansionFile.getInputStream(pathToFileInsideZip);

Yukarıdaki kod, her iki dosyadaki tüm dosyaların birleştirilmiş haritasından okuyarak ana genişletme dosyanızda veya yama genişletme dosyanızda bulunan herhangi bir dosyaya erişim sağlar. getAPKExpansionFile() yöntemini sağlamanız yeterlidir. android.content.Context uygulamanızın ve hem ana genişletme dosyası hem de yama genişletme dosyasının sürüm numarası.

Belirli bir genişletme dosyasından okumayı tercih ederseniz istenen genişletme dosyasının yoluyla ZipResourceFile oluşturucuyu kullanabilirsiniz:

Kotlin

// Get a ZipResourceFile representing a specific expansion file
val expansionFile = ZipResourceFile(filePathToMyZip)

// Get an input stream for a known file inside the expansion file ZIPs
expansionFile.getInputStream(pathToFileInsideZip).use {
    ...
}

Java

// Get a ZipResourceFile representing a specific expansion file
ZipResourceFile expansionFile = new ZipResourceFile(filePathToMyZip);

// Get an input stream for a known file inside the expansion file ZIPs
InputStream fileStream = expansionFile.getInputStream(pathToFileInsideZip);

Genişletme dosyalarınızda bu kitaplığı kullanma hakkında daha fazla bilgi edinmek için örnek uygulamanın, CRC kullanarak indirilen dosyaları doğrulamak üzere ek kod içeren SampleDownloaderActivity sınıfına bakın. Bu örneği kendi uygulamanızın temeli olarak kullanıyorsanız bunun için xAPKS dizisinde genişletme dosyalarınızın bayt boyutunu bildirmeniz gerektiğini unutmayın.

Genişletme Dosyalarınızı Test Etme

Uygulamanızı yayınlamadan önce test etmeniz gereken iki şey vardır: Genişletme dosyalarını okumak ve dosyaları indirmek.

Dosya okuma testi

Uygulamanızı Google Play'e yüklemeden önce, uygulamanızın paylaşılan depolama alanındaki dosyaları okuma yeteneğini test etmeniz gerekir. Tek yapmanız gereken dosyaları cihazın paylaşılan depolama alanındaki uygun konuma eklemek ve uygulamanızı başlatmaktır:

  1. Cihazınızda, Google Play'in dosyalarınızı kaydedeceği paylaşılan depolama alanında uygun dizini oluşturun.

    Örneğin, paket adınız com.example.android ise paylaşılan depolama alanında Android/obb/com.example.android/ dizinini oluşturmanız gerekir. (Paylaşılan depolama birimini eklemek ve bu dizini manuel olarak oluşturmak için test cihazınızı bilgisayarınıza takın.)

  2. Genişletme dosyalarını bu dizine manuel olarak ekleyin. Dosyalarınızı Google Play'in kullanacağı dosya adı biçimine uygun şekilde yeniden adlandırdığınızdan emin olun.

    Örneğin, dosya türünden bağımsız olarak com.example.android uygulamasının ana genişletme dosyası main.0300110.com.example.android.obb olmalıdır. Sürüm kodu istediğiniz herhangi bir değer olabilir. Şunları unutmayın:

    • Ana genişletme dosyası her zaman main ile, yama dosyası ise patch ile başlar.
    • Paket adı her zaman dosyanın Google Play'de eklendiği APK'nın adıyla eşleşir.
  3. Genişletme dosyaları artık cihazda olduğuna göre genişletme dosyalarınızı test etmek için uygulamanızı yükleyip çalıştırabilirsiniz.

Genişletme dosyalarının ele alınmasıyla ilgili bazı hatırlatmaları aşağıda bulabilirsiniz:

  • .obb genişletme dosyalarını silmeyin veya yeniden adlandırmayın (verileri farklı bir konumda paketinden açmış olsanız bile). Bu işlem, Google Play'in (veya uygulamanızın) genişletme dosyasını sürekli olarak indirmesine neden olur.
  • Diğer verileri obb/ dizininize kaydetmeyin. Bazı verileri paketinden çıkarmanız gerekiyorsa bunları getExternalFilesDir() tarafından belirtilen konuma kaydedin.

Dosya indirmelerini test etme

Uygulamanızın bazen ilk açıldığında genişletme dosyalarını manuel olarak indirmesi gerektiğinden, uygulamanızın URL'leri başarılı bir şekilde sorgulayabileceğinden, dosyaları indirebileceğinden ve cihaza kaydedebileceğinden emin olmak için bu işlemi test etmeniz önemlidir.

Uygulamanızın manuel indirme prosedürünü uygulayıp uygulamadığını test etmek için uygulamanızı dahili test kanalına yayınlayabilirsiniz. Böylece uygulamanız yalnızca yetkili test kullanıcılarına sunulur. Her şey beklendiği gibi çalışırsa uygulamanız, ana etkinlik başlar başlamaz genişletme dosyalarını indirmeye başlayacaktır.

Not: Önceden, bir uygulamayı yayınlanmamış "taslak" sürümünü yükleyerek test edebiliyordunuz. Bu işlev artık desteklenmemektedir. Bunun yerine dahili, kapalı veya açık test kanalına yayınlamanız gerekir. Daha fazla bilgi için Taslak Uygulamalar Daha Uzun Zaman Desteklenmez bölümüne bakın.

Uygulamanız güncelleniyor

Google Play'de genişletme dosyaları kullanmanın en iyi avantajlarından biri, orijinal öğelerin tümünü yeniden indirmeden uygulamanızı güncelleyebilmenizdir. Google Play her APK ile iki genişletme dosyası sağlamanıza izin verdiğinden, ikinci dosyayı güncellemeleri ve yeni öğeleri sağlayan bir "yama" olarak kullanabilirsiniz. Bu işlem, kullanıcılar için büyük ve pahalı olabilecek ana genişletme dosyasını yeniden indirme ihtiyacını önler.

Yama genişletme dosyası teknik olarak ana genişletme dosyasıyla aynıdır ve Android sistemi veya Google Play, ana ve yama genişletme dosyalarınız arasında fiili yama uygulamaz. Uygulama kodunuz gerekli tüm yamaları gerçekleştirmelidir.

Genişletme dosyalarınız olarak ZIP dosyalarını kullanırsanız APK Genişletme paketinde bulunan APK Genişletme Zip Kitaplığı, yama dosyanızı ana genişletme dosyasıyla birleştirme özelliğini içerir.

Not: Yalnızca yama genişletme dosyasında değişiklik yapmanız gerekse bile, Google Play'in güncelleme yapabilmesi için APK'yı güncellemeniz gerekir. Uygulamada kod değişikliği yapmanız gerekmiyorsa manifestteki versionCode değerini güncellemeniz yeterlidir.

Play Console'da APK ile ilişkili ana genişletme dosyasını değiştirmediğiniz sürece, uygulamanızı daha önce yüklemiş olan kullanıcılar ana genişletme dosyasını indirmezler. Mevcut kullanıcılar yalnızca güncellenen APK'yı ve yeni yama genişletme dosyasını (önceki ana genişletme dosyasını tutarak) alırlar.

Genişletme dosyalarında yapılan güncellemelerle ilgili olarak unutulmaması gereken birkaç sorun aşağıda belirtilmiştir:

  • Uygulamanız için aynı anda yalnızca iki genişletme dosyası olabilir. Bir ana genişletme dosyası ve bir yama genişletme dosyası. Bir dosya güncellenirken, Google Play önceki sürümü siler (manuel güncelleme yaparken uygulamanız da olmalıdır).
  • Bir yama genişletme dosyası eklerken, Android sistemi aslında uygulamanıza veya ana genişletme dosyanıza yama uygulamaz. Uygulamanızı, yama verilerini destekleyecek şekilde tasarlamanız gerekir. Ancak APK Genişletme paketinde, ZIP dosyalarının genişletme dosyası olarak kullanılması için bir kitaplık bulunur. Bu kitaplık, yama dosyasındaki verileri ana genişletme dosyasıyla birleştirir. Böylece tüm genişletme dosyası verilerini kolayca okuyabilirsiniz.