ผสานรวมการส่งเนื้อหา (Kotlin และ Java)

ใช้ขั้นตอนในคู่มือนี้เพื่อเข้าถึง Asset Pack ของแอปจากโค้ด Java

สร้างสำหรับ Kotlin และ Java

ทำตามขั้นตอนต่อไปนี้เพื่อสร้างการนำส่งเนื้อหา Play ลงใน Android App Bundle ของโปรเจ็กต์ คุณไม่จำเป็นต้องใช้ Android Studio เพื่อทำตามขั้นตอนเหล่านี้

  1. อัปเดตเวอร์ชันของปลั๊กอิน Android Gradle ในไฟล์ build.gradle ของโปรเจ็กต์เป็น 4.0.0 ขึ้นไป

  2. ในไดเรกทอรีระดับบนสุดของโปรเจ็กต์ ให้สร้างไดเรกทอรีสำหรับ Asset Pack ระบบจะใช้ชื่อไดเรกทอรีนี้เป็นชื่อ Asset Pack ชื่อ Asset Pack ต้องขึ้นต้นด้วยตัวอักษรและมีได้เฉพาะตัวอักษร ตัวเลข และขีดล่างเท่านั้น

  3. ในไดเรกทอรี Asset Pack ให้สร้างไฟล์ build.gradle และเพิ่มโค้ดต่อไปนี้ ตรวจสอบว่าได้ระบุชื่อ Asset Pack และประเภทการนำส่งเพียงประเภทเดียว ดังนี้

    ดึงดูด

    // 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. ในไฟล์ build.gradle ของแอปในโปรเจ็กต์ ให้เพิ่มชื่อ Asset Pack ทุกรายการในโปรเจ็กต์ตามที่แสดงด้านล่าง

    ดึงดูด

    // 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. ในไฟล์ settings.gradle ของโปรเจ็กต์ ให้รวม Asset Pack ทั้งหมดในโปรเจ็กต์ตามที่แสดงด้านล่าง

    ดึงดูด

    // 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. ในไดเรกทอรี Asset Pack ให้สร้างไดเรกทอรีย่อยต่อไปนี้ src/main/assets

  7. วางเนื้อหาในไดเรกทอรี src/main/assets คุณสร้างไดเรกทอรีย่อยในนี้ได้ด้วย ตอนนี้โครงสร้างไดเรกทอรีของแอปควรมีลักษณะดังนี้

    • build.gradle
    • settings.gradle
    • app/
    • asset-pack-name/build.gradle
    • asset-pack-name/src/main/assets/your-asset-directories
  8. สร้าง Android App Bundle ด้วย Gradle ใน App Bundle ที่สร้างขึ้น ไดเรกทอรีระดับรูทจะมีสิ่งต่อไปนี้รวมอยู่ด้วย

    • asset-pack-name/manifest/AndroidManifest.xml: กำหนดค่าตัวระบุและโหมดการนำส่งของ Asset Pack
    • asset-pack-name/assets/your-asset-directories: ไดเรกทอรีที่มีเนื้อหาทั้งหมดที่ส่งเป็นส่วนหนึ่งของ Asset Pack

    Gradle จะสร้างไฟล์ Manifest สำหรับ Asset Pack แต่ละรายการและแสดงผลไดเรกทอรี assets/ ให้คุณ

  9. (ไม่บังคับ) รวมไลบรารีการนำส่งเนื้อหา Play หากคุณวางแผนที่จะใช้การนำส่งแบบ Fast-Follow และแบบออนดีมานด์

    ดึงดูด

    implementation "com.google.android.play:asset-delivery:2.3.0"
    // For Kotlin use asset-delivery-ktx
    implementation "com.google.android.play:asset-delivery-ktx:2.3.0"

    Kotlin

    implementation("com.google.android.play:asset-delivery:2.3.0")
    // For Kotlin use core-ktx
    implementation("com.google.android.play:asset-delivery-ktx:2.3.0")

  10. (ไม่บังคับ) กำหนดค่า App Bundle ให้รองรับรูปแบบการบีบอัดพื้นผิวที่แตกต่างกัน

