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 yeterlidir. Ancak bazı uygulamalar, yüksek kaliteli grafikler, medya dosyaları veya diğer büyük öğeler için daha fazla alan gerektirir. Daha önce, uygulamanızın sıkıştırılmış indirme boyutu 100 MB'ı aşıyorsa kullanıcı uygulamayı açtığında ek kaynakları kendiniz barındırmanız ve indirmeniz gerekiyordu. Ek dosyaları barındırmak ve sunmak maliyetli olabilir ve kullanıcı deneyimi genellikle idealden daha az olur. Bu süreci sizin için kolaylaştırmak ve kullanıcılar için daha keyifli hale getirmek amacıyla Google Play, APK'nızı tamamlayan iki büyük genişletme dosyası eklemenize olanak tanır.

Google Play, uygulamanızın genişletme dosyalarını barındırır ve cihazda ücretsiz olarak sunar. Genişletme dosyaları, uygulamanızın erişebileceği cihazın paylaşılan depolama alanına (SD kart veya takılabilir USB bölümü; "harici" depolama alanı olarak da bilinir) kaydedilir. Google Play, çoğu cihazda genişletme dosyalarını APK'yı indirdiği sırada indirir. Böylece uygulamanız, kullanıcı tarafından ilk kez açıldığında ihtiyaç duyduğu her şeye sahip olur. Ancak bazı durumlarda uygulamanızın dosyaları Google Play'den indirmesi gerekir.

Genişletme dosyalarını kullanmak istemiyorsanız ve uygulamanızın sıkıştırılmış indirme boyutu 100 MB'tan büyükse uygulamanızı Android uygulama paketleri kullanarak yüklemeniz gerekir. Bu yöntem, sıkıştırılmış indirme boyutunun 500 MB'a kadar olmasına olanak tanır. Ayrıca, uygulama paketleri kullanıldığında APK oluşturma ve imzalama işlemleri Google Play'e bırakıldığından kullanıcılar, uygulamanızı çalıştırmak için gereken kod ve kaynakları içeren optimize edilmiş APK'lar indirir. Birden fazla APK veya genişletme dosyası oluşturmanız, imzalamanız ve yönetmeniz gerekmez. Kullanıcılar ise daha küçük ve daha optimize edilmiş indirmeler elde eder.

Genel Bakış

Google Play Console'u kullanarak bir APK yüklediğinizde, APK'ya bir veya iki genişletme dosyası ekleyebilirsiniz. Her dosya en fazla 2 GB olabilir ve istediğiniz biçimde olabilir. Ancak indirme sırasında bant genişliğinden tasarruf etmek 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 ihtiyaç duyduğu 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 şekilde kullanabilirsiniz ancak ana genişletme dosyasının birincil öğeleri sunmasını ve nadiren güncellenmesini öneririz. Yama genişletme dosyası daha küçük olmalı ve "yama taşıyıcı" olarak işlev görmeli, her büyük sürümde veya gerektiğinde güncellenmelidir.

Ancak uygulama güncellemeniz yalnızca yeni bir yama genişletme dosyası gerektirse bile manifestte güncellenmiş bir 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ı, anlamsal olarak ana genişletme dosyasıyla aynıdır. Her dosyayı istediğiniz şekilde kullanabilirsiniz.

Dosya adı biçimi

Yüklediğiniz her genişletme dosyası, seçtiğiniz herhangi bir biçimde (ZIP, PDF, MP4 vb.) olabilir. Bir dizi kaynak dosyasını ve bu dizi için sonraki yamaları kapsüllemek ve şifrelemek üzere JOBB aracını da kullanabilirsiniz. Dosya türü ne olursa olsun, 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 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 ilişkilendirildiği 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 yüklediğinizde uygulanan sürümü korur. Bu nedenle, "ilk" kelimesi vurgulanır.

<package-name>
Uygulamanızın Java tarzı 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. Ana genişletme dosyası yüklerseniz dosyanın adı şu şekilde değiştirilir:

main.314159.com.example.app.obb

Depolama konumu

Google Play, genişletme dosyalarınızı bir cihaza indirdiğinde bunları sistemin paylaşılan depolama konumuna kaydeder. Düzgün çalışması için genişletme dosyalarını silmemeniz, taşımamanız veya yeniden adlandırmamanız gerekir. Uygulamanızın indirme işlemini Google Play'den kendisinin yapması gerektiği durumlarda 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>/

Bu dizinde hiçbir zaman uygulama başına ikiden fazla genişletme dosyası bulunmaz. Biri ana genişletme dosyası, diğeri ise yama genişletme dosyasıdır (gerekirse). 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, harici depolama izni olmadan OBB genişletme dosyalarını okuyabilir. Ancak Android 6.0 (API düzeyi 23) ve sonraki sürümlerin bazı uygulamalarında yine izin gerekir. Bu nedenle, uygulama manifestinde READ_EXTERNAL_STORAGE iznini beyan etmeniz ve çalışma zamanında aşağıdaki şekilde izin istemeniz gerekir:

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

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

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 ardından OBB genişletme dosyalarını silmeyin ve açılan verileri aynı dizine kaydetmeyin. Açtığınız dosyaları getExternalFilesDir() tarafından belirtilen dizine kaydetmeniz gerekir. Ancak mümkünse verileri açmanızı gerektirmek yerine doğrudan dosyadan okumanıza olanak tanıyan bir genişletme dosyası biçimi kullanmanız en iyisidir. Örneğin, verilerinizi doğrudan ZIP dosyasından okuyan APK Expansion Zip Library adlı bir kitaplık projesi sağladık.

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

