APK Genişletme Dosyaları

Google Play, kullanıcıların indirdiği sıkıştırılmış APK'nın en fazla 100 MB olmasını gerektirir. Çoğu uygulama için bu, uygulamanın tüm kodu ve öğeleri için yeterli alan demektir. Ancak bazı uygulamalar yüksek kaliteli grafikler, medya dosyaları veya diğer büyük öğeler için daha fazla alana ihtiyaç duyar. 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 ideal olmaktan uzaktır. Google Play, bu süreci sizin için daha kolay ve kullanıcılar için daha keyifli hale getirmek amacıyla APK'nıza ek olarak 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 size maliyet yansıtılmaksızın cihaza sunar. Genişletme dosyaları, uygulamanızın bu dosyalara erişebileceği cihazın paylaşılan depolama alanında (SD kart veya takılabilir USB bölümü; "harici" depolama olarak da bilinir) depolanır. Google Play, çoğu cihazda APK ile birlikte genişletme dosyalarını da indirir. Böylece, kullanıcı uygulamanızı ilk kez açtığında uygulamanızda ihtiyaç duyulan her şey bulunur. Ancak bazı durumlarda uygulamanız, başladığında dosyaları Google Play'den indirmelidir.

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ı 200 MB'a kadar sıkıştırılmış indirme boyutuna izin veren Android Uygulama Paketleri'ni kullanarak yüklemeniz gerekir. Ayrıca, uygulama paketlerinin kullanılması APK oluşturma ve imzalama işlemlerini Google Play'e ertelediği için kullanıcılar yalnızca uygulamanızı çalıştırmak için ihtiyaç duydukları kod ve kaynaklarla optimize edilmiş APK'lar indirir. Birden fazla APK veya genişletme dosyası oluşturmanız, imzalamanız ve yönetmeniz gerekmez. Böylece kullanıcılar daha küçük ve daha optimize edilmiş indirme işlemleri gerçekleştirir.

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 biçimde olabilir ancak indirme sırasında bant genişliğini korumak için sıkıştırılmış bir dosya kullanmanızı öneririz. Her genişletme dosyasının işlevi farklıdır:

  • 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 küçük güncellemeler için kullanılır.

İki genişletme dosyasını istediğiniz şekilde kullanabilirsiniz. Ancak ana genişletme dosyasının birincil öğeleri yayınlamasını ve nadiren güncellenmesini öneririz. Yama genişletme dosyası daha küçük olmalı ve her büyük sürümle veya gerektiğinde güncellenen bir "yama taşıyıcısı" olarak kullanılmalıdır.

Ancak uygulama güncellemeniz için yalnızca yeni bir yama genişletme dosyası gerekiyorsa bile manifest dosyasında 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 kaynak dosyası grubunu ve bu gruba ait sonraki yamaları kapsayıp şifrelemek için JOBB aracını da kullanabilirsiniz. Google Play, dosya türünden bağımsız olarak bu dosyaları opak ikili blob olarak kabul eder ve aşağıdaki şemayı kullanarak dosyaları yeniden adlandırır:

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

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

main veya patch
Dosyanın ana genişletme dosyası 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" ifadesi vurgulanmıştı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 dosya şu şekilde yeniden adlandırılır:

main.314159.com.example.app.obb

Depolama konumu

Google Play, genişleme dosyalarınızı bir cihaza indirdiğinde bunları sistemin ortak depolama konumuna kaydeder. Doğru şekilde çalışmasını sağlamak için genişleme dosyalarını silmemeli, taşımamalı veya yeniden adlandırmamalısınız. Uygulamanızın Google Play'den indirme işlemini gerçekleştirmesi gerekiyorsa dosyaları tam olarak aynı konuma kaydetmeniz gerekir.

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

<shared-storage>/Android/obb/<package-name>/
  • <shared-storage>, paylaşılan depolama alanının yoludur ve getExternalStorageDirectory() adresinden kullanılabilir.
  • <package-name>, uygulamanızın Java tarzı paket adıdır ve getPackageName() adresinden kullanılabilir.

Her uygulama için bu dizinde en fazla iki genişletme dosyası bulunur. Bunlardan 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 izin almaya devam etmeniz gerekir. Bu nedenle, READ_EXTERNAL_STORAGE iznini uygulama manifestinde beyan etmeniz ve aşağıdaki şekilde çalışma zamanında izin istemeniz gerekir:

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