ผสานรวมกับการนำส่งเนื้อหา Play API

Java API ของการนำส่งเนื้อหา Play มีคลาส AssetPackManager สำหรับขอ Asset Pack จัดการการดาวน์โหลด และเข้าถึงเนื้อหา อย่าลืมเพิ่มไลบรารีการนำส่งเนื้อหา Play ลงในโปรเจ็กต์ก่อน

คุณใช้ API นี้ตามประเภทการนำส่งของ Asset Pack ที่ต้องการเข้าถึง ขั้นตอนเหล่านี้แสดงในผังงานต่อไปนี้

แผนภาพขั้นตอนของ Asset Pack สำหรับภาษาโปรแกรม Java

รูปที่ 1 แผนภาพโฟลว์สำหรับการเข้าถึง Asset Pack

การนำส่งขณะติดตั้ง

Asset Pack ที่กำหนดค่าเป็น install-time จะพร้อมใช้งานทันทีเมื่อเปิดแอป ใช้ AssetManager API ของ Java เพื่อเข้าถึงเนื้อหาที่แสดงในโหมดนี้

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

การจัดส่งแบบเร่งด่วนและแบบออนดีมานด์

ส่วนต่อไปนี้จะแสดงวิธีรับข้อมูลเกี่ยวกับ Asset Pack ก่อนดาวน์โหลด วิธีเรียกใช้ API เพื่อเริ่มการดาวน์โหลด และวิธีเข้าถึงแพ็กเกจที่ดาวน์โหลด ส่วนเหล่านี้ใช้กับ Asset Pack fast-follow และ on-demand

ตรวจสอบสถานะ

ระบบจะจัดเก็บ Asset Pack แต่ละรายการไว้ในโฟลเดอร์แยกต่างหากในที่จัดเก็บข้อมูลภายในของแอป ใช้เมธอด getPackLocation() เพื่อระบุโฟลเดอร์รูทของ Asset Pack เมธอดนี้จะแสดงผลค่าต่อไปนี้

แสดงผลค่า สถานะ
ออบเจ็กต์ AssetPackLocation ที่ถูกต้อง โฟลเดอร์รูทของ Asset Pack พร้อมให้เข้าถึงได้ทันทีที่ assetsPath()
null ไม่รู้จัก Asset Pack หรือไม่มีเนื้อหา

ดูข้อมูลการดาวน์โหลดเกี่ยวกับ Asset Pack

แอปต้องเปิดเผยขนาดของการดาวน์โหลดก่อนดึงข้อมูล Asset Pack ใช้เมธอด requestPackStates() หรือ getPackStates() เพื่อกำหนดขนาดของการดาวน์โหลดและดูว่ากำลังดาวน์โหลดแพ็กอยู่หรือไม่

Kotlin

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

Java

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

requestPackStates() เป็นฟังก์ชันระงับที่แสดงผลออบเจ็กต์ AssetPackStates ส่วน getPackStates() เป็นเมธอดแบบอะซิงโครนัสที่แสดงผล Task<AssetPackStates> เมธอด packStates() ของออบเจ็กต์ AssetPackStates จะแสดงผล Map<String, AssetPackState> แผนที่นี้มีสถานะของ Asset Pack ที่ขอแต่ละรายการ โดยคีย์ตามชื่อ ดังนี้

Kotlin

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

Java

Map<String, AssetPackState> AssetPackStates#packStates()

คำขอสุดท้ายจะแสดงดังต่อไปนี้

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

เมธอด AssetPackState ต่อไปนี้จะระบุขนาดของ Asset Pack, จำนวนที่ดาวน์โหลดจนถึงตอนนี้ (หากมีการขอ) และจำนวนที่โอนไปยังแอปแล้ว

หากต้องการดูสถานะของ Asset Pack ให้ใช้เมธอด status() ซึ่งจะแสดงสถานะเป็นจำนวนเต็มที่สอดคล้องกับช่องค่าคงที่ในคลาส AssetPackStatus Asset Pack ที่ยังไม่ได้ติดตั้งจะมีสถานะเป็น AssetPackStatus.NOT_INSTALLED