İpucu: Medya dosyalarını ZIP olarak paketliyorsanız ZIP'inizi açmanıza gerek kalmadan, dosyalar üzerinde ofset ve uzunluk kontrolleri (ör. MediaPlayer.setDataSource() ve SoundPool.load()) ile medya oynatma çağrılarını kullanabilirsiniz. Bu özelliğin çalışması için ZIP paketlerini oluştururken medya dosyalarında ek sıkıştırma işlemi yapmamalısınız. Ö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 genişletme dosyalarınızı APK'yı cihaza indirdiği sırada indirip kaydeder. Ancak bazı durumlarda Google Play, genişletme dosyalarını indiremez veya kullanıcı, daha önce indirilmiş genişletme dosyalarını silmiş olabilir. Bu durumlarla başa çıkmak için uygulamanız, ana etkinlik başladığında Google Play tarafından sağlanan bir URL'yi kullanarak dosyaları kendi başına indirebilmelidir.

Yüksek düzeyde indirme işlemi şu şekilde görünür:

  1. Kullanıcı, uygulamanızı Google Play'den yüklemeyi seçer.
  2. Google Play, genişletme dosyalarını indirebiliyorsa (çoğu cihazda bu durum geçerlidir) 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 cihaza kaydedilip kaydedilmediğini kontrol etmelidir.
    1. Evetse uygulamanız kullanıma hazırdır.
    2. Hayırsa uygulamanız, genişletme dosyalarını Google Play'den HTTP üzerinden indirmelidir. Uygulamanız, Google Play'in uygulama lisanslama hizmetini kullanarak Google Play istemcisine istek göndermelidir. Bu hizmet, her genişletme dosyasının adı, dosya boyutu ve URL'si ile yanıt verir. Bu bilgilerle dosyaları indirip uygun depolama konumuna kaydedebilirsiniz.

Dikkat: Uygulamanız başlatıldığında dosyalar cihazda yoksa Google Play'den genişletme dosyalarını indirmek için gerekli kodu eklemeniz çok önemlidir. Genişletme Dosyalarını İndirme ile ilgili aşağıdaki bölümde belirtildiği gibi, bu süreci büyük ölçüde basitleştiren ve indirme işlemini sizden en az miktarda kodla bir hizmetten gerçekleştiren bir kitaplık kullanıma sunduk.

Geliştirme yapılacaklar listesi

Uygulamanızla genişletme dosyalarını kullanmak için yapmanız gereken görevlerin özetini aşağıda bulabilirsiniz:

  1. Öncelikle uygulamanızın sıkıştırılmış indirme boyutunun 100 MB'tan fazla olması gerekip gerekmediğini belirleyin. Alan çok değerlidir ve toplam indirme boyutunuzu mümkün olduğunca küçük tutmanız gerekir. Uygulamanız, birden fazla ekran yoğunluğu için grafik öğelerinizin birden fazla sürümünü sağlamak amacıyla 100 MB'tan fazla alan kullanıyorsa bunun yerine birden fazla APK yayınlamayı düşünebilirsiniz. Bu APK'ların her biri yalnızca hedeflediği ekranlar için gereken öğeleri içerir. Google Play'de yayınlarken en iyi sonuçları elde etmek için uygulamanızın derlenmiş kod ve kaynaklarının tamamını içeren ancak APK oluşturma ve imzalama işlemini Google Play'e bırakan bir Android App Bundle yükleyin.
  2. APK'nızdan hangi uygulama kaynaklarının ayrılacağını belirleyin ve bunları ana genişletme dosyası olarak kullanılacak bir dosyada paketleyin.

    Normalde, ana genişletme dosyasında güncelleme yaparken 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şarsa öğelerinizin geri kalanı 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ı ve bunları APK Expansion Zip Library'yi kullanarak okumanızı öneririz.

  4. Uygulamanızın ana etkinliğine, başlatma sırasında genişletme dosyalarının cihazda olup olmadığını kontrol eden bir mantık ekleyin. Dosyalar cihazda değilse genişletme dosyalarının URL'lerini istemek için Google Play'in uygulama lisanslama hizmetini kullanın, ardından bu dosyaları indirip kaydedin.

    Yazmanız gereken kod miktarını büyük ölçüde azaltmak ve indirme sırasında iyi bir kullanıcı deneyimi sağlamak için indirme davranışınızı uygulamak üzere Downloader Library'yi kullanmanızı öneririz.

    Kitaplığı kullanmak yerine kendi indirme hizmetinizi oluşturursanız genişletme dosyalarının adını değiştirmemeniz ve bunları uygun depolama konumuna kaydetmeniz gerektiğini unutmayın.

Uygulama geliştirme işlemini tamamladıktan sonra Genişletme Dosyalarınızı Test Etme rehberini inceleyin.