Android 6 ve sonraki sürümlerde harici depolama alanı izninin çalışma zamanında istenmesi gerekir. Ancak Android'in bazı uygulamalarında OBB dosyalarını okuma izni gerekmez. Aşağıdaki kod snippet'inde, harici depolama alanı izni istemeden önce okuma erişimini nasıl kontrol edeceğiniz 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 OBB genişletme dosyalarını daha sonra silmeyin ve açılmış verileri aynı dizine kaydetmeyin. Ayıkladığı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şleme 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çmak zorunda kalmadan, ofset ve uzunluk kontrolleri (ör. MediaPlayer.setDataSource() ve SoundPool.load()) içeren dosyalarda medya oynatma çağrılarını kullanabilirsiniz. Bunun işe yaraması için ZIP paketlerini oluştururken medya dosyalarında ek sıkıştırma işlemi gerçekleştirmemeniz gerekir. Örneğin, zip aracını kullanırken sıkıştırılmaması gereken dosya uzantılarını 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 aynı anda genişletme dosyalarınızı indirip kaydeder. Ancak bazı durumlarda Google Play genişletme dosyalarını indiremez veya kullanıcı daha önce indirilen genişletme dosyalarını silmiş olabilir. Bu durumların üstesinden gelmek için uygulamanızın, ana etkinlik başladığında Google Play tarafından sağlanan bir URL'yi kullanarak dosyaları kendi başına 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 cihazda bu mümkündür) 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şleme dosyalarının cihaza önceden kaydedilip kaydedilmediğini kontrol etmelidir.
    1. Cevabınız evet ise uygulamanız kullanıma hazırdır.
    2. Aksi takdirde, 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şleme dosyasının adını, dosya boyutunu ve URL'sini içeren bir yanıt verir. Bu bilgilerle dosyaları indirip uygun depolama konumuna kaydedebilirsiniz.

Dikkat: Uygulamanız başladığında dosyalar cihazda yoksa genişleme dosyalarını Google Play'den indirmek için gerekli kodu eklemeniz önemlidir. Genişleme dosyalarını indirme 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 bir hizmetten sizin minimum miktarda kod göndermenizle gerçekleştiren bir kitaplık hazırladık.

Geliştirme kontrol 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 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 yer kaplıyorsa bunun yerine her APK'nın yalnızca hedeflediği ekranlar için gereken öğ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 tüm derlenmiş kod ve kaynakları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 kullanmak için bir dosyada paketleyin.

    Normalde, ikinci yama genişletme dosyasını yalnızca ana genişletme dosyasında güncelleme yaparken kullanmalısınız. Ancak kaynaklarınız ana genişletme dosyası için belirlenen 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ı silmeniz, taşımanı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 ardından bunları APK Genişletme ZIP Kitaplığı'nı kullanarak okumanızı öneririz.

  4. Uygulamanızın ana etkinliğine, başlangıçta cihazda genişleme dosyalarının olup olmadığını kontrol eden bir mantık ekleyin. Dosyalar cihazda yoksa Google Play'in uygulama lisanslama hizmetini kullanarak genişleme dosyalarının URL'lerini isteyin, ardından 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şleme dosyalarının adını değiştirmemeniz ve bunları uygun depolama konumuna kaydetmeniz gerektiğini unutmayın.

Uygulamanızın geliştirmesini tamamladıktan sonra Genişletme Dosyalarınızı Test Etme başlıklı kılavuzu uygulayın.

Kurallar ve Sınırlılıklar

