שילוב העברת נכסים (Kotlin ו-Java)

בעזרת השלבים במדריך הזה תוכלו לגשת לחבילות הנכסים של האפליקציה מקוד ה-Java.

פיתוח גרסאות build ל-Kotlin ול-Java

כדי לבנות את Play Asset Delivery במכשיר Android של הפרויקט, צריך לפעול לפי השלבים הבאים קובץ App Bundle אין צורך להשתמש ב-Android Studio כדי לבצע את השלבים האלה.

  1. מעדכנים את הגרסה של הפלאגין Android Gradle בקובץ build.gradle של הפרויקט ל-4.0.0 ואילך.

  2. בספרייה ברמה העליונה של הפרויקט, יוצרים ספרייה של הנכס . שם הספרייה הזה משמש כשם של חבילת הנכסים. שמות של חבילות נכסים חייבים להתחיל באות ויכולים להכיל רק אותיות, מספרים וקו תחתון.

  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 של אפליקציית הפרויקט, צריך להוסיף את השם של כל חבילת נכסים בפרויקט שלכם, כפי שמוצג בהמשך:

    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. בחבילת האפליקציות שנוצרה, התיקייה ברמת השורש כוללת עכשיו את הפריטים הבאים:

    • asset-pack-name/manifest/AndroidManifest.xml: הגדרת המזהה ושל אופן המסירה של חבילת הנכסים
    • asset-pack-name/assets/your-asset-directories: הספרייה שמכילה את כל הנכסים שנשלחים כחלק מחבילת הנכסים

    Gradle יוצר את המניפסט לכל חבילת נכסים ומפיק את הספרייה assets/ בשבילכם.

  9. (אופציונלי) אם אתם מתכננים להשתמש בהעברה מסוג fast-follow ובהעברה על פי דרישה, צריך לכלול את ספריית העברת הנכסים של 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 לשליחת בקשות לקבלת חבילות נכסים, לניהול ההורדות ולגישה לנכסים. חשוב להוסיף את ספריית Play Asset Delivery לפרויקט לפני כן.

מטמיעים את ה-API הזה בהתאם לסוג המסירה של חבילת הנכסים שאליה רוצים לגשת. השלבים האלו מוצגים בתרשים הזרימה הבא.

דיאגרמת זרימה של חבילת נכסים לשפת התכנות Java

איור 1. תרשים זרימה לגישה לחבילות נכסים

מסירה בזמן ההתקנה

חבילות נכסים שהוגדרו כ-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");

משלוח מהיר ועל פי דרישה

בקטעים הבאים מוסבר איך לקבל מידע על חבילות נכסים לפני שמורידים אותם, איך קוראים ל-API כדי להתחיל את ההורדה, ואז איך לגשת לחבילות שהורדתם. הקטעים האלה חלים על fast-follow ועל on-demand חבילות נכסים.

בדיקת הסטטוס

כל חבילת נכסים מאוחסנת בתיקייה נפרדת באחסון הפנימי של האפליקציה. משתמשים ב getPackLocation() כדי לקבוע את תיקיית הבסיס של חבילת נכסים. השיטה הזו מחזירה את הערכים הבאים:

הערך המוחזר סטטוס
אובייקט AssetPackLocation תקין תיקיית הבסיס של חבילת הנכסים מוכנה לגישה מיידית בכתובת assetsPath()
null חבילה לא מוכרת של נכסים או נכסים לא זמינים

קבלת מידע על הורדה של חבילות נכסים

האפליקציות נדרשות לציין את גודל ההורדה לפני אחזור הנכס . משתמשים ב 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 מספקות את גודל חבילת הנכסים, את כמות הנתונים שהורדתם עד כה (אם ביקשת זאת) ואת הסכום שכבר הועבר לאפליקציה:

כדי לקבל את הסטטוס של חבילת נכסים, צריך להשתמש בפונקציה status() method, שמחזירה את הסטטוס כמספר שלם שתואם למספר קבוע בשדה AssetPackStatus בכיתה. חבילת נכסים שעדיין לא הותקנה תהיה בסטטוס AssetPackStatus.NOT_INSTALLED.