Kurallar ve Sınırlamalar

APK genişletme dosyaları ekleme özelliği, uygulamanızı Play Console'u kullanarak yüklediğinizde kullanılabilir. Uygulamanızı ilk kez yüklerken veya genişletme dosyalarını kullanan bir uygulamayı güncellerken aşağıdaki kurallara ve sınırlamalara dikkat etmeniz gerekir:

  1. Her genişletme dosyası en fazla 2 GB olabilir.
  2. Google Play'den genişletme dosyalarınızı indirmek için kullanıcının uygulamanızı Google Play'den edinmiş olması gerekir. Uygulama başka yollarla yüklendiyse Google Play, genişletme dosyalarınızın URL'lerini sağlamaz.
  3. İndirme işlemini uygulamanızın içinden gerçekleştirirken Google Play'in her dosya için sağladığı URL, her indirme için benzersizdir ve her biri, uygulamanıza verildikten kısa süre sonra sona erer.
  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, başlangıçta ilişkilendirildiği APK'nın aldığı sürümü korur.
  5. Farklı cihazlar için farklı genişletme dosyaları sağlamak amacıyla genişletme dosyalarını birden fazla APK ile birlikte kullanıyorsanız yine de benzersiz bir versionCode değeri sağlamak ve her APK için farklı filtreler bildirmek üzere her cihaz için ayrı APK'lar yüklemeniz gerekir.
  6. Yalnızca genişletme dosyalarını değiştirerek uygulamanızda güncelleme yapamazsı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. obb/ Dizinine başka veri kaydetmeyin. Bazı verileri açmanız gerekiyorsa bunları getExternalFilesDir() tarafından belirtilen konuma kaydedin.
  8. .obb genişletme dosyasını silmeyin veya yeniden adlandırmayın (güncelleme yapmıyorsanız). Bu durumda Google Play (veya uygulamanız) genişletme dosyasını tekrar tekrar indirir.
  9. Genişletme dosyasını manuel olarak güncellerken önceki genişletme dosyasını silmeniz gerekir.

Genişletme dosyalarını indirme

Google Play, çoğu durumda APK'yı yüklerken veya güncellerken genişletme dosyalarınızı aynı anda indirip cihaza kaydeder. Bu sayede, uygulamanız ilk kez başlatıldığında genişletme dosyaları kullanılabilir. Ancak bazı durumlarda uygulamanızın, Google Play'in uygulama lisanslama hizmetinden gelen yanıtta size sağlanan bir URL'den isteyerek genişletme dosyalarını kendisinin indirmesi gerekir.

Genişletme dosyalarınızı indirmek için gereken temel mantık şöyledir:

  1. Uygulamanız başladığında, paylaşılan depolama alanındaki (Android/obb/<package-name>/ dizininde) genişletme dosyalarını arayın.
    1. Genişletme dosyaları varsa her şey hazır demektir ve uygulamanız devam edebilir.
    2. Genişletme dosyaları 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 istekte bulunun.
      2. Genişletme dosyalarını indirmek ve kaydetmek için Google Play'in sağladığı 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ı aynen kullanmanız gerekir.

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