หากคำขอล้มเหลว ให้ใช้เมธอด errorCode() ซึ่งค่าที่แสดงผลจะสอดคล้องกับช่องค่าคงที่ในคลาส AssetPackErrorCode

ติดตั้ง

ใช้เมธอด requestFetch() หรือ fetch() เพื่อดาวน์โหลด Asset Pack เป็นครั้งแรก หรือเรียกใช้การอัปเดต Asset Pack ให้เสร็จสมบูรณ์ โดยทำดังนี้

Kotlin

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

Java

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

เมธอดนี้จะแสดงผลออบเจ็กต์ AssetPackStates ที่มีรายการแพ็กเกจ รวมถึงสถานะและขนาดการดาวน์โหลดเริ่มต้น หาก Asset Pack ที่ขอผ่าน requestFetch() หรือ fetch() กำลังดาวน์โหลดอยู่ ระบบจะแสดงสถานะการดาวน์โหลดและจะไม่เริ่มการดาวน์โหลดเพิ่มเติม

ตรวจสอบสถานะการดาวน์โหลด

คุณควรใช้ AssetPackStateUpdatedListener เพื่อติดตามความคืบหน้าในการติดตั้งแพ็กเนื้อหา การอัปเดตสถานะจะแบ่งตามแพ็กเพื่อรองรับการติดตามสถานะของ Asset Pack แต่ละรายการ คุณเริ่มใช้ Asset Pack ที่พร้อมใช้งานได้ก่อนที่การดาวน์โหลดอื่นๆ ทั้งหมดสำหรับคำขอของคุณจะเสร็จสมบูรณ์

Kotlin

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

Java

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

การดาวน์โหลดขนาดใหญ่

หากการดาวน์โหลดมีขนาดใหญ่กว่า 200 MB และผู้ใช้ไม่ได้ใช้ Wi-Fi การดาวน์โหลดจะไม่เริ่มจนกว่าผู้ใช้จะให้ความยินยอมอย่างชัดแจ้งเพื่อดำเนินการดาวน์โหลดต่อโดยใช้การเชื่อมต่ออินเทอร์เน็ตมือถือ ในทำนองเดียวกัน หากการดาวน์โหลดมีขนาดใหญ่และผู้ใช้สูญเสียการเชื่อมต่อ Wi-Fi การดาวน์โหลดจะหยุดชั่วคราวและต้องได้รับความยินยอมอย่างชัดแจ้งเพื่อดำเนินการต่อโดยใช้การเชื่อมต่ออินเทอร์เน็ตมือถือ แพ็กที่หยุดชั่วคราวมีสถานะเป็น WAITING_FOR_WIFI หากต้องการเรียกใช้ขั้นตอน UI เพื่อขอความยินยอมจากผู้ใช้ ให้ใช้เมธอด showConfirmationDialog()

โปรดทราบว่าหากแอปไม่เรียกใช้เมธอดนี้ การดาวน์โหลดจะหยุดชั่วคราวและจะกลับมาทำงานอีกครั้งโดยอัตโนมัติเมื่อผู้ใช้กลับมาเชื่อมต่อ Wi-Fi เท่านั้น

ต้องมีการยืนยันจากผู้ใช้

หากแพ็กมีสถานะ REQUIRES_USER_CONFIRMATION การดาวน์โหลดจะไม่ดำเนินการต่อจนกว่าผู้ใช้จะยอมรับกล่องโต้ตอบที่แสดงพร้อมกับ showConfirmationDialog() สถานะนี้อาจเกิดขึ้นเมื่อ Play ไม่รู้จักแอป เช่น หากแอปเป็นแอปที่โหลดจากแหล่งที่ไม่รู้จัก โปรดทราบว่าการเรียกใช้ showConfirmationDialog() ในกรณีนี้จะทำให้แอปได้รับการอัปเดต หลังจากอัปเดตแล้ว คุณจะต้องขอชิ้นงานอีกครั้ง

ตัวอย่างการใช้งาน Listener มีดังนี้

Kotlin

