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

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

บิลด์สำหรับ Kotlin และ Java

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

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

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

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

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

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

    Groovy

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

    Kotlin

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

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

ผสานรวมกับ Play Asset Delivery API

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

คุณใช้ 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");

การแสดงโฆษณาตามอย่างรวดเร็วและแบบออนดีมานด์

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

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

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

ผลลัพธ์ สถานะ
ออบเจ็กต์ AssetPackLocation ที่ถูกต้อง โฟลเดอร์รูทของชุดชิ้นงานพร้อมให้เข้าถึงได้ทันทีที่ 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> แผนที่นี้มีสถานะของแพ็กเกจชิ้นงานแต่ละรายการที่ขอ โดยจัดเรียงตามชื่อ

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 Asset Pack ที่ยังไม่ได้ติดตั้งมีสถานะ 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 แต่ละรายการ คุณเริ่มใช้ 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 ภายในไดเรกทอรีรูทของแพ็กเกจชิ้นงาน คุณดูเส้นทางไปยังไดเรกทอรี 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() เพื่อยกเลิกคำขอแพ็กเกจชิ้นงานที่ใช้งานอยู่ โปรดทราบว่าคำขอนี้เป็นการดำเนินการ ที่ดีที่สุด

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

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

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

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

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

ทดสอบ Play Asset Delivery ในพื้นที่และจาก Google Play