Uygulamanız ücretsizse (ücretli bir uygulama değilse) muhtemelen uygulama lisanslama hizmetini kullanmamışsınızdır. Bu hizmet, öncelikle uygulamanızın lisanslama politikalarını uygulamanız ve kullanıcının uygulamanızı kullanma hakkına sahip olmasını (Google Play'de uygulamanız için yasal olarak ödeme yapmış olması) sağlamanız için tasarlanmıştır. Genişletme dosyası işlevini kolaylaştırmak için lisanslama hizmeti, Google Play'de barındırılan uygulamanızın genişletme dosyalarının URL'sini içeren bir yanıt sağlayacak şekilde geliştirildi. Bu nedenle, uygulamanız kullanıcılar için ücretsiz olsa bile APK genişletme dosyalarını kullanmak üzere 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ın ücretsiz olup olmadığına bakılmaksızın, Google Play genişletme dosyası URL'lerini yalnızca kullanıcı uygulamanızı Google Play'den edinmişse döndürür.

LVL'ye ek olarak, genişletme dosyalarını HTTP bağlantısı üzerinden indirip cihazın paylaşılan depolama alanında uygun konuma kaydeden bir kod grubuna ihtiyacınız vardır. Bu prosedürü uygulamanıza eklerken dikkate almanız gereken birkaç sorun vardır:

  • Cihazda genişletme dosyaları için yeterli alan olmayabilir. Bu nedenle, indirme işlemine başlamadan önce kontrol etmeli ve yeterli alan yoksa kullanıcıyı uyarmalısınız.
  • Kullanıcı etkileşiminin engellenmesini önlemek ve indirme işlemi tamamlanırken kullanıcının uygulamanızdan çıkmasına izin vermek için dosya indirme işlemleri arka plan hizmetinde yapılmalıdır.
  • İstek ve indirme sırasında çeşitli hatalar oluşabilir. Bu hataları düzgün bir şekilde ele almanız gerekir.
  • İndirme sırasında ağ bağlantısı değişebilir. Bu nedenle, bu tür değişiklikleri yönetmeniz ve indirme işlemi kesintiye uğrarsa mümkün olduğunda devam ettirmeniz gerekir.
  • İndirme işlemi arka planda gerçekleşirken indirme ilerleme durumunu gösteren, tamamlandığında kullanıcıyı bilgilendiren ve seçildiğinde kullanıcıyı uygulamanıza geri götüren bir bildirim sağlamanız gerekir.

Bu işi sizin için kolaylaştırmak amacıyla Downloader Library'yi geliştirdik. Bu kitaplık, lisanslama hizmeti aracılığıyla genişletme dosyası URL'lerini ister, genişletme dosyalarını indirir, yukarıda listelenen tüm görevleri yerine getirir ve hatta etkinliğinizin duraklatılıp indirme işleminin devam ettirilmesine olanak tanır. Uygulamanıza Downloader Library'yi ve birkaç kod kancası eklediğinizde, genişletme dosyalarını indirmek için gereken işin neredeyse tamamı sizin için kodlanmış olur. Bu nedenle, sizin tarafınızdan en az çabayla en iyi kullanıcı deneyimini sunmak için genişletme dosyalarınızı indirmek üzere Downloader Library'yi kullanmanızı öneririz. Aşağıdaki bölümlerde, kitaplığı uygulamanıza nasıl entegre edeceğiniz açıklanmaktadır.

Google Play URL'lerini kullanarak genişletme dosyalarını indirmek için kendi çözümünüzü geliştirmeyi tercih ederseniz lisans isteğinde bulunmak üzere Uygulama Lisanslama dokümanlarını incelemeniz, ardından yanıt ekstralarından genişletme dosyası adlarını, boyutlarını ve URL'lerini almanız gerekir. Lisanslama politikası olarak APKExpansionPolicy sınıfını (Lisans Doğrulama Kitaplığı'nda bulunur) kullanmanız gerekir. Bu sınıf, lisanslama hizmetinden genişletme dosyası adlarını, boyutlarını ve URL'lerini alır.

İndirme Kitaplığı hakkında

Uygulamanızla APK genişletme dosyalarını kullanmak ve sizin tarafınızdan minimum çabayla en iyi kullanıcı deneyimini sağlamak için Google Play APK Genişletme Kitaplığı paketinde bulunan Downloader Library'yi kullanmanızı öneririz. Bu kitaplık, genişletme dosyalarınızı arka plan hizmetinde indirir, indirme durumuyla ilgili kullanıcı bildirimi gösterir, ağ bağlantısı kaybını işler, mümkün olduğunda indirme işlemine devam eder ve daha fazlasını yapar.

Downloader Library'yi kullanarak genişletme dosyası indirmelerini uygulamak için yapmanız gerekenler:

  • Her biri yalnızca birkaç satır kod gerektiren özel bir Service alt sınıfı ve BroadcastReceiver alt sınıfı genişletin.
  • Ana etkinliğinize, genişletme dosyalarının daha önce indirilip indirilmediğini kontrol eden, indirilmediyse indirme işlemini başlatan ve bir ilerleme durumu kullanıcı arayüzü gösteren bir mantık ekleyin.
  • Ana etkinliğinizde, indirme ilerleme durumuyla ilgili güncellemeleri alan birkaç yöntem içeren bir geri çağırma arayüzü uygulayın.

Aşağıdaki bölümlerde, Downloader Library'yi kullanarak uygulamanızı nasıl ayarlayacağınız açıklanmaktadır.

İndirici Kitaplığı'nı kullanmaya hazırlanma

İndirme Kitaplığı'nı kullanmak için SDK Manager'dan iki paket indirmeniz ve uygulamanıza uygun kitaplıkları eklemeniz gerekir.

İlk olarak Android SDK Manager'ı (Tools > SDK Manager) açın ve Appearance & Behavior > System Settings > Android SDK (Görünüm ve Davranış > Sistem Ayarları > Android SDK) bölümünde SDK Tools (SDK Araçları) sekmesini seçerek şunları belirleyin ve indirin:

  • Google Play Licensing Library paketi
  • Google Play APK Expansion Library paketi

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

  1. Dosya > Yeni > Yeni Modül'ü seçin.
  2. Yeni Modül Oluştur penceresinde Android Kitaplığı'nı ve ardından Sonraki'yi seçin.
  3. "Google Play License Library" ve "Google Play Downloader Library" gibi bir uygulama/kitaplık adı belirtin, Minimum SDK düzeyi'ni seçin, ardından Bitir'i seçin.
  4. Dosya > Proje Yapısı'nı seçin.
  5. Özellikler sekmesini seçin ve Kitaplık Deposu bölümünde <sdk>/extras/google/ dizinindeki 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 Kitaplığı, Lisans Doğrulama Kitaplığı'na bağlıdır. Lisans Doğrulama Kitaplığı'nı, İndirme Kitaplığı'nın proje özelliklerine eklediğinizden emin olun.

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

  1. Dizinleri <sdk>/tools/ dizini olarak değiştirin.
  2. Hem LVL hem de Downloader Library'yi projenize eklemek için android update project komutunu --library seçeneğiyle birlikte çalıştırı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 Google Play'den genişletme dosyalarını indirme özelliğini hızlıca entegre edebilirsiniz. Genişletme dosyaları için seçtiğiniz biçim ve bunları paylaşılan depolama alanından nasıl okuduğunuz, uygulama ihtiyaçlarınıza göre değerlendirmeniz gereken ayrı bir uygulamadır.

İpucu: APK genişletme paketi, bir uygulamada Downloader Library'nin nasıl kullanılacağını gösteren örnek bir uygulama içerir. Örnekte, APK genişletme paketinde bulunan APK Expansion Zip Library adlı üçüncü bir kitaplık kullanılır. Genişletme dosyalarınız için ZIP dosyalarını 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 beyan etme

Genişletme dosyalarını indirmek için Downloader Library'nin birkaç izne ihtiyacı vardır. Bu izinleri uygulamanızın manifest dosyasında belirtmeniz gerekir. Bu türler şunlardır:

<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: İndirici Kitaplığı varsayılan olarak API düzeyi 4'ü gerektirir ancak APK Genişletme Zip Kitaplığı için API düzeyi 5 gerekir.

İndirme hizmetini uygulama

İndirme işlemini arka planda gerçekleştirmek için İndirici Kitaplığı, genişletmeniz gereken Service adlı kendi alt sınıfını sağlar.DownloaderService DownloaderService, genişletme dosyalarını sizin için indirmenin yanı sıra:

  • Gerekli olduğunda (ör. bağlantı kaybı nedeniyle) indirmeyi duraklatmak ve mümkün olduğunda (bağlantı sağlandığında) indirmeye devam etmek için cihazın ağ bağlantısındaki değişiklikleri (CONNECTIVITY_ACTION yayını) 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 indirmeyi manuel olarak duraklatıp devam ettirmesine olanak tanır.
  • Paylaşılan depolama alanının bağlı ve kullanılabilir olduğunu, dosyaların mevcut olmadığını ve yeterli alan olduğunu, genişletme dosyalarını indirmeden önce doğrular. Ardından, bunlardan herhangi biri doğru değilse kullanıcıyı bilgilendirir.

Yapmanız gereken tek şey, 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, yayıncı hesabınızın Base64 kodlu RSA ortak anahtarını içeren bir dize döndürmelidir. Bu anahtara Play Console'daki profil sayfasından erişebilirsiniz (bkz. Lisanslama için Ayarları Yapma).
getSALT()
Bu, lisanslama hizmetinin Policy Obfuscator oluşturmak için kullandığı rastgele bayt dizisini döndürmelidir. Salt, lisanslama verilerinizin kaydedildiği karartılmış SharedPreferencesdosyanın benzersiz ve bulunamaz olmasını sağlar.
getAlarmReceiverClassName()
Bu, uygulamanızda indirme işleminin yeniden başlatılması gerektiğini belirten alarmı alması gereken BroadcastReceiver sınıf adını döndürmelidir (bu durum, indirici hizmeti beklenmedik bir şekilde durduğunda yaşanabilir).

Örneğin, DownloaderService için eksiksiz bir uygulama aşağıda verilmiştir:

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 herkese açık anahtar olacak şekilde güncellemeniz gerekir. Anahtarı, profil bilgilerinizin bulunduğu Developer Console'da bulabilirsiniz. Bu, indirmelerinizi test ederken bile gereklidir.

Hizmeti manifest dosyanızda bildirmeyi unutmayın:

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

Alarm alıcısını uygulama

Dosya indirme işlemlerinin ilerleme durumunu izlemek ve gerekirse indirme işlemini yeniden başlatmak için DownloaderService, uygulamanızdaki BroadcastReceiver öğesine Intent gönderen bir RTC_WAKEUP alarmı planlar. İndirme işleminin durumunu kontrol eden ve gerekirse yeniden başlatan Downloader Library'den bir API çağırmak için BroadcastReceiver 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();
        }
    }
}

