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

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

פיתוח כלים ל-Kotlin ו-Java

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

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

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

  3. בספרייה של חבילת הנכסים, יוצרים קובץ build.gradle ומוסיפים את באמצעות הקוד הבא. חשוב לציין את השם של חבילת הנכסים ורק אחת סוג משלוח:

    מגניב

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

    מגניב

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

    מגניב

    // 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-name/assets/your-asset-directories: ספרייה שמכילה את כל הנכסים שהועברו כחלק מחבילת הנכסים

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

  9. (אופציונלי) יש לכלול את ספריית Play Asset Delivery אם אתם מתכננים להשתמש במעקב מהיר ובמשלוח על פי דרישה.

    מגניב

    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

ממשק ה-API של Java ל-Play Asset Delivery מספקת AssetPackManager class לשליחה של בקשות לחבילות נכסים, ניהול הורדות וגישה לנכסים. קודם צריך להוסיף את הספרייה ל-Play Asset Delivery לפרויקט.

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

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

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

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

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

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

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

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

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

השלב הבא

בדיקה של העברת נכסים ב-Play באופן מקומי ודרך Google Play.