Mengintegrasikan Asset Delivery (Kotlin & Java)

Gunakan langkah-langkah dalam panduan ini untuk mengakses paket aset aplikasi Anda dari kode Java.

Mem-build untuk Kotlin & Java

Gunakan langkah-langkah berikut untuk membuat Play Asset Delivery ke dalam Android App Bundle project Anda. Anda tidak perlu menggunakan Android Studio untuk melakukan langkah-langkah ini.

  1. Update versi plugin Android Gradle di file build.gradle project Anda ke 4.0.0 atau yang lebih baru.

  2. Di direktori level teratas project Anda, buat direktori untuk paket aset. Nama direktori ini digunakan sebagai nama paket aset. Nama paket aset harus diawali dengan huruf dan hanya boleh berisi huruf, angka, dan garis bawah.

  3. Dalam direktori paket aset, buat file build.gradle dan tambahkan kode berikut. Pastikan untuk menentukan nama paket aset dan satu jenis pengiriman saja:

    Groovy

    // In the asset pack’s build.gradle file:
    plugins {
      id 'com.android.asset-pack'
    }
    
    assetPack {
        packName = "asset-pack-name" // Directory name for the asset pack
        dynamicDelivery {
            deliveryType = "[ install-time | fast-follow | on-demand ]"
        }
    }
    

    Kotlin

    // In the asset pack’s build.gradle.kts file:
    plugins {
      id("com.android.asset-pack")
    }
    
    assetPack {
      packName.set("asset-pack-name") // Directory name for the asset pack
      dynamicDelivery {
        deliveryType.set("[ install-time | fast-follow | on-demand ]")
      }
    }
    
  4. Pada file build.gradle aplikasi di project, tambahkan nama setiap paket aset dalam project Anda seperti yang ditunjukkan di bawah ini:

    Groovy

    // In the app build.gradle file:
    android {
        ...
        assetPacks = [":asset-pack-name", ":asset-pack2-name"]
    }
    

    Kotlin

    // In the app build.gradle.kts file:
    android {
        ...
        assetPacks += listOf(":asset-pack-name", ":asset-pack2-name")
    }
    
  5. Dalam file settings.gradle project, sertakan semua paket aset dalam project Anda seperti yang ditunjukkan di bawah ini:

    Groovy

    // In the settings.gradle file:
    include ':app'
    include ':asset-pack-name'
    include ':asset-pack2-name'
    

    Kotlin

    // In the settings.gradle.kts file:
    include(":app")
    include(":asset-pack-name")
    include(":asset-pack2-name")
    
  6. Dalam direktori paket aset, buat subdirektori berikut: src/main/assets

  7. Tempatkan aset di direktori src/main/assets. Anda juga dapat membuat subdirektori di sini. Struktur direktori untuk aplikasi Anda sekarang akan terlihat seperti berikut:

    • build.gradle
    • settings.gradle
    • app/
    • asset-pack-name/build.gradle
    • asset-pack-name/src/main/assets/your-asset-directories
  8. Buat Android App Bundle menggunakan Gradle. Dalam app bundle yang dihasilkan, direktori level root kini menyertakan hal berikut:

    • asset-pack-name/manifest/AndroidManifest.xml: Mengonfigurasi mode pengiriman dan pengenal paket aset
    • asset-pack-name/assets/your-asset-directories: Direktori yang berisi semua aset yang dikirim sebagai bagian dari paket aset

    Gradle menghasilkan manifes untuk setiap paket aset dan menghasilkan direktori assets/ untuk Anda.

  9. (Opsional) Sertakan Library Play Core jika Anda berencana menggunakan pengiriman fast-follow dan on-demand

    Groovy

    implementation "com.google.android.play:core:$play_core_version"
    // For Kotlin use core-ktx
    implementation "com.google.android.play:core-ktx:$play_core_version"
    

    Kotlin

    implementation("com.google.android.play:core:$playCoreVersion")
    // For Kotlin use core-ktx
    implementation("com.google.android.play:core-ktx:$playCoreVersion")
    

  10. (Opsional) Konfigurasikan app bundle Anda untuk mendukung berbagai format kompresi tekstur.

Integrasi dengan Play Core API

Play Core Java API menyediakan class AssetPackManager untuk meminta paket aset, mengelola download, dan mengakses aset. Pastikan untuk terlebih dahulu Menambahkan Library Play Core ke project Anda.