Bu sınıfın, hizmetinizin getAlarmReceiverClassName() yönteminde adı döndürmeniz gereken sınıf olduğunu unutmayın (önceki bölüme bakın).

Alıcıyı manifest dosyanızda beyan etmeyi unutmayın:

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

İndirme işlemini başlatma

Uygulamanızdaki ana etkinlik (başlatıcı simgenizle başlatılan etkinlik), genişletme dosyalarının cihazda olup olmadığını doğrulama ve yoksa indirme işlemini başlatma sorumluluğundadır.

İndirici kitaplığını kullanarak indirme işlemini başlatmak için aşağıdaki prosedürler gerekir:

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

    Downloader Library, bu süreçte yardımcı olmak için Helper sınıfında bazı API'ler içerir:

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

    Örneğin, Apk Expansion paketinde sağlanan örnek uygulama, genişletme dosyalarının cihazda 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ığını belirten bir boole değerini içerir. (Ayrıntılar için örnek uygulamanın SampleDownloaderActivity sınıfına bakın.)

    Bu yöntem yanlış 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, indirme 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 ilerleme durumunu gösteren etkinliği (genellikle indirmeyi başlatan etkinliğin aynısı) açar.
    • serviceClass: DownloaderService uygulamanız için Class nesnesi. Hizmeti başlatmak ve gerekirse indirme işlemine başlamak için gereklidir.

    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 indirme işlemi devam ediyorsa döndürülür.
    • LVL_CHECK_REQUIRED: Genişletme dosyası URL'lerinin alınması 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 için davranış temelde aynıdır ve normalde bu konuda endişelenmenize gerek yoktur. startDownloadServiceIfRequired() işlevini çağıran ana etkinliğinizde, yanıtın NO_DOWNLOAD_REQUIRED olup olmadığını kontrol etmeniz yeterlidir. Yanıt NO_DOWNLOAD_REQUIRED dışında bir değerse, İndirici Kitaplığı indirme işlemine başlar ve indirme ilerleme durumunu göstermek için etkinlik kullanıcı arayüzünüzü güncellemeniz gerekir (sonraki adıma bakın). Yanıt evet NO_DOWNLOAD_REQUIRED ise dosyalar kullanılabilir ve uygulamanız başlatılabilir.

    Ö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ürdüğünde DownloaderClientMarshaller.CreateStub(IDownloaderClient client, Class<?> downloaderService) işlevini çağırarak IStub örneği oluşturun. IStub, etkinliğiniz ile indirici hizmeti arasında bir bağlama sağlar. Böylece etkinliğiniz, indirme ilerleme durumu hakkında geri aramalar alır.

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

    startDownloadServiceIfRequired() indirme işlemini başlattıktan sonra, etkinliğinizin onCreate() yöntemi sırasında IStub öğenizi oluşturmak 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 döndükten sonra etkinliğiniz onResume() yöntemine bir çağrı alır. Bu noktada, IStub üzerinde connect() yöntemini çağırıp uygulamanızın Context değerini iletmeniz gerekir. Bunun tersine, etkinliğinizin onStop() geri çağırmasında disconnect() yöntemini çağırmalısınız.

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

    connect() işlevini IStub üzerinde çağırmak, etkinliğinizi DownloaderService ile bağlar. Böylece etkinliğiniz, IDownloaderClient arayüzü üzerinden indirme durumundaki değişikliklerle ilgili geri aramalar alır.