אם בקשה מסוימת נכשלת, משתמשים errorCode() ש-method עם הערך המוחזר תואם לשדה קבוע AssetPackErrorCode בכיתה.

התקנה

משתמשים בrequestFetch() או fetch() להורדת חבילת נכסים בפעם הראשונה או קריאה לעדכון של חבילת נכסים שצריך להשלים:

Kotlin

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

Java

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

השיטה הזו מחזירה אובייקט AssetPackStates שמכיל רשימה של חבילות, את הגדלים והסטטוסים הראשוניים שלהן להורדה. אם כבר מתבצעת הורדה של חבילת נכסים דרך requestFetch() או fetch(), ההורדה הסטטוס מוחזר ולא מתחילה הורדה נוספת.

מעקב אחרי מצבי ההורדה

עליך ליישם AssetPackStateUpdatedListener לעקוב אחר התקדמות ההתקנה של הנכס חבילות. עדכוני הסטטוס מפורטים לפי חבילה כדי לאפשר מעקב אחרי הסטטוס של חבילות נכסים ספציפיות. אפשר להתחיל להשתמש בחבילות הנכסים הזמינות לפני שכל ההורדות האחרות שקשורות לבקשה שלך הסתיימו.

Kotlin

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

Java

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

הורדות גדולות

אם קובץ ההורדה גדול מ- 200MB והמשתמש לא מחובר לרשת Wi-Fi, קובץ ההורדה לא מתחיל לפני שהמשתמש מביע הסכמה באופן מפורש להמשיך להוריד באמצעות חיבור לחבילת גלישה. באופן דומה, אם ההורדה גדולה המשתמש מאבד את חיבור ה-Wi-Fi, ההורדה מושהית ונדרשת הסכמה מפורשת להמשיך באמצעות חיבור לחבילת גלישה. לחבילה מושהית יש את המצב WAITING_FOR_WIFI. כדי להפעיל את התהליך בממשק המשתמש ולבקש מהמשתמשים להביע הסכמה, צריך להשתמש ב showConfirmationDialog() .

חשוב לזכור שאם האפליקציה לא קוראת לשיטה הזו, ההורדה מושהית ותתחדש באופן אוטומטי רק כשהמשתמש יחזור להתחבר ל-Wi-Fi.

אישור נדרש מהמשתמש

אם לחבילה יש את הסטטוס REQUIRES_USER_CONFIRMATION, ההורדה לא תמשיך עד שהמשתמש יאשר את תיבת הדו-שיח שמוצגת עם showConfirmationDialog(). הסטטוס הזה יכול להופיע כשהאפליקציה לא מזוהה על ידי Play – לדוגמה, אם האפליקציה נטענה ממקור לא ידוע. חשוב לזכור ששיחות טלפון showConfirmationDialog() במקרה כזה, האפליקציה תעודכן. לאחר העדכון, יהיה עליך כדי לבקש שוב את הנכסים.

דוגמה להטמעה של מאזין:

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 מכיל את התקדמות ההורדה, את סטטוס ההורדה ואת קודי השגיאות של הכשל.

גישה לחבילות נכסים

אפשר לגשת לחבילת נכסים באמצעות קריאות למערכת הקבצים אחרי שבקשת ההורדה מגיעה לסטטוס COMPLETED. משתמשים ב getPackLocation() כדי לקבל את תיקיית הבסיס של חבילת הנכסים.

הנכסים מאוחסנים בספרייה assets ברמה הבסיסית (root) של חבילת הנכסים אפשר לקבל את הנתיב לספרייה assets באמצעות ה-method הנוחה 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() כדי לתזמן את ההסרה של חבילת נכסים.

אחזור של מיקומים של מספר חבילות נכסים

אפשר להשתמש ב-getPackLocations() כדי לשלוח שאילתה לגבי הסטטוס של כמה חבילות נכסים בו-זמנית, וכתוצאה מכך תקבלו מפה של חבילות הנכסים והמיקומים שלהן. המפה הוחזרה על ידי getPackLocations() מכיל רשומה עבור כל חבילה שהורדו ומעודכנים כרגע.

השלב הבא

בודקים את Play Asset Delivery באופן מקומי ומ-Google Play.