File Ekspansi APK

Google Play mewajibkan APK terkompresi yang didownload pengguna tidak boleh lebih dari 100 MB. Bagi sebagian besar aplikasi, ini berarti ada banyak ruang untuk semua kode dan aset aplikasi. Namun, beberapa aplikasi memerlukan lebih banyak ruang untuk grafis high-fidelity, file media, atau aset besar lainnya. Sebelumnya, jika ukuran download terkompresi aplikasi Anda melebihi 100 MB, Anda harus meng-hosting dan mendownload sendiri resource tambahan saat pengguna membuka aplikasi. Meng-hosting dan menayangkan file tambahan dapat memerlukan biaya, dan pengalaman pengguna sering kali kurang ideal. Agar proses ini memudahkan Anda dan lebih menarik bagi pengguna, Google Play memungkinkan Anda melampirkan dua file ekspansi besar yang melengkapi APK.

Google Play akan meng-hosting file ekspansi untuk aplikasi Anda dan menayangkannya ke perangkat secara gratis. File ekspansi disimpan ke lokasi penyimpanan bersama perangkat tersebut ( kartu SD atau partisi USB yang dapat dipasang; juga dikenal sebagai penyimpanan "eksternal") tempat aplikasi dapat mengaksesnya. Pada sebagian besar perangkat, Google Play mendownload file ekspansi sekaligus mendownload APK, sehingga aplikasi Anda memiliki semua yang diperlukan saat pengguna membukanya untuk pertama kali. Namun, dalam beberapa kasus, aplikasi Anda harus mendownload file dari Google Play saat aplikasi dimulai.

Jika Anda tidak ingin menggunakan file ekspansi dan ukuran download terkompresi aplikasi Anda lebih besar dari 100 MB, sebaiknya upload aplikasi menggunakan Android App Bundle yang memungkinkan ukuran download terkompresi hingga 200 MB. Selain itu, karena penggunaan app bundle berarti menyerahkan pembuatan dan penandatanganan APK kepada Google Play, pengguna akan mendownload APK yang dioptimalkan hanya dengan kode dan resource yang diperlukan untuk menjalankan aplikasi Anda. Anda tidak perlu membuat, menandatangani, dan mengelola beberapa APK atau file ekspansi, dan pengguna akan mendapatkan hasil download yang lebih kecil dan lebih optimal.

Ringkasan

Setiap kali mengupload APK menggunakan Konsol Google Play, Anda memiliki opsi untuk menambahkan satu atau dua file ekspansi ke APK. Setiap file dapat berukuran hingga 2 GB dan dapat berupa format apa pun yang Anda pilih, tetapi sebaiknya gunakan file terkompresi untuk menghemat bandwidth selama proses download. Secara konsep, setiap file ekspansi memainkan peran yang berbeda:

  • File ekspansi utama adalah file ekspansi utama untuk resource tambahan yang diperlukan oleh aplikasi Anda.
  • File ekspansi patch bersifat opsional dan ditujukan untuk update kecil pada file ekspansi utama.

Meskipun Anda dapat menggunakan dua file perluasan dengan cara apa pun, sebaiknya file perluasan utama mengirimkan aset utama dan harus jarang diperbarui (jika memang diperbarui); file perluasan patch harus lebih kecil dan berfungsi sebagai “pembawa patch”, yang diperbarui dengan setiap rilis utama atau jika diperlukan.

Akan tetapi, meskipun update aplikasi hanya memerlukan file perluasan patch baru, Anda tetap harus mengupload APK baru dengan versionCode yang telah diupdate dalam manifes. ( Konsol Play tidak mengizinkan Anda mengupload file ekspansi ke APK yang sudah ada.)

Catatan: File ekspansi patch secara semantik sama dengan file ekspansi utama—Anda dapat menggunakan setiap file dengan cara apa pun yang Anda inginkan.

Format nama file

Setiap file ekspansi yang Anda upload dapat berupa format apa pun yang Anda pilih (ZIP, PDF, MP4, dll.). Anda juga dapat menggunakan alat JOBB untuk merangkum dan mengenkripsi kumpulan file resource dan patch berikutnya untuk kumpulan tersebut. Terlepas dari jenis file, Google Play menganggapnya sebagai blob biner opaque dan mengganti nama filenya menggunakan skema berikut:

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

Ada tiga komponen untuk skema ini:

main atau patch
Menentukan apakah file adalah file ekspansi utama atau patch. Hanya boleh ada satu file utama dan satu file patch untuk setiap APK.
<expansion-version>
Ini adalah bilangan bulat yang cocok dengan kode versi APK yang terkait dengan perluasan pertama (sesuai dengan nilai android:versionCode aplikasi).

"Pertama" ditekankan karena meskipun Konsol Play memungkinkan Anda untuk menggunakan kembali file ekspansi yang diupload dengan APK baru, nama file ekspansi tidak berubah—Konsol ini mempertahankan versi yang diterapkan saat Anda pertama kali mengupload file.

<package-name>
Nama paket bergaya Java aplikasi Anda.

Misalnya, anggap versi APK Anda adalah 314159 dan nama paket Anda adalah com.example.app. Jika Anda mengupload file ekspansi utama, nama file akan diubah menjadi:

main.314159.com.example.app.obb

Lokasi penyimpanan

Saat Google Play mendownload file ekspansi ke perangkat, Google Play akan menyimpannya ke lokasi penyimpanan bersama sistem. Untuk memastikan perilaku yang benar, Anda tidak boleh menghapus, memindahkan, atau mengganti nama file ekspansi. Jika aplikasi Anda harus melakukan download dari Google Play, Anda harus menyimpan file tersebut ke lokasi yang sama persis.

Metode getObbDir() akan menampilkan lokasi spesifik untuk file ekspansi Anda dalam bentuk berikut:

<shared-storage>/Android/obb/<package-name>/

Untuk setiap aplikasi, tidak pernah ada lebih dari dua file ekspansi dalam direktori ini. Salah satunya adalah file ekspansi utama dan file lainnya adalah file ekspansi patch (jika perlu). Versi sebelumnya akan ditimpa saat Anda mengupdate aplikasi dengan file ekspansi baru. Sejak Android 4.4 (API level 19), aplikasi dapat membaca file ekspansi OBB tanpa izin penyimpanan eksternal. Namun, beberapa implementasi Android 6.0 (API level 23) dan yang lebih baru masih memerlukan izin, sehingga Anda harus menyatakan izin READ_EXTERNAL_STORAGE di manifes aplikasi dan meminta izin pada runtime seperti berikut:

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