Anda menerapkan API ini sesuai dengan jenis pengiriman paket aset yang ingin Anda akses. Langkah-langkah ini ditampilkan dalam diagram alir berikut.

Diagram alir paket aset untuk bahasa pemrograman Java

Gambar 1. Diagram alir untuk mengakses paket aset

Pengiriman waktu penginstalan

Paket aset yang dikonfigurasi sebagai install-time akan segera tersedia saat aplikasi diluncurkan. Gunakan Java AssetManager API untuk mengakses aset yang ditayangkan dalam mode ini:

Kotlin

import android.content.res.AssetManager
...
val context: Context = createPackageContext("com.example.app", 0)
val assetManager: AssetManager = context.assets
val stream: InputStream = assetManager.open("asset-name")

Java

import android.content.res.AssetManager;
...
Context context = createPackageContext("com.example.app", 0);
AssetManager assetManager = context.getAssets();
InputStream is = assetManager.open("asset-name");

Pengiriman fast-follow dan on-demand

Bagian berikut menunjukkan cara memperoleh informasi tentang paket aset sebelum didownload, cara memanggil API untuk memulai download, lalu cara mengakses paket yang didownload. Bagian ini berlaku untuk paket aset fast-follow dan on-demand.

Memeriksa status

Setiap paket aset disimpan dalam folder terpisah dalam penyimpanan internal aplikasi. Gunakan metode getPackLocation() untuk menentukan folder utama paket aset. Metode ini akan menampilkan nilai berikut:

Nilai yang ditampilkan Status
Objek AssetPackLocation yang valid Folder utama paket aset siap untuk akses langsung di assetsPath()
null Paket aset tidak dikenal atau aset tidak tersedia

Mendapatkan informasi download tentang paket aset

Aplikasi wajib mengungkapkan ukuran download sebelum mengambil paket aset. Gunakan requestPackStates() atau getPackStates() untuk menentukan ukuran download dan mengetahui status paket sudah didownload atau belum.

Kotlin

suspend fun requestPackStates(packNames: List<String>): AssetPackStates

Java

Task<AssetPackStates> getPackStates(List<String> packNames)

requestPackStates() adalah fungsi penangguhan yang menampilkan objek AssetPackStates, sedangkan getPackStates() adalah metode asinkron yang menampilkan Task<AssetPackStates>. Metode packStates() dari objek AssetPackStates akan menampilkan Map<String, AssetPackState>. Peta ini berisi status setiap paket aset yang diminta, yang terkunci berdasarkan namanya:

Kotlin

AssetPackStates#packStates(): Map<String, AssetPackState>

Java

Map<String, AssetPackState> AssetPackStates#packStates()

Permintaan akhir ditunjukkan oleh hal berikut:

Kotlin

const val assetPackName = "assetPackName"
coroutineScope.launch {
  try {
    val assetPackStates: AssetPackStates =
      manager.requestPackStates(listOf(assetPackName))
    val assetPackState: AssetPackState =
      assetPackStates.packStates()[assetPackName]
  } catch (e: RuntimeExecutionException) {
    Log.d("MainActivity", e.message)
  }
}

Java

final String assetPackName = "myasset";