APK genişletme dosyası ekleme özelliği, uygulamanızı Play Console'u kullanarak yüklediğinizde kullanılabilir. Uygulamanızı ilk kez yüklerken veya genişletme dosyaları 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. Genişleme dosyalarınızı Google Play'den indirmek için kullanıcı, uygulamanızı Google Play'den edinmiş olmalıdır. Uygulama başka bir yöntemle yüklendiyse Google Play, genişletme dosyalarınızın URL'lerini sağlamaz.
  3. İndirme işlemini uygulamanızdan gerçekleştirirken Google Play'in her dosya için sağladığı URL, her indirme için benzersizdir ve uygulamanıza sağlandıktan kısa bir süre sonra geçerlilik süresi 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şleme dosyasının adı değişmez. Dosyanın orijinal olarak ilişkilendirildiği APK tarafından alınan sürüm korunur.
  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 her cihaz için benzersiz bir versionCode değeri sağlamak ve her APK için farklı filtreler tanımlamak üzere ayrı APK'lar yüklemeniz gerekir.
  6. Genişletme dosyalarını değiştirerek uygulamanızda 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 APK'nızı yalnızca versionCode (ve belki de versionName) dosyasını değiştirerek güncelleyebilirsiniz.
  7. obb/ dizininizle ilişkili başka veriler kaydetmeyin. Bazı verileri açmanız gerekiyorsa bunları getExternalFilesDir() tarafından belirtilen konuma kaydedin.
  8. Güncelleme yapmıyorsanız .obb genişletme dosyasını silmeyin veya yeniden adlandırmayın. Bu işlem, Google Play'in (veya uygulamanızın) genişletme dosyası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ı indirme

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

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

  1. Uygulamanız başladığında, paylaşılan depolama alanında (Android/obb/<package-name>/ dizininde) genişletme dosyalarını arayın.
    1. Genişletme dosyaları mevcutsa uygulamanız çalışmaya devam edebilir.
    2. Genişletme dosyaları bulunmuyorsa:
      1. Uygulamanızın genişleme dosyası adlarını, boyutlarını ve URL'lerini almak için Google Play'in uygulama lisanslama özelliğini kullanarak bir istek gönderin.
      2. Genişletme dosyalarını indirmek ve 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ı aynen kullanmanız gerekir.

        Not: Google Play'in genişletme dosyalarınız 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.