Untuk Android versi 6 dan yang lebih baru, izin penyimpanan eksternal perlu diminta pada runtime. Namun, beberapa implementasi Android tidak memerlukan izin untuk membaca file OBB. Cuplikan kode berikut menunjukkan cara memeriksa akses baca sebelum meminta izin penyimpanan eksternal:

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

Jika Anda harus mengekstrak isi file ekspansi, jangan hapus file ekspansi OBB setelahnya dan jangan simpan data yang tidak diekstrak dalam direktori yang sama. Anda harus menyimpan file yang belum diekstrak dalam direktori yang ditentukan oleh getExternalFilesDir(). Namun, jika memungkinkan, sebaiknya gunakan format file ekspansi yang memungkinkan Anda membaca langsung dari file, bukan mengharuskan Anda mengekstrak data. Misalnya, kami telah menyediakan project library yang disebut Library Zip Ekspansi APK yang membaca data Anda langsung dari file ZIP.

Perhatian: Tidak seperti file APK, file apa pun yang disimpan di penyimpanan bersama dapat dibaca oleh pengguna dan aplikasi lain.

Tips: Jika mengemas file media ke ZIP, Anda dapat menggunakan panggilan pemutaran media pada file dengan kontrol offset dan durasi (misalnya MediaPlayer.setDataSource() dan SoundPool.load()) tanpa perlu mengekstrak ZIP Anda. Agar hal ini dapat bekerja, Anda tidak boleh melakukan kompresi tambahan pada file media saat membuat paket ZIP. Misalnya, saat menggunakan alat zip, Anda harus menggunakan opsi -n untuk menentukan akhiran file yang seharusnya tidak dikompresi:
zip -n .mp4;.ogg main_expansion media_files

Proses download

Biasanya, Google Play akan mendownload dan menyimpan file ekspansi sekaligus mendownload APK ke perangkat. Namun, dalam beberapa kasus, Google Play tidak dapat mendownload file ekspansi atau pengguna mungkin telah menghapus file ekspansi yang didownload sebelumnya. Untuk menangani situasi ini, aplikasi Anda harus dapat mendownload file itu sendiri saat aktivitas utama dimulai, menggunakan URL yang disediakan oleh Google Play.

Proses download dari tingkat tinggi akan terlihat seperti ini:

  1. Pengguna memilih untuk menginstal aplikasi dari Google Play.
  2. Jika Google Play dapat mendownload file ekspansi (yang berlaku untuk sebagian besar perangkat), Google Play akan mendownloadnya bersama dengan APK.

    Jika Google Play tidak dapat mendownload file ekspansi, maka akan mendownload APK saja.

  3. Saat pengguna meluncurkan aplikasi, aplikasi Anda harus memeriksa apakah file ekspansi sudah tersimpan di perangkat.
    1. Jika ya, aplikasi Anda siap digunakan.
    2. Jika tidak, aplikasi Anda harus mendownload file ekspansi melalui HTTP dari Google Play. Aplikasi Anda harus mengirim permintaan ke klien Google Play menggunakan layanan Pemberian Lisensi aplikasi Google Play, yang merespons dengan nama, ukuran file, dan URL untuk setiap file ekspansi. Dengan informasi ini, nantinya Anda dapat mendownload file dan menyimpannya ke lokasi penyimpanan yang tepat.

Perhatian: Penting bagi Anda untuk menyertakan kode yang diperlukan guna mendownload file perluasan dari Google Play jika file belum ada di perangkat saat aplikasi dimulai. Seperti yang telah dijelaskan di bagian berikut ini tentang Mendownload File Ekspansi, kami telah menyediakan library untuk Anda yang sangat memudahkan proses ini dan melakukan download dari layanan dengan jumlah kode minimal dari Anda.

Checklist pengembangan

Berikut adalah ringkasan tugas yang harus Anda lakukan untuk menggunakan file perluasan dengan aplikasi Anda:

  1. Pertama-tama tentukan apakah ukuran download terkompresi aplikasi Anda harus lebih dari 100 MB. Ruang penyimpanan sangat berharga dan Anda harus menjaga ukuran download total sekecil mungkin. Jika aplikasi Anda menggunakan lebih dari 100 MB untuk memberikan beberapa versi aset visual untuk beberapa kepadatan layar, pertimbangkan untuk memublikasikan beberapa APK di mana setiap APK hanya berisi aset yang diperlukan untuk layar yang ditargetkannya. Untuk hasil terbaik saat memublikasikan ke Google Play, upload Android App Bundle, yang mencakup semua kode dan resource terkompilasi aplikasi, tetapi serahkan pembuatan dan penandatanganan APK ke Google Play.
  2. Tentukan resource aplikasi mana yang akan dipisah dari APK dan kemas dalam file untuk digunakan sebagai file ekspansi utama.

    Biasanya, Anda seharusnya hanya menggunakan file ekspansi patch kedua saat melakukan pembaruan pada file ekspansi utama. Namun, jika resource Anda melebihi batas 2 GB untuk file ekspansi utama, Anda dapat menggunakan file patch untuk sisa aset Anda.

  3. Kembangkan aplikasi Anda sedemikian rupa menggunakan resource dari file ekspansi di lokasi penyimpanan bersama perangkat.

    Ingat bahwa Anda tidak boleh menghapus, memindahkan, atau mengganti nama file ekspansi.

    Jika aplikasi Anda tidak menuntut format tertentu, sebaiknya buat file ZIP untuk file ekspansi, lalu membacanya menggunakan Library Zip Ekspansi APK.

  4. Tambahkan logika ke aktivitas utama aplikasi Anda yang memeriksa apakah file perluasan berada di perangkat saat dimulai. Jika file tidak ada di perangkat, gunakan layanan Pemberian Lisensi aplikasi Google Play untuk meminta URL untuk file ekspansi, lalu download dan simpan file tersebut.

    Untuk mengurangi jumlah kode yang harus Anda tulis dan memastikan pengalaman pengguna yang baik selama proses download, sebaiknya gunakan Library Downloader untuk mengimplementasikan perilaku download Anda.

    Jika Anda membuat layanan download sendiri dan bukan menggunakan library, perhatikan bahwa Anda tidak boleh mengubah nama file ekspansi dan harus menyimpannya ke lokasi penyimpanan yang tepat.