İndirme işleminin ilerleme durumu

İndirme ilerleme durumuyla ilgili güncellemeler almak ve DownloaderService ile etkileşimde bulunmak için Downloader Library'nin IDownloaderClient arayüzünü uygulamanız gerekir. Genellikle, indirme işlemini başlatmak için kullandığınız etkinlik, indirme ilerleme durumunu göstermek 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 öğesini oluşturduktan sonra, DownloaderService örneğinize bağlı bir Messenger nesnesi ileten bu yönteme bir çağrı alırsınız. Hizmete istek göndermek için (ör. indirmeleri duraklatmak ve devam ettirmek) DownloaderServiceMarshaller.CreateProxy() işlevini çağırarak hizmete bağlı IDownloaderService arayüzünü almanız gerekir.

Önerilen uygulama aşağıdaki gibi 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ığında, indirme hizmetine komut gönderebilirsiniz. Örneğin, indirme işlemini duraklatabilir ve devam ettirebilirsiniz (requestPauseDownload() ve requestContinueDownload()).

onDownloadStateChanged(int newState)
İndirme hizmeti, indirme durumunda değişiklik olduğunda (ör. indirme başladığında veya tamamlandığında) bu yöntemi çağırır.

newState değeri, IDownloaderClient sınıfının STATE_* sabitlerinden biri tarafından belirtilen olası değerlerden biri olacaktır.

Kullanıcılarınıza faydalı bir mesaj sağlamak için Helpers.getDownloaderStringResourceIDFromState() işlevini çağırarak her durum için ilgili bir dize isteğinde bulunabilirsiniz. Bu işlev, Downloader Library ile birlikte gelen dizelerden birinin kaynak kimliğini döndürür. Örneğin, "Dolaşımda olduğunuz için indirme duraklatıldı" dizesi STATE_PAUSED_ROAMING ile eşleşir.

onDownloadProgress(DownloadProgressInfo progress)
İndirme hizmeti, tahmini kalan süre, mevcut hız, genel ilerleme durumu ve toplam boyut gibi indirme ilerleme durumuyla ilgili çeşitli bilgileri açıklayan bir DownloadProgressInfo nesnesi sunmak için bu yöntemi çağırır. 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 Expansion paketiyle birlikte verilen örnek uygulamadaki SampleDownloaderActivity bölümüne bakın.

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

requestPauseDownload()
İndirme işlemini duraklatır.
requestContinueDownload()
Duraklatılmış bir indirme işlemine devam eder.
setDownloadFlags(int flags)
Dosyaların indirilmesinin uygun olduğu ağ türleriyle ilgili kullanıcı tercihlerini ayarlar. Mevcut uygulama bir işaret olan FLAGS_DOWNLOAD_OVER_CELLULAR işaretini destekler ancak başka işaretler de ekleyebilirsiniz. Bu işaret varsayılan olarak etkin değildir. Bu nedenle, 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 kullanıcı tercihi sunabilirsiniz. Bu durumda şu numaraları arayabilirsiniz:

Kotlin

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

Java

remoteService
    .setDownloadFlags(IDownloaderService.FLAGS_DOWNLOAD_OVER_CELLULAR);