Uygulamanız ücretsizse (ücretli değilse) muhtemelen uygulama lisanslama hizmetini kullanmamışsınızdır. Bu özellik, öncelikle uygulamanız için lisanslama politikalarını uygulamanız ve kullanıcının uygulamanızı kullanma hakkına sahip olmasını (Google Play'de bunun için ödeme yaptığından) sağlamak üzere tasarlanmıştır. Genişletme dosyası işlevini kolaylaştırmak için lisanslama hizmeti, uygulamanıza Google Play'de barındırılan uygulamanızın genişleme dosyalarının URL'sini içeren bir yanıt verecek şekilde geliştirildi. Bu nedenle, 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 tutmanız gerekmez. Kitaplığın, genişleme dosyalarınızın URL'sini döndüren isteği gerçekleştirmesi yeterlidir.

Not: Uygulamanız ücretsiz olsun veya olmasın Google Play, genişleme dosyası URL'lerini yalnızca kullanıcı uygulamanızı Google Play'den edindiyse döndürür.

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

  • Cihazda genişletme dosyaları için yeterli alan olmayabilir. Bu nedenle, indirme işlemine başlamadan önce alanı kontrol etmeniz ve yeterli alan yoksa kullanıcıyı uyarmanız gerekir.
  • Dosya indirme işlemleri, kullanıcı etkileşimini engellememek ve indirme tamamlanırken kullanıcının uygulamanızdan çıkmasına izin vermek için 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.
  • Ağ bağlantısı indirme sırasında değişebilir. Bu nedenle, bu tür değişiklikleri yönetmeniz ve kesinti olursa mümkün olduğunda indirme işlemini devam ettirmeniz gerekir.
  • İndirme işlemi arka planda gerçekleşirken indirme ilerleme durumunu gösteren, indirme işlemi tamamlandığında kullanıcıyı bilgilendiren ve kullanıcının seçmesi durumunda kullanıcıyı uygulamanıza geri götüren bir bildirim sağlamanız gerekir.

Bu işlemi sizin için basitleştirmek amacıyla, lisanslama hizmeti aracılığıyla genişleme dosyası URL'lerini isteyen, genişleme dosyalarını indiren, yukarıda listelenen tüm görevleri gerçekleştiren ve hatta etkinliğinizin indirme işlemini duraklatmasına ve devam ettirmesine olanak tanıyan İndirici Kitaplığı'nı oluşturduk. Uygulamanıza İndirici Kitaplığı'nı ve birkaç kod kancası ekleyerek genişleme dosyalarını indirmeyle ilgili neredeyse tüm işlemleri sizin için kodlamış olursunuz. Bu nedenle, en iyi kullanıcı deneyimini sunmak için en az çabayı göstermeniz gerekir. Bu nedenle, genişletme dosyalarınızı indirmek üzere İndirme Arşivi'ni kullanmanızı öneririz. Aşağıdaki bölümlerdeki bilgilerde, kitaplığın uygulamanıza nasıl entegre edileceği açıklanmaktadır.

Google Play URL'lerini kullanarak genişleme dosyalarını indirmek için kendi çözümünüzü geliştirmeyi tercih ederseniz lisans isteği göndermek için uygulama lisanslama dokümanlarını uygulamanız, ardından genişleme dosyası adlarını, boyutlarını ve URL'lerini yanıt ekstralarından almanız gerekir. Lisanslama politikanız olarak APKExpansionPolicy sınıfını (Lisans Doğrulama Kitaplığı'na dahildir) kullanmanız gerekir. Bu sınıf, lisanslama hizmetinden genişleme dosyası adlarını, boyutlarını ve URL'lerini yakalar.

İndirme Aracı Kitaplığı hakkında

APK genişletme dosyalarını uygulamanızla kullanmak ve en az çabayla en iyi kullanıcı deneyimini sunmak için Google Play APK Genişletme Kitaplığı paketine dahil olan İndirme Aracı Kitaplığı'nı kullanmanızı öneririz. Bu kitaplık, genişleme dosyalarınızı arka plan hizmetinde indirir, indirme durumunu içeren bir kullanıcı bildirimi gösterir, ağ bağlantısının kesilmesini yönetir, mümkün olduğunda indirme işlemini devam ettirir ve daha birçok işlem yapar.

İndirme Aracı Kitaplığı'nı kullanarak genişletme dosyası indirmelerini uygulamak için tek yapmanız gereken:

  • Her biri sizden 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şleme dosyalarının indirilip indirilmediğini kontrol eden ve indirilmediyse indirme işlemini başlatan ve ilerleme kullanıcı arayüzü gösteren bir mantık ekleyin.
  • Ana etkinliğinizde, indirme ilerleme durumuyla ilgili güncellemeler alan birkaç yöntem içeren bir geri çağırma arayüzü uygulayın.

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

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

İndirici 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ünde SDK Araçları sekmesini seçerek aşağıdakileri indirin:

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

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

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

Not: İndirici Kitaplığı, Lisans Doğrulama Kitaplığı'na bağlıdır. Lisans Doğrulama Kitaplığı'nı İndirici 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/ dizinine değiştirin.
  2. Hem LVL'yi hem de İndirici Kitaplığı'nı projenize eklemek için android update project'yi --library seçeneğiyle çalıştırın. Örnek:
    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ığı'nı hem de İndirici Kitaplığı'nı eklediğinizde, Google Play'den genişletme dosyası indirme özelliğini hızlıca entegre edebilirsiniz. Genişleme dosyaları için seçtiğiniz biçim ve bu dosyaları paylaşılan depolama alanından nasıl okuyacağınız, uygulama ihtiyaçlarınıza göre dikkate almanız gereken ayrı bir uygulamadır.

İpucu: Apk Expansion paketinde, Downloader Library'nin bir uygulamada nasıl kullanılacağını gösteren bir örnek uygulama bulunur. Örnekte, Apk Expansion paketinde bulunan APK Expansion Zip Library adlı üçüncü bir kitaplık kullanılır. Genişletme dosyalarınız için ZIP dosyaları kullanmayı planlıyorsanız APK Genişletme ZIP Kitaplığı'nı da uygulamanıza 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

İndirme Arşivi'nin genişletme dosyalarını indirmesi için uygulamanızın manifest dosyasında belirtmeniz gereken çeşitli izinler gerekir. 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: İndirici kitaplığı için varsayılan olarak API düzeyi 4 gerekir ancak APK genişletme ZIP kitaplığı için API düzeyi 5 gerekir.

İndirme hizmetinin uygulanması

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

  • Gerekli olduğunda (ör. bağlantı kaybı nedeniyle) indirme işlemini duraklatmak ve mümkün olduğunda (bağlantı elde edildiğinde) indirme işlemini devam ettirmek için cihazın ağ bağlantısında (CONNECTIVITY_ACTION yayını) değişiklikleri dinleyen bir BroadcastReceiver kaydeder.
  • Hizmetin sonlandırıldığı durumlarda indirme işlemini yeniden denemek için bir RTC_WAKEUP alarmı planlar.
  • İndirme ilerleme durumunu ve hataları ya da durum değişikliklerini gösteren özel bir Notification oluşturur.
  • Uygulamanızın indirme işlemini manuel olarak duraklatmasına ve devam ettirmesine olanak tanır.
  • Genişleme dosyalarını indirmeden önce ortak depolama alanının bağlı ve kullanılabilir olup olmadığını, dosyaların mevcut olup olmadığını ve yeterli alan olup olmadığını doğrular. Ardından, bunlardan herhangi biri doğru değilse kullanıcıyı bilgilendirir.

Bunun için uygulamanızda DownloaderService sınıfını genişleten bir sınıf oluşturmanız ve belirli uygulama ayrıntılarını sağlamak için üç yöntemi geçersiz kılmanız yeterlidir:

getPublicKey()
Bu işlev, yayıncı hesabınız için Base64 kodlu RSA ortak anahtarı olan bir dize döndürmelidir. Bu anahtarı Play Console'daki profil sayfasından bulabilirsiniz (Lisanslama için ayarlama bölümüne bakın).
getSALT()
Bu işlev, lisanslama Policy'ın Obfuscator oluşturmak için kullandığı rastgele baytlardan oluşan bir dizi döndürmelidir. Tuz, lisanslama verilerinizin kaydedildiği karartılmış SharedPreferencesdosyanızı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ının adını döndürmelidir (indirme hizmeti beklenmedik bir şekilde durursa bu durumla karşılaşılabilir).

Örneğin, DownloaderService için eksiksiz bir uygulamayı aşağıda bulabilirsiniz:

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

Not: BASE64_PUBLIC_KEY değerini yayıncı hesabınıza ait herkese açık anahtar olarak güncellemeniz gerekir. Anahtarı, Developer Console'daki profil bilgilerinizin altında bulabilirsiniz. Bu, indirmelerinizi test ederken bile gereklidir.

Hizmeti manifest dosyanızda belirtmeyi unutmayın:

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

Alarm alıcısını uygulama

DownloaderService, dosya indirme işlemlerinin ilerleme durumunu izlemek ve gerekirse indirme işlemini yeniden başlatmak için uygulamanızda bir BroadcastReceiver'a Intent yayınlayan bir RTC_WAKEUP alarmı planlar. İndirme işleminin durumunu kontrol eden ve gerekirse yeniden başlatan bir API'yi çağırmak için BroadcastReceiver'yi tanımlamanız gerekir.

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

Örnek:

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, hizmetinizin getAlarmReceiverClassName() yönteminde adı döndürmeniz gereken sınıftır (önceki bölüme bakın).

Alıcının manifest dosyanızda belirtilmesi gerekir:

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

İndirme işlemini başlatma

Uygulamanızdaki ana etkinlik (başlatıcı simgeniz tarafından başlatılan etkinlik), genişleme dosyalarının cihazda olup olmadığını doğrulamaktan ve yoksa indirme işlemini başlatmaktan sorumludur.

İndirme işlemini İndirme Aracı Kitaplığı'nı kullanarak başlatmak için aşağıdaki işlemleri yapmanız gerekir:

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

    İndirme Aracı Kitaplığı, bu sürece 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ığına dair bir boole değeri içerir. (Ayrıntılar için örnek uygulamanın SampleDownloaderActivity sınıfına bakın.)

    Bu yöntem yanlış döndürürse uygulamanın indirme işlemini başlatması 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 bir PendingIntent. Bu, DownloaderService'un indirme işleminin ilerleme durumunu göstermek için oluşturduğu 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 etkinlik) açar.
    • serviceClass: DownloaderService uygulamanız için Class nesnesi. Hizmeti başlatmak ve gerekirse indirme işlemini başlatmak için gereklidir.

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

    • NO_DOWNLOAD_REQUIRED: Dosyalar zaten mevcutsa veya indirme işlemi devam ediyorsa döndürülür.
    • LVL_CHECK_REQUIRED: Genişletme dosyası URL'lerini almak için lisans doğrulaması gerekiyorsa döndürülür.
    • DOWNLOAD_REQUIRED: Genişletme dosyası URL'leri zaten biliniyor ancak indirilmediyse döndürülür.

    LVL_CHECK_REQUIRED ve DOWNLOAD_REQUIRED için davranış temelde aynıdır ve genellikle bunlarla ilgili endişelenmenize gerek yoktur. startDownloadServiceIfRequired() işlevini çağıran ana etkinliğinizde, yanıtın NO_DOWNLOAD_REQUIRED olup olmadığını kontrol edebilirsiniz. Yanıt NO_DOWNLOAD_REQUIRED değilse İndirici Kitaplığı indirme işlemini başlatır 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 NO_DOWNLOAD_REQUIRED ise dosyalar kullanılabilir durumdadır ve uygulamanız başlatılabilir.

    Örnek:

    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 ç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 durumuyla ilgili geri çağırma çağrıları alır.

    CreateStub()'u çağırarak IStub'ünüzü örneklemek için IDownloaderClient arayüzünün bir uygulamasını ve DownloaderService uygulamanızı ona iletmeniz gerekir. İndirme ilerleme durumunu alma ile ilgili bir 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'unuzu örneklemek için CreateStub() işlevini ç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() çağrısı alır. Burada, IStub üzerinde connect()'yi çağırarak uygulamanızın Context değerini iletmeniz gerekir. Buna karşılık, etkinliğinizin onStop() geri çağırma işlevinde disconnect() işlevini ç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 üzerinde connect() çağrısı yaptığınızda etkinliğiniz DownloaderService'ye bağlanır. Böylece etkinliğiniz, IDownloaderClient arayüzü üzerinden indirme durumundaki değişikliklerle ilgili geri çağırma alır.

İndirme işleminin ilerleme durumu

İndirme ilerleme durumuyla ilgili güncellemeler almak ve DownloaderService ile etkileşimde bulunmak için İndirici Kitaplığı'nın IDownloaderClient arayüzünü uygulamanız gerekir. Genellikle, indirme işlemini başlatmak için kullandığınız etkinlik, indirme 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 sınıfını örnekledikten sonra bu yönteme bir çağrı alırsınız. Bu çağrı, DownloaderService örneğinize bağlı bir Messenger nesnesi iletilir. İndirme işlemlerini duraklatma ve devam ettirme gibi hizmete istek göndermek için hizmete bağlı IDownloaderService arayüzünü almak üzere DownloaderServiceMarshaller.CreateProxy() numaralı telefonu aramanı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ıktan sonra indirme hizmetine indirme işlemini duraklatma ve devam ettirme (requestPauseDownload() ve requestContinueDownload()) gibi komutlar gönderebilirsiniz.

onDownloadStateChanged(int newState)
İndirme hizmetinde indirme durumunda bir değişiklik olduğunda (ör. indirme işleminin başlaması veya tamamlanması) bu işlev çağrılır.

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

Kullanıcılarınıza faydalı bir mesaj sunmak için Helpers.getDownloaderStringResourceIDFromState() işlevini çağırarak her durum için karşılık gelen bir dize isteyebilirsiniz. Bu işlev, İndirici Kitaplığı ile birlikte paketlenen dizelerden birinin kaynak kimliğini döndürür. Örneğin, "Dolaşımdaysanız indirme duraklatıldı" dizesi STATE_PAUSED_ROAMING değerine karşılık gelir.

onDownloadProgress(DownloadProgressInfo progress)
İndirme hizmeti, indirme ilerleme kullanıcı arayüzünü güncelleyebilmeniz için bir DownloadProgressInfo nesnesi yayınlamak üzere bunu çağırır. Bu nesne, kalan tahmini süre, mevcut hız, genel ilerleme ve toplam dahil olmak üzere indirme ilerleme durumuyla ilgili çeşitli bilgileri açıklar.

İpucu: İndirme ilerleme durumu kullanıcı arayüzünü güncelleyen bu geri çağırmalara ilişkin örnekler için Apk Expansion paketiyle birlikte sağlanan örnek uygulamadaki SampleDownloaderActivity bölümüne bakın.

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

requestPauseDownload()
İndirmeyi duraklatır.
requestContinueDownload()
Duraklatılmış bir indirme işlemini devam ettirir.
setDownloadFlags(int flags)
Dosya indirilmesine izin verilen ağ türleri için kullanıcı tercihlerini belirler. Mevcut uygulamada bir işaret (FLAGS_DOWNLOAD_OVER_CELLULAR) desteklenir ancak başka işaretler de ekleyebilirsiniz. Bu işaret varsayılan olarak etkin değildir. Bu nedenle, kullanıcının genişleme dosyalarını indirmek için kablosuz bağlantı kullanması gerekir. Hücresel ağ üzerinden indirme işlemlerini etkinleştirmek için bir 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'yi kullanma

Google Play İndirici Kitaplığı'nı kullanmak yerine kendi indirici hizmetinizi oluşturmaya karar verirseniz yine de Lisans Doğrulama Kitaplığı'nda sağlanan APKExpansionPolicy öğesini kullanmanız gerekir. APKExpansionPolicy sınıfı, ServerManagedPolicy ile neredeyse aynıdır (Google Play Lisans Doğrulama Kitaplığı'nda kullanılabilir) ancak APK genişleme dosyası yanıt ekstraları için ek işlemler 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.

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

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

İndirici Kitaplığı'nı kullanmadığınızda APKExpansionPolicy değerini nasıl kullanacağınız hakkında daha fazla bilgi için Uygulamanıza Lisanslama Ekleme dokümanlarına bakın. Bu dokümanlarda, 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şleme 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>/'e kaydedilir.

Dosyalarınızı nasıl okuduğunuzdan bağımsız olarak, her zaman önce harici depolama alanının okunabilir olup olmadığını kontrol etmeniz gerekir. Kullanıcının depolama alanını USB üzerinden bir bilgisayara bağlamış veya SD kartı çıkarmış olması mümkündür.

Not: Uygulamanız başladığında, getExternalStorageState() işlevini çağırarak 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 birkaç olası dize arasından 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ı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.

Uygulamanızda, her iki genişletme dosyanızın da tam yolunu içeren bir dizi almak için kullanabileceğiniz yöntem aşağıda 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ı Contextve istenen genişletme dosyasının sürümünü göndererek çağırabilirsiniz.

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

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

APK Genişletme Sıkıştırılmış Kitaplığını Kullanma

Google Market Apk Genişletme paketi, APK Genişletme Zip Kitaplığı (<sdk>/extras/google/google_market_apk_expansion/zip_file/ konumunda) adlı bir kitaplık içerir. 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ığı kullanarak ZIP uzantı dosyalarınızdaki kaynakları sanal dosya sistemi olarak kolayca okuyabilirsiniz.

APK Genişleme 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 sağlar:
getAPKExpansionFiles()
Yukarıdaki yöntemin aynısı. Bu yöntem, her iki uzantı dosyasının da tam dosya yolunu döndürür.
getAPKExpansionZipFile(Context ctx, int mainVersion, int patchVersion)
Hem ana dosyanın hem de düzeltme 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. Bu durumda, yama dosyasının verileri ana dosyanın üzerine birleştirilir.
ZipResourceFile
Ortak depolama alanındaki bir ZIP dosyasını temsil eder ve ZIP dosyalarınıza dayalı sanal bir dosya sistemi sağlamak için tüm işlemleri gerçekleştirir. APKExpansionSupport.getAPKExpansionZipFile() kullanarak veya ZipResourceFile ile genişleme 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. Birkaç önemli yöntem şunlardır:
getInputStream(String assetPath)
ZIP dosyasındaki bir dosyayı okumak için bir InputStream sağlar. assetPath, ZIP dosyası içeriğinin 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çeriğinin 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şim 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 aracılığıyla düzenleyen 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ını atlama

Medya dosyalarını depolamak için genişletme dosyalarınızı kullanıyorsanız ZIP dosyası, ofset ve uzunluk kontrolleri sağlayan Android medya oynatma çağrılarını (MediaPlayer.setDataSource() ve SoundPool.load() gibi) kullanmanıza yine de olanak tanır. Bunun işe yaraması için ZIP paketlerini oluştururken medya dosyalarında ek sıkıştırma işlemi gerçekleştirmemeniz gerekir. Örneğin, zip aracını kullanırken sıkıştırılmaması gereken dosya uzantılarını belirtmek için -n seçeneğini kullanmanız gerekir:

zip -n .mp4;.ogg main_expansion media_files

ZIP dosyasından okuma

APK Genişletme Zip Kitaplığı kullanılırken ZIP'inizdeki bir dosyayı okumak için genellikle aşağıdakiler gerekir:

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 dosyanın da tüm dosyalarının birleştirilmiş bir 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 kullanırken uygulamanız android.content.Context ve hem ana genişletme dosyasının hem de yama genişletme dosyasının sürüm numarasını sağlamanız yeterlidir.

Belirli bir genişletme dosyasından okumak isterseniz ZipResourceFile kurucusunu, istediğiniz genişletme dosyasının yoluyla birlikte 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şleme dosyalarınız için bu kitaplığı kullanma hakkında daha fazla bilgi edinmek isterseniz CRC kullanarak indirilen dosyaları doğrulamak için ek kod içeren örnek uygulamanın SampleDownloaderActivity sınıfına bakın. Bu örneği kendi uygulamanızın temeli olarak kullanırsanız xAPKS dizisinde genişleme dosyalarınızın bayt boyutunu belirtmeniz gerektiğini unutmayın.

Genişletme dosyalarınızı test etme

Uygulamanızı yayınlamadan önce iki şeyi test etmeniz gerekir: Genişletme dosyalarını okuma ve dosyaları indirme.

Dosya okuma işlemlerini test etme

Uygulamanızı Google Play'e yüklemeden önce, uygulamanızın paylaşılan depolama alanınızdaki dosyaları okuma özelliğ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 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 herhangi bir değer olabilir. Şunu unutmayın:

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

Genişleme dosyalarını işlemeyle ilgili bazı hatırlatmalar:

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

Dosya indirmelerini test etme

Uygulamanız bazen ilk açıldığında genişletme dosyalarını manuel olarak indirmesi gerektiğinden, uygulamanızın URL'leri başarıyla sorgulayabileceğinden, dosyaları indirip cihaza kaydedebileceğinden emin olmak için bu süreci test etmeniz önemlidir.

Uygulamanızın manuel indirme prosedürünü uygulama şeklini test etmek için uygulamanızı dahili test kanalına yayınlayarak yalnızca yetkili test kullanıcılarının kullanımına sunabilirsiniz. Her şey beklendiği gibi giderse uygulamanız, ana etkinlik başlar başlamaz genişletme dosyalarını indirmeye başlar.

Not: Daha önce, yayınlanmamış bir "taslak" sürümü yükleyerek uygulamaları test edebiliyordunuz. Bu işlev artık desteklenmemektedir. Bunun yerine, dahili, kapalı veya açık bir 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 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üncellemeler ve yeni öğeler sağlayan bir "yama" olarak kullanabilirsiniz. Bu sayede, kullanıcılar için büyük ve pahalı olabilecek ana genişleme dosyasını yeniden indirmeniz gerekmez.

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 gerçek yamalama işlemi gerçekleştirmez. Uygulama kodunuz gerekli tüm yamaları kendisi yapmalıdır.

Genişletme dosyalarınız olarak ZIP dosyaları kullanıyorsanız APK Genişletme paketine dahil olan APK Genişletme Zip Kitaplığı, yama dosyanızı ana genişletme dosyasıyla birleştirme olanağı sunar.

Not: Yalnızca yamanın genişletilmiş 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ında versionCode öğesini 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ı indirmez. Mevcut kullanıcılara yalnızca güncellenmiş APK ve yeni yama genişletme dosyası (önceki ana genişletme dosyası korunur) gönderilir.

Genişleme dosyalarındaki güncellemelerle ilgili göz önünde bulundurulması gereken birkaç nokta aşağıda verilmiştir:

  • Uygulamanız için tek seferde yalnızca iki genişletme dosyası olabilir. Bir ana genişletme dosyası ve bir yama genişletme dosyası. Google Play, bir dosyada güncelleme yapılırken önceki sürümü siler (manuel güncellemeler yapılırken uygulamanızın da bunu yapması gerekir).
  • Android sistemi, yama genişletme dosyası eklendiğinde uygulamanızı veya ana genişletme dosyanızı yamamaz. Uygulamanızı, yama verilerini destekleyecek şekilde tasarlamanız gerekir. Ancak Apk genişletme paketi, ZIP dosyalarını genişletme dosyası olarak kullanmaya yönelik 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.