Setelah Anda menyelesaikan pengembangan aplikasi, ikuti panduan untuk Menguji File Ekspansi Anda.

Aturan dan Batasan

Menambahkan file ekspansi APK adalah fitur yang tersedia saat Anda mengupload aplikasi menggunakan Konsol Play. Saat mengupload aplikasi untuk pertama kali atau mengupdate aplikasi yang menggunakan file ekspansi, Anda harus mengetahui aturan dan batasan berikut:

  1. Setiap file ekspansi tidak boleh lebih dari 2 GB.
  2. Untuk mendownload file ekspansi dari Google Play, pengguna harus mendapatkan aplikasi Anda dari Google Play. Google Play tidak akan memberikan URL untuk file ekspansi jika aplikasi diinstal dengan cara lain.
  3. Saat melakukan download dari dalam aplikasi, URL yang disediakan Google Play untuk setiap file bersifat unik untuk setiap download dan akan segera berakhir setelah diberikan ke aplikasi Anda.
  4. Jika mengupdate aplikasi dengan APK baru atau mengupload beberapa APK untuk aplikasi yang sama, Anda dapat memilih file perluasan yang telah diupload untuk APK sebelumnya. Nama file perluasan tidak berubah—hal ini mempertahankan versi yang diterima oleh APK ke file mana pun yang pertama kali dikaitkan.
  5. Jika menggunakan file ekspansi yang digabungkan dengan beberapa APK untuk menyediakan file ekspansi yang berbeda untuk perangkat yang berbeda, Anda tetap harus mengupload APK terpisah untuk setiap perangkat agar dapat memberikan nilai versionCode unik dan mendeklarasikan filter yang berbeda untuk setiap APK.
  6. Anda tidak dapat melakukan update pada aplikasi hanya dengan mengubah file ekspansi —Anda harus mengupload APK baru untuk mengupdate aplikasi. Jika perubahan hanya terkait dengan aset dalam file ekspansi, Anda dapat mengupdate APK hanya dengan mengubah versionCode (dan mungkin juga versionName.

  7. Jangan menyimpan data lain ke direktori obb/. Jika Anda harus mengekstrak beberapa data, simpan data tersebut ke lokasi yang ditentukan oleh getExternalFilesDir().
  8. Jangan menghapus atau mengganti nama file ekspansi .obb (kecuali jika Anda melakukan update). Jika melakukannya, Google Play (atau aplikasi Anda) akan berulang kali mendownload file ekspansi.
  9. Saat mengupdate file ekspansi secara manual, Anda harus menghapus file ekspansi sebelumnya.

Mendownload File Perluasan

Pada sebagian besar situasi, Google Play akan mendownload dan menyimpan file perluasan ke perangkat sekaligus menginstal atau mengupdate APK. Dengan cara ini, file ekspansi akan tersedia saat aplikasi Anda diluncurkan untuk pertama kalinya. Namun, dalam beberapa kasus, aplikasi Anda harus mendownload file ekspansi itu sendiri dengan memintanya dari URL yang disediakan untuk Anda sebagai respons dari layanan Pemberian Lisensi aplikasi Google Play.

Logika dasar yang diperlukan untuk mendownload file perluasan adalah sebagai berikut:

  1. Saat aplikasi Anda dimulai, cari file ekspansi pada lokasi penyimpanan bersama (dalam direktori Android/obb/<package-name>/).
    1. Jika sudah terdapat file ekspansi, berarti Anda sudah siap dan aplikasi dapat dilanjutkan.
    2. Jika tidak terdapat file ekspansi:
      1. Lakukan permintaan menggunakan Pemberian Lisensi aplikasi Google Play untuk mendapatkan nama, ukuran, dan URL file perluasan aplikasi Anda.
      2. Gunakan URL yang disediakan oleh Google Play untuk mendownload dan menyimpan file perluasan. Anda harus menyimpan file tersebut ke lokasi penyimpanan bersama (Android/obb/<package-name>/) dan menggunakan nama file persis yang diberikan berdasarkan respons Google Play.

        Catatan: URL yang disediakan oleh Google Play untuk file perluasan bersifat unik untuk setiap download dan akan segera berakhir setelah diberikan ke aplikasi Anda.

Jika aplikasi Anda gratis (bukan aplikasi berbayar), Anda mungkin belum pernah menggunakan layanan Pemberian Lisensi aplikasi. Layanan ini utamanya dirancang untuk membantu Anda menerapkan kebijakan pemberian lisensi untuk aplikasi Anda dan memastikan bahwa pengguna berhak menggunakan aplikasi Anda (pengguna berhak membelinya di Google Play). Untuk memfasilitasi fungsionalitas file ekspansi, layanan pemberian lisensi telah ditingkatkan untuk memberikan respons terhadap aplikasi Anda yang menyertakan URL file ekspansi aplikasi yang dihosting di Google Play. Jadi, meskipun aplikasi Anda gratis untuk pengguna, Anda harus menyertakan Library Verifikasi Lisensi (LVL) untuk menggunakan file ekspansi APK. Tentu saja, jika aplikasi Anda gratis, Anda tidak perlu menerapkan verifikasi lisensi—cukup gunakan library untuk melakukan permintaan yang menampilkan URL file ekspansi Anda.

Catatan: Baik aplikasi Anda gratis atau tidak, Google Play menampilkan URL file ekspansi hanya jika pengguna memperoleh aplikasi Anda dari Google Play.

Selain LVL, Anda memerlukan sekumpulan kode yang mendownload file ekspansi melalui koneksi HTTP dan menyimpannya ke lokasi yang tepat pada penyimpanan bersama perangkat. Saat Anda menerapkan prosedur ini ke aplikasi, ada beberapa masalah yang harus dipertimbangkan:

  • Perangkat mungkin tidak memiliki cukup ruang untuk file ekspansi, jadi Anda harus memeriksa sebelum memulai download dan memberi tahu pengguna jika tidak tersedia cukup ruang.
  • Proses download file harus dilakukan di layanan latar belakang untuk menghindari pemblokiran interaksi pengguna dan memungkinkan pengguna keluar dari aplikasi Anda saat proses download selesai.
  • Berbagai macam error mungkin terjadi selama permintaan dan proses download yang harus Anda tangani dengan baik.
  • Konektivitas jaringan dapat berubah selama download, sehingga Anda harus menangani perubahan tersebut dan jika terganggu, lanjutkan proses download tersebut jika memungkinkan.
  • Meskipun proses download terjadi di latar belakang, Anda harus memberikan notifikasi yang menunjukkan progres download, memberi tahu pengguna setelah selesai, dan mengarahkan pengguna kembali ke aplikasi Anda saat dipilih.

Guna menyederhanakan pekerjaan ini untuk Anda, kami telah membuat Library Downloader, yang meminta URL file ekspansi melalui layanan pemberian lisensi, mendownload file ekspansi, melakukan semua tugas yang tercantum di atas, dan bahkan mengizinkan aktivitas untuk menjeda dan melanjutkan download. Dengan menambahkan Library Downloader dan beberapa rangkaian kode ke aplikasi Anda, hampir semua pekerjaan untuk mendownload file ekspansi sudah dikodekan untuk Anda. Oleh karena itu, demi memberikan pengalaman pengguna terbaik dengan upaya minimal atas nama Anda, sebaiknya gunakan Library Downloader untuk mendownload file ekspansi Anda. Informasi di bagian berikut menjelaskan cara mengintegrasikan library ke aplikasi Anda.

Jika ingin mengembangkan solusi Anda sendiri untuk mendownload file ekspansi menggunakan URL Google Play, Anda harus mengikuti dokumentasi Pemberian Lisensi aplikasi untuk melakukan permintaan lisensi, lalu mengambil nama file ekspansi, ukuran, dan URL dari tambahan respons. Sebaiknya gunakan class APKExpansionPolicy (disertakan dalam Library Verifikasi Lisensi) sebagai kebijakan pemberian lisensi, yang mencatat nama file ekspansi, ukuran, dan URL dari layanan pemberian lisensi.

Tentang Library Downloader

Agar dapat menggunakan file ekspansi APK dengan aplikasi Anda dan memberikan pengalaman pengguna terbaik dengan upaya minimal atas nama Anda, sebaiknya gunakan Library Downloader yang disertakan dalam paket Library Perluasan APK Google Play. Library ini mendownload file ekspansi di layanan latar belakang, menampilkan notifikasi kepada pengguna beserta status downloadnya, menangani hilangnya konektivitas jaringan, melanjutkan download jika memungkinkan, dan banyak lagi.

Untuk menerapkan download file ekspansi menggunakan Library Downloader, yang perlu Anda lakukan adalah:

  • Luaskan subclass Service khusus dan subclass BroadcastReceiver yang masing-masing hanya memerlukan beberapa baris kode dari Anda.
  • Tambahkan beberapa logika ke aktivitas utama yang memeriksa apakah file ekspansi telah didownload dan, jika tidak, akan meminta proses download dan menampilkan UI progres.
  • Implementasikan antarmuka callback dengan beberapa metode dalam aktivitas utama Anda yang menerima update terkait progres download.

Bagian berikut menjelaskan cara menyiapkan aplikasi Anda menggunakan Library Downloader.

Bersiap untuk menggunakan Library Downloader

Agar dapat menggunakan Library Downloader, Anda harus mendownload dua paket dari SDK Manager dan menambahkan library yang sesuai ke aplikasi Anda.

Pertama, buka Android SDK Manager (Tools > SDK Manager), dan di bagian Appearance & Behavior > System Settings > Android SDK, pilih tab SDK Tools untuk memilih dan mendownload:

  • Paket Library Pemberian Lisensi Google Play
  • Paket Library Perluasan APK Google Play

Buat modul library baru untuk Library Verifikasi Lisensi dan Library Downloader. Untuk setiap library:

  1. Pilih File > New > New Module.
  2. Di jendela Create New Module, pilih Android Library, lalu pilih Next.
  3. Tentukan nama aplikasi/Library seperti "Library Pemberian Lisensi Google Play" dan "Library Downloader Google Play", pilih Minimum SDK level, lalu pilih Finish.
  4. Pilih File > Project Structure.
  5. Pilih tab Properties dan di Library Repository, masukkan library dari direktori <sdk>/extras/google/ (play_licensing/ untuk Library Verifikasi Lisensi atau play_apk_expansion/downloader_library/ untuk Library Downloader).
  6. Pilih OK untuk membuat modul baru.

Catatan: Library Downloader bergantung pada Library Verifikasi Lisensi. Pastikan Anda menambahkan Library Verifikasi Lisensi ke properti project Library Downloader.

Atau, dari command line, update project Anda untuk menyertakan library:

  1. Ubah direktori ke direktori <sdk>/tools/.
  2. Jalankan android update project dengan opsi --library untuk menambahkan LVL dan Library Downloader ke project Anda. Contoh:
    android update project --path ~/Android/MyApp \
    --library ~/android_sdk/extras/google/market_licensing \
    --library ~/android_sdk/extras/google/market_apk_expansion/downloader_library
    

Dengan menambahkan Library Verifikasi Lisensi dan Library Downloader ke aplikasi, Anda dapat dengan cepat mengintegrasikan kemampuan untuk mendownload file ekspansi dari Google Play. Format yang Anda pilih untuk file ekspansi dan cara Anda membacanya dari penyimpanan bersama merupakan penerapan terpisah yang harus Anda pertimbangkan berdasarkan kebutuhan aplikasi Anda.

Tips: Paket Ekspansi Apk menyertakan contoh aplikasi yang menunjukkan cara menggunakan Library Downloader dalam aplikasi. Contoh aplikasi tersebut menggunakan library ketiga yang tersedia di paket Ekspansi Apk yang disebut Library Zip Ekspansi APK. Jika Anda berencana menggunakan file ZIP untuk file ekspansi, sebaiknya tambahkan juga Library Zip Perluasan APK ke aplikasi Anda. Untuk informasi selengkapnya, lihat bagian di bawah ini tentang Menggunakan Library Zip Perluasan APK.

Mendeklarasikan izin pengguna

Untuk mendownload file ekspansi, Library Downloader memerlukan beberapa izin yang harus Anda deklarasikan dalam file manifes aplikasi. API tersebut adalah:

<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>

Catatan: Secara default, Library Downloader memerlukan API level 4, tetapi Library Zip Perluasan APK memerlukan API level 5.

Mengimplementasikan layanan downloader

Agar dapat mendownload di latar belakang, Library Downloader menyediakan subclass Service-nya sendiri bernama DownloaderService yang harus Anda perluas. Selain mendownload file ekspansi untuk Anda, DownloaderService juga:

  • Mendaftarkan BroadcastReceiver yang memproses perubahan pada konektivitas jaringan perangkat (siaran CONNECTIVITY_ACTION) untuk menjeda proses download jika diperlukan (misalnya karena kehilangan konektivitas) dan melanjutkan proses download jika memungkinkan (saat mendapatkan konektivitas).
  • Menjadwalkan alarm RTC_WAKEUP untuk mencoba kembali proses download apabila layanan dihentikan.
  • Membuat Notification kustom yang menampilkan progres download dan setiap perubahan error atau status.
  • Mengizinkan aplikasi secara manual menjeda dan melanjutkan proses download.
  • Memverifikasi bahwa penyimpanan bersama sudah terpasang dan tersedia, bahwa file yang diperlukan belum tersedia, dan bahwa ruang penyimpanan yang cukup sudah tersedia, semuanya sebelum mendownload file ekspansi. Lalu memberi tahu pengguna jika salah satu dari pernyataan tersebut tidak benar.

Yang perlu Anda lakukan adalah membuat class di aplikasi Anda yang memperluas class DownloaderService dan mengganti tiga metode untuk memberikan detail aplikasi tertentu:

getPublicKey()
Metode ini harus menampilkan string yang merupakan kunci publik RSA berenkode Base64 untuk akun penerbit Anda, yang tersedia dari halaman profil di Konsol Play (lihat Menyiapkan Pemberian Lisensi).
getSALT()
Metode ini harus menampilkan array byte acak yang digunakan Policy pemberian lisensi untuk membuat Obfuscator. Salt memastikan bahwa file SharedPreferences obfuscation tempat data lisensi Anda disimpan akan unik dan tidak dapat ditemukan.
getAlarmReceiverClassName()
Metode ini harus menampilkan nama class dari BroadcastReceiver di aplikasi Anda yang akan menerima alarm yang menunjukkan bahwa download harus dimulai ulang (yang mungkin terjadi jika layanan downloader tiba-tiba berhenti).

Misalnya, berikut adalah implementasi lengkap dari DownloaderService:

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

Pemberitahuan: Anda harus mengupdate nilai BASE64_PUBLIC_KEY menjadi kunci publik yang dimiliki oleh akun penerbit Anda. Anda dapat menemukan kuncinya di Konsol Developer di bagian informasi profil. Hal ini diperlukan bahkan saat menguji proses download Anda.

Pastikan untuk mendeklarasikan layanan dalam file manifes:

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

Mengimplementasikan penerima alarm

Untuk memantau progres download file dan memulai ulang proses download jika diperlukan, file DownloaderService menjadwalkan alarm RTC_WAKEUP yang mengirim Intent ke BroadcastReceiver di aplikasi Anda. Anda harus menentukan BroadcastReceiver untuk memanggil API dari Library Downloader yang memeriksa status download dan memulai ulang prosesnya jika diperlukan.

Cukup ganti metode onReceive() untuk memanggil DownloaderClientMarshaller.startDownloadServiceIfRequired().

Contoh:

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

Perhatikan bahwa ini adalah class yang harus Anda tampilkan namanya dalam metode getAlarmReceiverClassName() layanan Anda (lihat bagian sebelumnya).

Pastikan untuk mendeklarasikan penerima dalam file manifes Anda:

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

Memulai download

Aktivitas utama di aplikasi Anda (yang dimulai oleh ikon peluncur) bertanggung jawab untuk memverifikasi apakah file ekspansi sudah ada di perangkat dan memulai download jika belum ada.

Memulai download menggunakan Library Downloader memerlukan prosedur berikut:

  1. Periksa apakah file telah didownload.

    Library Downloader berisi beberapa API di class Helper untuk membantu proses ini:

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

    Misalnya, contoh aplikasi yang disediakan dalam paket Perluasan Apk memanggil metode berikut dalam metode onCreate() aktivitas untuk memeriksa apakah file ekspansi sudah ada di perangkat:

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

    Dalam hal ini, setiap objek XAPKFile menyimpan ukuran file dan nomor versi file ekspansi dan boolean yang diketahui, apakah merupakan file ekspansi utama atau tidak. (Lihat contoh class SampleDownloaderActivity aplikasi untuk detailnya.)

    Jika metode ini menampilkan false, maka aplikasi harus memulai proses download.

  2. Mulai download dengan memanggil metode statis DownloaderClientMarshaller.startDownloadServiceIfRequired(Context c, PendingIntent notificationClient, Class<?> serviceClass).

    Metode ini menggunakan parameter berikut:

    • context: Context aplikasi Anda.
    • notificationClient: Sebuah PendingIntent untuk memulai aktivitas utama. Parameter ini digunakan dalam Notification yang dibuat DownloaderService untuk menampilkan progres download. Saat pengguna memilih notifikasi, sistem akan memanggil PendingIntent yang Anda berikan di sini dan akan membuka aktivitas yang menampilkan progres download (biasanya aktivitas yang sama yang memulai download).
    • serviceClass: Objek Class untuk penerapan DownloaderService, diperlukan untuk memulai layanan dan memulai download jika diperlukan.

    Metode ini menampilkan integer yang menunjukkan apakah download diperlukan atau tidak. Nilai yang dimungkinkan adalah:

    • NO_DOWNLOAD_REQUIRED: Ditampilkan jika file sudah ada atau proses download sedang berlangsung.
    • LVL_CHECK_REQUIRED: Ditampilkan jika verifikasi lisensi diperlukan untuk mendapatkan URL file ekspansi.
    • DOWNLOAD_REQUIRED: Ditampilkan jika URL file ekspansi sudah diketahui, tetapi belum didownload.

    Perilaku untuk LVL_CHECK_REQUIRED dan DOWNLOAD_REQUIRED pada dasarnya sama dan biasanya Anda tidak perlu mengkhawatirkannya. Dalam aktivitas utama Anda yang memanggil startDownloadServiceIfRequired(), cukup periksa apakah respons tersebut adalah NO_DOWNLOAD_REQUIRED. Jika respons merupakan hal selain NO_DOWNLOAD_REQUIRED, Library Downloader akan memulai download dan Anda harus mengupdate UI aktivitas untuk menampilkan progres download (lihat langkah berikutnya). Jika respons merupakan NO_DOWNLOAD_REQUIRED, maka file tersebut telah tersedia dan aplikasi Anda dapat dimulai.

    Contoh:

    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. Saat startDownloadServiceIfRequired() menampilkan hal selain NO_DOWNLOAD_REQUIRED, buat instance IStub dengan memanggil DownloaderClientMarshaller.CreateStub(IDownloaderClient client, Class<?> downloaderService). IStub memberikan binding antara aktivitas Anda dengan layanan downloader, sehingga aktivitas Anda menerima callback terkait progres download.

    Agar dapat membuat instance IStub dengan memanggil CreateStub(), Anda harus meneruskan penerapan antarmuka IDownloaderClient dan implementasi DownloaderService. Bagian berikutnya tentang Menerima progres download membahas tentang antarmuka IDownloaderClient, yang biasanya harus Anda implementasikan di class Activity yang dapat memungkinkan Anda mengupdate UI aktivitas saat status download berubah.

    Sebaiknya panggil CreateStub() untuk membuat instance IStub selama metode onCreate() aktivitas Anda, setelah startDownloadServiceIfRequired() memulai download.

    Misalnya, dalam contoh kode sebelumnya untuk onCreate(), Anda dapat merespons hasil startDownloadServiceIfRequired() seperti ini:

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

    Setelah metode onCreate() ditampilkan, aktivitas Anda menerima panggilan ke onResume(), di mana Anda nantinya akan memanggil connect() di IStub, lalu meneruskannya ke aplikasi Anda Context. Sebaliknya, Anda harus memanggil disconnect() dalam callback onStop() aktivitas Anda.

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

    Dengan memanggil connect() di IStub, aktivitas Anda akan diikat ke DownloaderService, memungkinkan aktivitas menerima callback terkait perubahan status download melalui antarmuka IDownloaderClient.

Menerima progres download

Untuk menerima update terkait progres download dan untuk berinteraksi dengan DownloaderService, Anda harus mengimplementasikan antarmuka IDownloaderClient Library Downloader. Biasanya, aktivitas yang Anda gunakan untuk memulai download harus mengimplementasikan antarmuka ini untuk menampilkan progres download dan mengirim permintaan ke layanan.

Metode antarmuka yang diperlukan untuk IDownloaderClient adalah:

onServiceConnected(Messenger m)
Setelah membuat instance IStub dalam aktivitas, Anda akan menerima panggilan ke metode ini, yang akan meneruskan objek Messenger yang terhubung dengan instance DownloaderService Anda. Untuk mengirim permintaan ke layanan, seperti menjeda dan melanjutkan download, Anda harus memanggil DownloaderServiceMarshaller.CreateProxy() untuk menerima antarmuka IDownloaderService yang terhubung ke layanan.

Implementasi yang direkomendasikan akan terlihat seperti berikut:

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

Dengan objek IDownloaderService yang diinisialisasi, Anda dapat mengirim perintah ke layanan downloader, seperti menjeda dan melanjutkan download (requestPauseDownload() dan requestContinueDownload()).

onDownloadStateChanged(int newState)
Layanan download akan memanggilnya saat terjadi perubahan status download, seperti download dimulai atau selesai.

Nilai newState akan menjadi salah satu dari beberapa kemungkinan nilai yang ditentukan oleh salah satu konstanta STATE_* class IDownloaderClient.

Untuk memberikan pesan yang berguna kepada pengguna, Anda dapat meminta string yang sesuai untuk setiap status dengan memanggil Helpers.getDownloaderStringResourceIDFromState(). Tindakan ini akan menampilkan ID resource untuk salah satu string yang dipaketkan dengan Library Downloader. Misalnya, string "Download dijeda karena Anda roaming" akan sesuai dengan STATE_PAUSED_ROAMING.

onDownloadProgress(DownloadProgressInfo progress)
Layanan download memanggilnya untuk mengirimkan objek DownloadProgressInfo, yang menjelaskan berbagai informasi tentang progres download, termasuk perkiraan sisa waktu, kecepatan saat ini, progres keseluruhan, dan total sehingga Anda dapat mengupdate UI progres download.

Tips: Untuk contoh callback yang mengupdate UI progres download, lihat SampleDownloaderActivity di aplikasi contoh yang disediakan dengan paket Perluasan APK.

Beberapa metode publik untuk antarmuka IDownloaderService yang mungkin berguna bagi Anda di antaranya:

requestPauseDownload()
Menjeda download.
requestContinueDownload()
Melanjutkan download yang dijeda.
setDownloadFlags(int flags)
Menetapkan preferensi pengguna untuk jenis jaringan yang diperbolehkan mendownload file. Implementasi saat ini mendukung satu flag, FLAGS_DOWNLOAD_OVER_CELLULAR, tetapi Anda dapat menambahkan yang lain. Secara default, flag ini tidak diaktifkan, sehingga pengguna harus menggunakan Wi-Fi untuk mendownload file ekspansi. Sebaiknya berikan preferensi pengguna untuk mengaktifkan download melalui jaringan seluler. Dalam hal ini, Anda dapat memanggil:

Kotlin

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

Java

remoteService
    .setDownloadFlags(IDownloaderService.FLAGS_DOWNLOAD_OVER_CELLULAR);

Menggunakan APKExpansionPolicy

Jika memutuskan untuk membuat layanan downloader sendiri daripada menggunakan Library Downloader Google Play, Anda tetap harus menggunakan APKExpansionPolicy yang disediakan dalam Library Verifikasi Lisensi. Class APKExpansionPolicy hampir identik dengan ServerManagedPolicy (tersedia dalam Library Verifikasi Lisensi Google Play) tetapi menyertakan penanganan tambahan untuk tambahan respons file ekspansi APK.

Catatan: Jika Anda menggunakan Library Downloader seperti yang dibahas di bagian sebelumnya, library tersebut akan menjalankan semua interaksi dengan APKExpansionPolicy, sehingga Anda tidak perlu menggunakan class ini langsung.

Class tersebut menyertakan metode untuk membantu Anda mendapatkan informasi yang diperlukan terkait file perluasan yang tersedia:

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

Untuk informasi selengkapnya tentang cara menggunakan APKExpansionPolicy saat Anda tidak menggunakan Library Downloader, lihat dokumentasi untuk Menambahkan Lisensi ke Aplikasi Anda, yang menjelaskan cara mengimplementasikan kebijakan lisensi seperti ini.

Membaca File Ekspansi

Setelah file ekspansi APK disimpan di perangkat, cara Anda membaca file bergantung pada jenis file yang digunakan. Seperti yang dibahas dalam ringkasan, file ekspansi dapat berupa jenis file apa pun yang Anda inginkan, tetapi akan diganti namanya menggunakan format nama file tertentu dan disimpan ke <shared-storage>/Android/obb/<package-name>/.

Terlepas dari cara membaca file, Anda harus selalu memeriksa dulu apakah penyimpanan eksternal tersedia untuk dibaca atau tidak. Ada kemungkinan bahwa pengguna memiliki penyimpanan yang dipasang ke komputer melalui USB atau sebenarnya telah mengeluarkan kartu SD.

Catatan: Saat aplikasi dimulai, Anda harus selalu memeriksa apakah ruang penyimpanan eksternal tersedia dan dapat dibaca dengan memanggil getExternalStorageState(). Ini akan menampilkan salah satu dari beberapa kemungkinan string yang mewakili status penyimpanan eksternal. Agar dapat dibaca oleh aplikasi Anda, nilai yang ditampilkan harus MEDIA_MOUNTED.

Mendapatkan nama file

Seperti yang dijelaskan dalam ringkasan, file ekspansi APK disimpan menggunakan format nama file tertentu:

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

Untuk mendapatkan lokasi dan nama file ekspansi, Anda harus menggunakan metode getExternalStorageDirectory() dan getPackageName() untuk membuat jalur ke file Anda.

Berikut adalah metode yang dapat Anda gunakan di aplikasi untuk mendapatkan array yang berisi jalur lengkap ke kedua file ekspansi Anda:

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

Anda dapat memanggil metode ini dengan meneruskannya ke Context aplikasi Anda dan versi file ekspansi yang diinginkan.

Ada banyak cara untuk menentukan nomor versi file ekspansi. Salah satu cara sederhana adalah menyimpan versi dalam file SharedPreferences saat download dimulai, dengan mengkueri nama file ekspansi menggunakan metode getExpansionFileName(int index) class APKExpansionPolicy. Kemudian, Anda bisa mendapatkan kode versi dengan membaca file SharedPreferences saat ingin mengakses file perluasan.

Untuk informasi selengkapnya tentang membaca dari penyimpanan bersama, lihat dokumentasi Penyimpanan Data.

Menggunakan Library Zip Perluasan APK

Paket Perluasan APK Google Market menyertakan library yang disebut dengan Library Zip Perluasan APK (terletak di <sdk>/extras/google/google_market_apk_expansion/zip_file/). Ini adalah library opsional yang membantu Anda membaca file perluasan saat disimpan sebagai file ZIP. Dengan library ini, Anda dapat dengan mudah membaca resource dari file ekspansi ZIP sebagai sistem file virtual.

Library Zip Perluasan APK menyertakan class dan API berikut:

APKExpansionSupport
Menyediakan beberapa metode untuk mengakses file ZIP dan nama file ekspansi:
getAPKExpansionFiles()
Metode sama yang ditampilkan di atas yang menampilkan jalur file lengkap ke kedua file ekspansi.
getAPKExpansionZipFile(Context ctx, int mainVersion, int patchVersion)
Menampilkan ZipResourceFile yang merepresentasikan jumlah file utama dan file patch. Artinya, jika Anda menentukan mainVersion dan patchVersion, metode ini akan menampilkan ZipResourceFile yang memberikan akses baca ke semua data, dengan data file patch yang digabungkan di bagian atas file utama.
ZipResourceFile
Merepresentasikan file ZIP di penyimpanan bersama dan menjalankan semua tugas untuk memberikan sistem file virtual berdasarkan file ZIP Anda. Anda bisa mendapatkan instance menggunakan APKExpansionSupport.getAPKExpansionZipFile() atau ZipResourceFile dengan meneruskan kepadanya jalur ke file ekspansi Anda. Class ini menyertakan berbagai metode berguna, tetapi umumnya Anda tidak perlu mengakses sebagian besar metode tersebut. Beberapa metode penting adalah:
getInputStream(String assetPath)
Memberikan InputStream untuk membaca file dalam file ZIP. assetPath harus menjadi jalur ke file yang diinginkan, terkait dengan root pada isi file ZIP.
getAssetFileDescriptor(String assetPath)
Memberikan AssetFileDescriptor untuk file di dalam file ZIP. assetPath harus menjadi jalur ke file yang diinginkan, terkait dengan root pada isi file ZIP. Metode ini berguna untuk API Android tertentu yang memerlukan AssetFileDescriptor, seperti beberapa API MediaPlayer.
APEZProvider
Sebagian besar aplikasi tidak perlu menggunakan class ini. Class ini menentukan ContentProvider yang melakukan marshal pada data dari file ZIP melalui Uri penyedia konten agar dapat memberikan akses file untuk API Android tertentu yang mengharapkan akses Uri ke file media. Misalnya, class ini berguna jika Anda ingin memutar video dengan VideoView.setVideoURI().

Melewatkan kompresi ZIP file media

Jika Anda menggunakan file ekspansi untuk menyimpan file media, file ZIP masih memungkinkan Anda menggunakan panggilan pemutaran media Android yang memberikan kontrol offset dan durasi (seperti MediaPlayer.setDataSource() dan SoundPool.load()). Agar hal ini dapat bekerja, Anda tidak boleh menjalankan kompresi tambahan pada file media saat membuat paket ZIP. Misalnya, saat menggunakan alat zip, Anda harus menggunakan opsi -n untuk menentukan akhiran file yang seharusnya tidak dikompresi:

zip -n .mp4;.ogg main_expansion media_files

Membaca dari file ZIP

Saat menggunakan Library Zip Perluasan APK, membaca file dari ZIP biasanya memerlukan beberapa hal berikut:

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

Kode di atas memberikan akses ke file apa pun yang ada di file ekspansi utama atau file ekspansi patch, dengan membaca peta gabungan semua file dari kedua file tersebut. Semua yang Anda butuhkan untuk memberikan metode getAPKExpansionFile() adalah android.content.Context aplikasi dan nomor versi file ekspansi utama dan file ekspansi patch.

Jika lebih suka membaca dari file ekspansi tertentu, Anda dapat menggunakan konstruktor ZipResourceFile dengan jalur ke file ekspansi yang diinginkan:

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

Untuk informasi selengkapnya tentang menggunakan library ini untuk file ekspansi Anda, lihat class SampleDownloaderActivity aplikasi contoh, yang menyertakan kode tambahan untuk memverifikasi file yang didownload menggunakan CRC. Berhati-hatilah jika Anda menggunakan contoh ini sebagai dasar implementasi Anda sendiri, contoh ini mewajibkan Anda mendeklarasikan ukuran byte file perluasan Anda dalam array xAPKS.

Menguji file ekspansi Anda

Sebelum memublikasikan aplikasi, ada dua hal yang harus diuji: Membaca file ekspansi dan mendownload filenya.

Menguji pembacaan file

Sebelum mengupload aplikasi ke Google Play, Anda harus menguji kemampuan aplikasi untuk membaca file dari penyimpanan bersama. Yang perlu Anda lakukan adalah menambahkan file tersebut ke lokasi yang sesuai di penyimpanan bersama perangkat dan meluncurkan aplikasi Anda:

  1. Di perangkat, buat direktori yang sesuai di penyimpanan bersama tempat Google Play akan menyimpan file Anda.

    Misalnya, jika nama paket adalah com.example.android, Anda harus membuat direktori Android/obb/com.example.android/ di ruang penyimpanan bersama. (Sambungkan perangkat pengujian ke komputer untuk memasang penyimpanan bersama dan membuat direktori ini secara manual.)

  2. Tambahkan file ekspansi ke direktori tersebut secara manual. Pastikan Anda mengganti nama file agar sesuai dengan format nama file yang akan digunakan Google Play.

    Misalnya, terlepas dari jenis filenya, file ekspansi utama untuk aplikasi com.example.android seharusnya adalah main.0300110.com.example.android.obb. Kode versi dapat berupa nilai apa pun yang Anda inginkan. Cukup ingat:

    • file ekspansi utama selalu diawali dengan main dan file patch diawali dengan patch.
    • Nama paket selalu sama dengan nama APK tempat file dilampirkan di Google Play.
  3. Setelah file ekspansi berada di perangkat, Anda dapat menginstal dan menjalankan aplikasi untuk menguji file ekspansi.

Berikut adalah beberapa pengingat terkait penanganan file ekspansi:

  • Jangan menghapus atau mengganti nama file ekspansi .obb (bahkan jika Anda mengekstrak data ke lokasi lain). Jika melakukannya, Google Play (atau aplikasi Anda) akan berulang kali mendownload file ekspansi.
  • Jangan menyimpan data lain ke direktori obb/. Jika Anda harus mengekstrak beberapa data, simpan data tersebut ke lokasi yang ditentukan oleh getExternalFilesDir().

Menguji download file

Karena aplikasi Anda terkadang harus mendownload file ekspansi secara manual saat pertama kali dibuka, penting bagi Anda untuk menguji proses ini guna memastikan aplikasi Anda berhasil mengkueri URL, mendownload file, dan menyimpannya ke perangkat.

Untuk menguji implementasi prosedur download manual aplikasi, Anda dapat memublikasikannya ke track pengujian internal, sehingga hanya akan tersedia untuk penguji yang diizinkan. Jika semuanya berfungsi seperti yang diharapkan, aplikasi Anda akan mulai mendownload file ekspansi segera setelah aktivitas utama dimulai.

Catatan: Sebelumnya Anda dapat menguji aplikasi dengan mengupload versi "draf" yang tidak dipublikasikan. Fungsi ini tidak lagi didukung. Sebagai gantinya, Anda harus memublikasikannya ke track pengujian internal, tertutup, atau terbuka. Untuk informasi selengkapnya, lihat Aplikasi Draf Tidak Lagi Didukung.

Mengupdate Aplikasi Anda

Salah satu keuntungan besar menggunakan file ekspansi di Google Play adalah kemampuannya untuk mengupdate aplikasi tanpa mendownload ulang semua aset asli. Karena Google Play mengizinkan adanya dua file ekspansi dengan masing-masing APK, Anda dapat menggunakan file kedua sebagai "patch" yang memberikan update dan aset baru. Tindakan ini menghindari keharusan mendownload ulang file ekspansi utama yang mungkin terlalu besar dan mahal bagi pengguna.

file ekspansi patch secara teknis sama dengan file ekspansi utama dan baik sistem Android maupun Google Play tidak akan menjalankan proses patch yang sebenarnya di antara file perluasan utama dan patch. Kode aplikasi Anda harus menjalankan patch yang diperlukannya sendiri.

Jika Anda menggunakan file ZIP sebagai file ekspansi, Library Zip Ekspansi APK yang disertakan dengan paket Ekspansi APK akan menyertakan kemampuan untuk menggabungkan file patch dengan file ekspansi utama.

Catatan: Meskipun hanya perlu melakukan perubahan pada file perluasan patch, Anda masih harus mengupdate APK agar Google Play dapat menjalankan update. Jika Anda tidak memerlukan perubahan kode dalam aplikasi, cukup update versionCode di dalam manifes.

Selama Anda tidak mengubah file ekspansi utama yang terkait dengan APK di Konsol Play, pengguna yang sebelumnya menginstal aplikasi Anda tidak akan mendownload file ekspansi utama. Pengguna yang sudah ada hanya akan menerima APK terupdate dan file ekspansi patch baru (mempertahankan file ekspansi utama sebelumnya).

Berikut adalah beberapa masalah yang harus diperhatikan terkait update pada file ekspansi:

  • Hanya boleh ada dua file ekspansi untuk aplikasi Anda dalam satu waktu. Satu file perluasan utama dan satu file ekspansi patch. Selama update ke file, Google Play akan menghapus versi sebelumnya (dan begitu juga aplikasi Anda saat menjalankan update manual).
  • Saat menambahkan file ekspansi patch, sistem Android sebenarnya tidak akan melakukan patch pada aplikasi atau file ekspansi utama. Anda harus merancang aplikasi agar mendukung data patch. Akan tetapi, paket Perluasan APK menyertakan library untuk menggunakan file ZIP sebagai file ekspansi, yang menggabungkan data dari file patch ke file ekspansi utama, sehingga Anda dapat dengan mudah membaca semua data file ekspansi.