APKExpansionPolicy kullanma

Google Play Downloader Library'yi kullanmak yerine kendi indirici hizmetinizi oluşturmaya karar verirseniz Lisans Doğrulama Kitaplığı'nda sağlanan APKExpansionPolicy'ı kullanmaya devam etmeniz gerekir. APKExpansionPolicy sınıfı, ServerManagedPolicy sınıfıyla (Google Play Lisans Doğrulama Kitaplığı'nda bulunur) neredeyse aynıdır ancak APK genişletme dosyası yanıt ekstraları için ek işleme içerir.

Not: Önceki bölümde açıklandığı gibi Downloader Library'yi kullanıyorsanız kitaplık, APKExpansionPolicy ile tüm etkileşimi gerçekleştirir. Bu nedenle, bu sınıfı doğrudan kullanmanız gerekmez.

Bu sınıfta, mevcut genişletme dosyalarıyla ilgili gerekli bilgileri edinmenize yardımcı olacak yöntemler bulunur:

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

APKExpansionPolicy'yı Downloader Library'yi kullanmadığınız zamanlarda nasıl kullanacağınız hakkında daha fazla bilgi için Uygulamanıza Lisans Ekleme dokümanına bakın. Bu dokümanda, bu tür bir lisans politikasının nasıl uygulanacağı açıklanmaktadır.

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 dosya türüne bağlıdır. Genel bakış bölümünde belirtildiği gibi, genişletme dosyalarınız istediğiniz türde olabilir ancak belirli bir dosya adı biçimi kullanılarak yeniden adlandırılır ve <shared-storage>/Android/obb/<package-name>/ konumuna kaydedilir.

Dosyalarınızı nasıl okuduğunuzdan bağımsız olarak, öncelikle harici depolama alanının okunabilir olup olmadığını kontrol etmeniz gerekir. Kullanıcı, depolama alanını USB üzerinden bir bilgisayara bağlamış veya SD kartı gerçekten çıkarmış olabilir.

Not: Uygulamanız başlatıldığında, getExternalStorageState() çağrısını yaparak harici depolama alanının kullanılabilir ve okunabilir olup olmadığını her zaman kontrol etmeniz gerekir. Bu işlev, harici depolama alanının durumunu temsil eden olası birkaç dizeden birini döndürür. Dönüş değerinin uygulamanız tarafından okunabilmesi için MEDIA_MOUNTED olması gerekir.

Dosya adlarını alma

Genel bakış bölümünde açıklandığı gibi, APK genişletme dosyalarınız belirli bir dosya adı biçimiyle 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şturmak üzere getExternalStorageDirectory() ve getPackageName() yöntemlerini kullanmanız gerekir.

Aşağıda, uygulamanızda kullanabileceğiniz ve her iki genişletme dosyanızın tam yolunu içeren bir dizi döndüren bir yöntem verilmiştir:

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ızın Context ve istenen 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, indirme işlemi başladığında APKExpansionPolicy sınıfının getExpansionFileName(int index) yöntemiyle genişletme dosyasının adını sorgulayarak sürümü SharedPreferences dosyasına kaydetmektir. Ardından, 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 Expansion Zip Library'yi kullanma

Google Market Apk Expansion paketi, APK Expansion Zip Library (<sdk>/extras/google/google_market_apk_expansion/zip_file/ içinde bulunur) adlı bir kitaplık içerir. Bu, genişletme dosyalarınız ZIP dosyaları olarak kaydedildiğinde bunları okumanıza yardımcı olan isteğe bağlı bir kitaplıktır. Bu kitaplığı kullanarak ZIP genişletme dosyalarınızdaki kaynakları sanal dosya sistemi olarak kolayca okuyabilirsiniz.

APK Expansion Zip Library 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()
Yukarıda gösterilen ve her iki genişletme dosyasına da tam dosya yolunu döndüren yöntem.
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 bu, tüm verilere okuma erişimi sağlayan bir ZipResourceFile döndürür. Yama dosyasının verileri ana dosyayla 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şlemleri gerçekleştirir. APKExpansionSupport.getAPKExpansionZipFile() kullanarak veya ZipResourceFile ile genişletme dosyanızın yolunu ileterek bir örnek alabilirsiniz. Bu sınıf, çeşitli yararlı yöntemler içerir ancak genellikle bunların çoğuna erişmeniz gerekmez. Bu amaçla kullanabileceğiniz birkaç önemli yöntem şunlardır:
getInputStream(String assetPath)
ZIP dosyasındaki bir dosyayı okumak 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 kullanışlı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'leri için dosya erişimi sağlamak amacıyla ZIP dosyalarındaki verileri bir içerik sağlayıcı Uri üzerinden sıralayan bir ContentProvider tanımlar. Örneğin, VideoView.setVideoURI() ile bir video oynatmak istiyorsanız bu özellikten yararlanabilirsiniz.

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 ZIP dosyası, yine de ofset ve uzunluk kontrolleri sağlayan Android medya oynatma çağrılarını (ör. MediaPlayer.setDataSource() ve SoundPool.load()) kullanmanıza olanak tanır. Bunun çalışması için ZIP paketlerini oluştururken medya dosyalarında ek sıkıştırma işlemi 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 okuma