assetPackManager
    .getPackStates(Collections.singletonList(assetPackName))
    .addOnCompleteListener(new OnCompleteListener<AssetPackStates>() {
        @Override
        public void onComplete(Task<AssetPackStates> task) {
            AssetPackStates assetPackStates;
            try {
                assetPackStates = task.getResult();
                AssetPackState assetPackState =
                    assetPackStates.packStates().get(assetPackName);
            } catch (RuntimeExecutionException e) {
                Log.d("MainActivity", e.getMessage());
                return;
            })

Metode AssetPackState berikut memberikan ukuran paket aset, jumlah yang didownload sejauh ini (jika diminta), dan jumlah yang telah ditransfer ke aplikasi:

Untuk mendapatkan status paket aset, gunakan metode status(), yang menampilkan status sebagai bilangan bulat yang sesuai dengan bidang konstan di class AssetPackStatus. Paket aset yang belum diinstal akan menampilkan status AssetPackStatus.NOT_INSTALLED.

Jika permintaan gagal, gunakan metode errorCode(), yang nilai pengembaliannya sesuai dengan bidang konstan di class AssetPackErrorCode.

Instal

Gunakan requestFetch() atau fetch() untuk mendownload paket aset untuk pertama kalinya atau meminta update paket aset yang perlu diselesaikan:

Kotlin

suspend fun AssetPackManager.requestFetch(packs: List<String>): AssetPackStates

Java

Task<AssetPackStates> fetch(List<String> packNames)

Metode ini menampilkan AssetPackStates yang berisi daftar paket serta status dan ukuran download awal. Jika paket aset yang diminta melalui requestFetch() atau fetch() sudah didownload, status download akan ditampilkan dan tidak ada download tambahan yang dimulai.

Memantau status download

Anda harus mengimplementasikan AssetPackStateUpdatedListener untuk melacak progres penginstalan paket aset. Update status dikelompokkan per paket untuk mendukung pelacakan status paket aset individual. Anda dapat mulai menggunakan paket aset yang tersedia sebelum semua download lainnya karena permintaan Anda sudah selesai.

Kotlin

fun registerListener(listener: AssetPackStateUpdatedListener)
fun unregisterListener(listener: AssetPackStateUpdatedListener)

Java

void registerListener(AssetPackStateUpdatedListener listener)
void unregisterListener(AssetPackStateUpdatedListener listener)

Hasil download berukuran besar

Jika hasil download lebih besar dari 150 MB dan pengguna tidak menggunakan Wi-Fi, download tidak akan dimulai sebelum pengguna memberikan izin secara eksplisit untuk melanjutkan download dengan menggunakan koneksi data seluler. Demikian pula, jika hasil download berukuran besar dan pengguna terputus dari Wi-Fi, download akan dijeda dan izin eksplisit diperlukan untuk melanjutkan proses melalui koneksi data seluler. Paket yang dijeda akan berstatus WAITING_FOR_WIFI. Untuk memicu alur UI yang meminta pengguna memberikan persetujuan, gunakan metode requestCellularDataConfirmation() atau showCellularDataConfirmation().

Perhatikan jika aplikasi tidak memanggil metode ini, download akan dijeda dan akan dilanjutkan secara otomatis hanya saat pengguna kembali berada dalam koneksi Wi-Fi.

Berikut adalah contoh penerapan pemroses:

Kotlin

assetPackManager.registerListener { assetPackState ->
  when(assetPackState.status()) {
    AssetPackStatus.PENDING -> {
      Log.i(TAG, "Pending")
    }
    AssetPackStatus.DOWNLOADING -> {
      val downloaded = assetPackState.bytesDownloaded()
      val totalSize = assetPackState.totalBytesToDownload()
      val percent = 100.0 * downloaded / totalSize

      Log.i(TAG, "PercentDone=" + String.format("%.2f", percent))
    }
    AssetPackStatus.TRANSFERRING -> {
      // 100% downloaded and assets are being transferred.
      // Notify user to wait until transfer is complete.
    }
    AssetPackStatus.COMPLETED -> {
      // Asset pack is ready to use. Start the game.
    }
    AssetPackStatus.FAILED -> {
      // Request failed. Notify user.
      Log.e(TAG, assetPackState.errorCode())
    }
    AssetPackStatus.CANCELED -> {
      // Request canceled. Notify user.
    }
    AssetPackStatus.WAITING_FOR_WIFI -> {
      if (!waitForWifiConfirmationShown) {
        coroutineScope.launch {
          val resultCode =
            assetPackManager.requestCellularDataConfirmation(this@MainActivity)
          if (resultCode == RESULT_OK) {
            Log.d(TAG, "Confirmation dialog has been accepted.")
          } else if (resultCode == RESULT_CANCELED) {
            Log.d(TAG, "Confirmation dialog has been denied by the user.")
          }
        }
        waitForWifiConfirmationShown = true
      }
    }
    AssetPackStatus.NOT_INSTALLED -> {
      // Asset pack is not downloaded yet.
    }
    AssetPackStatus.UNKNOWN -> {
      Log.wtf(TAG, "Asset pack status unknown")
    }
  }
}

Java

assetPackStateUpdateListener = new AssetPackStateUpdateListener() {
    @Override
    public void onStateUpdate(AssetPackState assetPackState) {
      switch (assetPackState.status()) {
        case AssetPackStatus.PENDING:
          Log.i(TAG, "Pending");
          break;

        case AssetPackStatus.DOWNLOADING:
          long downloaded = assetPackState.bytesDownloaded();
          long totalSize = assetPackState.totalBytesToDownload();
          double percent = 100.0 * downloaded / totalSize;

          Log.i(TAG, "PercentDone=" + String.format("%.2f", percent));
          break;

        case AssetPackStatus.TRANSFERRING:
          // 100% downloaded and assets are being transferred.
          // Notify user to wait until transfer is complete.
          break;

        case AssetPackStatus.COMPLETED:
          // Asset pack is ready to use. Start the game.
          break;

        case AssetPackStatus.FAILED:
          // Request failed. Notify user.
          Log.e(TAG, assetPackState.errorCode());
          break;

        case AssetPackStatus.CANCELED:
          // Request canceled. Notify user.
          break;

        case AssetPackStatus.WAITING_FOR_WIFI:
          if (!waitForWifiConfirmationShown) {
            assetPackManager.showCellularDataConfirmation(MainActivity.this)
              .addOnSuccessListener(new OnSuccessListener<Integer> () {
                @Override
                public void onSuccess(Integer resultCode) {
                  if (resultCode == RESULT_OK) {
                    Log.d(TAG, "Confirmation dialog has been accepted.");
                  } else if (resultCode == RESULT_CANCELED) {
                    Log.d(TAG, "Confirmation dialog has been denied by the user.");
                  }
                }
              });
            waitForWifiConfirmationShown = true;
          }
          break;

        case AssetPackStatus.NOT_INSTALLED:
          // Asset pack is not downloaded yet.
          break;
        case AssetPackStatus.UNKNOWN:
          Log.wtf(TAG, "Asset pack status unknown")
          break;
      }
    }
}

Atau, Anda dapat menggunakan getPackStates() untuk mendapatkan status download saat ini. AssetPackStates berisi progres download, status download, dan kode error kegagalan.

Mengakses paket aset

Anda dapat mengakses paket aset menggunakan panggilan sistem file setelah permintaan download mencapai status COMPLETED. Gunakan metode getPackLocation() untuk mendapatkan folder utama paket aset.

Aset disimpan di direktori assets dalam direktori utama paket aset. Anda dapat memperoleh jalur ke direktori assets dengan menggunakan metode praktis assetsPath(). Gunakan metode berikut untuk memperoleh jalur ke aset tertentu:

Kotlin

private fun getAbsoluteAssetPath(assetPack: String, relativeAssetPath: String): String? {
    val assetPackPath: AssetPackLocation =
      assetPackManager.getPackLocation(assetPack)
      // asset pack is not ready
      ?: return null

    val assetsFolderPath = assetPackPath.assetsPath()
    // equivalent to: FilenameUtils.concat(assetPackPath.path(), "assets")
    return FilenameUtils.concat(assetsFolderPath, relativeAssetPath)
}

Java

private String getAbsoluteAssetPath(String assetPack, String relativeAssetPath) {
    AssetPackLocation assetPackPath = assetPackManager.getPackLocation(assetPack);

    if (assetPackPath == null) {
        // asset pack is not ready
        return null;
    }

    String assetsFolderPath = assetPackPath.assetsPath();
    // equivalent to: FilenameUtils.concat(assetPackPath.path(), "assets");
    String assetPath = FilenameUtils.concat(assetsFolderPath, relativeAssetPath);
    return assetPath;
}

Metode Play Core API lainnya

Berikut ini adalah beberapa metode API tambahan yang mungkin ingin Anda gunakan pada aplikasi.

Membatalkan permintaan

Gunakan cancel() untuk membatalkan permintaan paket aset aktif. Perhatikan bahwa permintaan ini adalah operasi dengan upaya terbaik.

Menghapus paket aset

Gunakan requestRemovePack() atau removePack() untuk menjadwalkan penghapusan paket aset.

Mendapatkan lokasi beberapa paket aset

Gunakan getPackLocations() untuk membuat kueri status beberapa paket aset sekaligus, yang menampilkan peta paket aset dan lokasinya. Peta yang dikembalikan oleh getPackLocations() berisi entri untuk setiap paket yang saat ini didownload dan sudah diupdate.

Langkah berikutnya

Uji Play Asset Delivery secara lokal dan dari Google Play.