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

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

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

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

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

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

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

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

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

    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. สร้างไดเรกทอรีย่อยต่อไปนี้ในไดเรกทอรีแพ็กชิ้นงาน 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 สำหรับแต่ละแพ็กเกจเนื้อหาและเอาต์พุตassets/ ไดเรกทอรีให้คุณ

  9. (ไม่บังคับ) ใส่ไลบรารีการนำส่งชิ้นงานของ Play หากวางแผนที่จะใช้การนำส่งแบบติดตามอย่างรวดเร็วและแบบออนดีมานด์

    Groovy

    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 Asset Delivery API

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

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

แผนภาพโฟลว์ของแพ็กเกจเนื้อหาสำหรับภาษาโปรแกรม Java

รูปที่ 1 แผนภาพลำดับงานสำหรับการเข้าถึงแพ็กชิ้นงาน

การนำส่งเมื่อติดตั้ง

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

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 เพื่อเริ่มดาวน์โหลด และวิธีเข้าถึงแพ็กที่ดาวน์โหลด ส่วนเหล่านี้มีผลกับ fast-follow และ on-demand Asset Pack

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

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

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

ดูข้อมูลการดาวน์โหลดเกี่ยวกับ 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> แผนที่นี้มีสถานะของแพ็กเนื้อหาที่ขอแต่ละรายการ โดยมีชื่อเป็นคีย์ ดังนี้

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, จำนวนที่ดาวน์โหลดไปแล้ว (หากมีการ ขอ) และจำนวนที่โอนไปยังแอปแล้ว

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

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

ติดตั้ง

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

Kotlin

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

Java

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

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

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

คุณควรใช้ AssetPackStateUpdatedListener เพื่อติดตามความคืบหน้าในการติดตั้งแพ็กเกจเนื้อหา การอัปเดตสถานะจะแบ่งตามแพ็กเพื่อรองรับการติดตามสถานะของ 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

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

ระบบจะจัดเก็บชิ้นงานไว้ในไดเรกทอรี assets ภายในไดเรกทอรีรูทของแพ็กเกจชิ้นงาน คุณดูเส้นทางไปยังไดเรกทอรี 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;
}

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

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

ยกเลิกคำขอ

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

นำแพ็กชิ้นงานออก

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

รับตำแหน่งของแพ็กชิ้นงานหลายรายการ

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

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

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