APK Expansion Zip Library kullanılırken ZIP dosyanızdan bir dosya okumak genellikle şunları 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ş bir haritasından okuyarak ana genişletme dosyanızda veya yama genişletme dosyanızda bulunan tüm dosyalara erişim sağlar. getAPKExpansionFile() yöntemini sağlamak için tek yapmanız gereken, uygulamanızın android.content.Context ve hem ana genişletme dosyası hem de yama genişletme dosyası için sürüm numarasını göndermektir.

Belirli bir genişletme dosyasından okumayı tercih ederseniz istediğiniz genişletme dosyasının yoluyla birlikte ZipResourceFile oluşturucusunu 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);

Bu kitaplığı genişletme dosyalarınız için kullanma hakkında daha fazla bilgi edinmek istiyorsanız örnek uygulamanın SampleDownloaderActivity sınıfına göz atın. Bu sınıf, indirilen dosyaları CRC kullanarak doğrulayan ek kod içerir. Bu örneği kendi uygulamanızın temeli olarak kullanırsanız 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ı okuma ve dosyaları indirme.

Dosya okuma işlemleri test ediliyor

Uygulamanızı Google Play'e yüklemeden önce, uygulamanızın paylaşılan depolama alanındaki dosyaları okuma özelliğini test etmeniz gerekir. Yapmanız gereken tek şey, dosyaları cihazın paylaşılan depolama alanında 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 alanını bağlamak 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 uyacak ş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 değer olabilir. Unutmayın:

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

Genişletme dosyalarının işlenmesiyle ilgili bazı hatırlatmaları aşağıda bulabilirsiniz:

  • .obb genişletme dosyalarını silmeyin veya yeniden adlandırmayın (verileri farklı bir konuma çıkarsanız bile). Bunu yaptığınızda Google Play (veya uygulamanız) genişletme dosyasını tekrar tekrar indirir.
  • obb/ Dizinine başka veri kaydetmeyin. Bazı verileri açmanız gerekiyorsa bunları getExternalFilesDir() tarafından belirtilen konuma kaydedin.

Dosya indirmelerini test etme

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

Uygulamanızın manuel indirme prosedürünü uygulamasını test etmek için uygulamayı dahili test kanalında yayınlayabilirsiniz. Böylece uygulama yalnızca yetkili test kullanıcıları tarafından kullanılabilir. Her şey beklendiği gibi çalışıyorsa uygulamanız, ana etkinlik başlar başlamaz genişletme dosyalarını indirmeye başlamalıdır.

Not: Daha önce, yayınlanmamış bir "taslak" sürüm yükleyerek uygulamaları test edebiliyordunuz. Bu işlev artık desteklenmemektedir. Bunun yerine, uygulamayı dahili, kapalı veya açık test kanalında yayınlamanız gerekir. Daha fazla bilgi için Taslak Uygulamalar Artık Desteklenmiyor başlıklı makaleyi inceleyin.

Uygulamanızı güncelleme

Google Play'de genişletme dosyalarını kullanmanın en büyük avantajlarından biri, orijinal öğelerin tamamını yeniden indirmeden uygulamanızı güncelleyebilmenizdir. Google Play, her APK ile iki genişletme dosyası göndermenize olanak tanıdığı için ikinci dosyayı, güncellemeler ve yeni öğeler sağlayan bir "yama" olarak kullanabilirsiniz. Bu sayede, ana genişletme dosyasının yeniden indirilmesi gerekmez. Bu dosya büyük ve kullanıcılar için maliyetli olabilir.

Yama genişletme dosyası teknik olarak ana genişletme dosyasıyla aynıdır ve ne Android sistemi ne de Google Play, ana ve yama genişletme dosyalarınız arasında gerçek bir yama işlemi gerçekleştirir. Uygulama kodunuz gerekli tüm yamaları kendisi uygulamalıdır.

Genişletme dosyalarınız olarak ZIP dosyalarını kullanıyorsanız Apk Expansion paketiyle birlikte gelen APK Expansion Zip Library, 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 manifest dosyasındaki 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ükleyen kullanıcılar ana genişletme dosyasını indirmez. Mevcut kullanıcılar yalnızca güncellenmiş APK'yı ve yeni yama genişletme dosyasını alır (önceki ana genişletme dosyası korunur).

Genişletme dosyalarındaki güncellemelerle ilgili olarak göz önünde bulundurmanız gereken birkaç sorun aşağıda verilmiş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ı. Google Play, bir dosyayı güncellerken önceki sürümü siler (manuel güncellemeler yaparken uygulamanız da bunu yapmalıdır).
  • Yama genişletme dosyası eklerken Android sistemi uygulamanızı veya ana genişletme dosyanızı yama yapmaz. Uygulamanızı yama verilerini destekleyecek şekilde tasarlamanız gerekir. Ancak Apk Expansion paketi, ZIP dosyalarını genişletme dosyası olarak kullanmak için bir kitaplık içerir. Bu kitaplık, yama dosyasındaki verileri ana genişletme dosyasıyla birleştirir. Böylece, tüm genişletme dosyası verilerini kolayca okuyabilirsiniz.