private val activityResultLauncher = registerForActivityResult(
    ActivityResultContracts.StartIntentSenderForResult()
) { result ->
    if (result.resultCode == RESULT_OK) {
        Log.d(TAG, "Confirmation dialog has been accepted.")
    } else if (result.resultCode == RESULT_CANCELED) {
        Log.d(TAG, "Confirmation dialog has been denied by the user.")
    }
}

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,
    AssetPackStatus.REQUIRES_USER_CONFIRMATION -> {
      if (!confirmationDialogShown) {
        assetPackManager.showConfirmationDialog(activityResultLauncher);
        confirmationDialogShown = true
      }
    }
    AssetPackStatus.NOT_INSTALLED -> {
      // Asset pack is not downloaded yet.
    }
    AssetPackStatus.UNKNOWN -> {
      Log.wtf(TAG, "Asset pack status unknown")
    }
  }
}

Java

assetPackStateUpdateListener = new AssetPackStateUpdateListener() {
    private final ActivityResultLauncher<IntentSenderRequest> activityResultLauncher =
      registerForActivityResult(
          new ActivityResultContracts.StartIntentSenderForResult(),
          new ActivityResultCallback<ActivityResult>() {
            @Override
            public void onActivityResult(ActivityResult result) {
              if (result.getResultCode() == RESULT_OK) {
                Log.d(TAG, "Confirmation dialog has been accepted.");
              } else if (result.getResultCode() == RESULT_CANCELED) {
                Log.d(TAG, "Confirmation dialog has been denied by the user.");
              }
            }
          });

    @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:
        case AssetPackStatus.REQUIRES_USER_CONFIRMATION:
          if (!confirmationDialogShown) {
            assetPackManager.showConfirmationDialog(activityResultLauncher);
            confirmationDialogShown = true;
          }
          break;

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

หรือจะใช้เมธอด getPackStates() เพื่อดูสถานะการดาวน์โหลดปัจจุบันก็ได้ AssetPackStates มีข้อมูลความคืบหน้าในการดาวน์โหลด สถานะการดาวน์โหลด และรหัสข้อผิดพลาดที่ทำให้ดาวน์โหลดไม่สำเร็จ

เข้าถึง Asset Pack

คุณเข้าถึง Asset Pack ได้โดยใช้การเรียกใช้ระบบไฟล์หลังจากคำขอดาวน์โหลดถึงสถานะ COMPLETED ใช้เมธอด getPackLocation() เพื่อรับโฟลเดอร์รูทของ Asset Pack

ระบบจะจัดเก็บเนื้อหาไว้ในไดเรกทอรี assets ภายในไดเรกทอรีรากของ Asset Pack คุณดูเส้นทางไปยังไดเรกทอรี assets ได้โดยใช้วิธีที่สะดวก assetsPath() ใช้วิธีการต่อไปนี้เพื่อดูเส้นทางไปยังชิ้นงานที่เฉพาะเจาะจง

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

เมธอดอื่นๆ ของ Play Asset Delivery API

ต่อไปนี้คือเมธอด API เพิ่มเติมบางส่วนที่คุณอาจต้องการใช้ในแอป

ยกเลิกคำขอ

ใช้ cancel() เพื่อยกเลิกคำขอ Asset Pack ที่ใช้งานอยู่ โปรดทราบว่าคำขอนี้เป็นการดำเนินการที่พยายามอย่างเต็มที่

นำ Asset Pack ออก

ใช้ requestRemovePack() หรือ removePack() เพื่อกำหนดเวลาการนำ Asset Pack ออก

ดูตำแหน่งของ Asset Pack หลายรายการ

ใช้ getPackLocations() เพื่อค้นหาสถานะของ Asset Pack หลายรายการพร้อมกัน ซึ่งจะแสดงแผนที่ของ Asset Pack และตำแหน่งของ Asset Pack แผนที่ที่ getPackLocations() แสดงผลจะมีรายการสำหรับแต่ละแพ็กที่ดาวน์โหลดและเป็นเวอร์ชันล่าสุดในปัจจุบัน

ขั้นตอนถัดไป

ทดสอบการนำส่งเนื้อหา Play ในเครื่องและจาก Google Play