یکپارچه سازی تحویل دارایی (کاتلین و جاوا)

از مراحل این راهنما برای دسترسی به بسته‌های دارایی برنامه خود از کد جاوا استفاده کنید.

ساخت برای کاتلین و جاوا

از مراحل زیر برای ایجاد Play Asset Delivery در بسته نرم افزار Android پروژه خود استفاده کنید. برای انجام این مراحل نیازی به استفاده از اندروید استودیو نیست.

  1. نسخه پلاگین Android Gradle را در فایل build.gradle پروژه خود به 4.0.0 یا بالاتر به روز کنید.

  2. در دایرکتوری سطح بالای پروژه خود، یک دایرکتوری برای بسته دارایی ایجاد کنید. این نام دایرکتوری به عنوان نام بسته دارایی استفاده می شود. نام بسته دارایی باید با یک حرف شروع شود و فقط شامل حروف، اعداد و زیرخط باشد.

  3. در پوشه asset pack یک فایل 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 ]"
        }
    }
    

    کاتلین

    // 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"]
    }
    

    کاتلین

    // 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'
    

    کاتلین

    // 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. بسته نرم افزاری اندروید را با Gradle بسازید . در بسته نرم افزاری تولید شده، دایرکتوری سطح ریشه اکنون شامل موارد زیر است:

    • 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"
    

    کاتلین

    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. (اختیاری) بسته نرم افزاری خود را برای پشتیبانی از فرمت های مختلف فشرده سازی بافت پیکربندی کنید.

با Play Asset Delivery API یکپارچه شوید

Play Asset Delivery Java API کلاس AssetPackManager را برای درخواست بسته‌های دارایی، مدیریت دانلودها و دسترسی به دارایی‌ها فراهم می‌کند. ابتدا حتماً کتابخانه تحویل دارایی Play را به پروژه خود اضافه کنید .

شما این API را با توجه به نوع تحویل بسته دارایی که می خواهید به آن دسترسی داشته باشید پیاده سازی می کنید. این مراحل در فلوچارت زیر نشان داده شده است.

نمودار جریان بسته دارایی برای زبان برنامه نویسی جاوا

شکل 1. نمودار جریان برای دسترسی به بسته های دارایی

تحویل در زمان نصب

بسته‌های دارایی که به‌عنوان install-time پیکربندی شده‌اند، بلافاصله با راه‌اندازی برنامه در دسترس هستند. از Java AssetManager API برای دسترسی به دارایی های ارائه شده در این حالت استفاده کنید:

کاتلین

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

جاوا

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() برای تعیین اندازه دانلود و اینکه آیا بسته در حال بارگیری است استفاده کنید.

کاتلین

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

جاوا

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

requestPackStates() یک تابع suspend است که یک شی AssetPackStates را برمی گرداند در حالی که getPackStates() یک روش ناهمزمان است که Task<AssetPackStates> برمی گرداند. متد packStates() یک شی AssetPackStates یک Map<String, AssetPackState> برمی گرداند. این نقشه شامل وضعیت هر بسته دارایی درخواستی است که با نام آن کلید زده شده است:

کاتلین

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

جاوا

Map<String, AssetPackState> AssetPackStates#packStates()

درخواست نهایی با موارد زیر نشان داده می شود:

کاتلین

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

جاوا

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() استفاده کنید که وضعیت را به عنوان یک عدد صحیح که مربوط به یک فیلد ثابت در کلاس AssetPackStatus است، برمی گرداند. بسته دارایی که هنوز نصب نشده است وضعیت AssetPackStatus.NOT_INSTALLED را دارد.

اگر درخواستی با شکست مواجه شد، از روش errorCode() استفاده کنید که مقدار بازگشتی آن مربوط به یک فیلد ثابت در کلاس AssetPackErrorCode است.

نصب کنید

از روش requestFetch() یا fetch() برای دانلود یک بسته دارایی برای اولین بار استفاده کنید یا برای تکمیل به روز رسانی یک بسته دارایی تماس بگیرید:

کاتلین

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

جاوا

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

این روش یک شی AssetPackStates حاوی لیستی از بسته ها و حالت ها و اندازه های دانلود اولیه آنها را برمی گرداند. اگر بسته دارایی درخواست شده از طریق requestFetch() یا fetch() در حال دانلود باشد، وضعیت دانلود برگردانده می شود و دانلود اضافی شروع نمی شود.

وضعیت های دانلود را مانیتور کنید

شما باید یک AssetPackStateUpdatedListener را برای ردیابی پیشرفت نصب بسته های دارایی پیاده سازی کنید. به‌روزرسانی‌های وضعیت در هر بسته برای پشتیبانی از ردیابی وضعیت بسته‌های دارایی جداگانه تجزیه می‌شوند. می‌توانید قبل از تکمیل همه دانلودهای دیگر برای درخواستتان، استفاده از بسته‌های دارایی موجود را شروع کنید.

کاتلین

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

جاوا

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

دانلودهای بزرگ

اگر دانلود بزرگتر از 200 مگابایت باشد و کاربر از Wi-Fi استفاده نکند، دانلود شروع نمی شود تا زمانی که کاربر صریحاً رضایت خود را برای ادامه دانلود با استفاده از اتصال داده تلفن همراه اعلام کند. به همین ترتیب، اگر بارگیری زیاد باشد و کاربر Wi-Fi را از دست بدهد، دانلود متوقف می‌شود و برای ادامه با استفاده از اتصال داده تلفن همراه، رضایت صریح لازم است. یک بسته متوقف شده دارای وضعیت WAITING_FOR_WIFI است. برای راه اندازی جریان UI برای درخواست رضایت از کاربر، از متد showConfirmationDialog() استفاده کنید.

توجه داشته باشید که اگر برنامه این روش را فراخوانی نکند، دانلود متوقف می‌شود و تنها زمانی که کاربر به اتصال Wi-Fi بازگشته است، به‌طور خودکار از سر گرفته می‌شود.

تایید کاربر مورد نیاز است

اگر بسته ای دارای وضعیت REQUIRES_USER_CONFIRMATION باشد، دانلود ادامه نمی یابد تا زمانی که کاربر گفتگوی نشان داده شده با showConfirmationDialog() را نپذیرد. این وضعیت زمانی رخ می‌دهد که برنامه توسط Play شناسایی نشود—مثلاً اگر برنامه از جانبی بارگذاری شده باشد. توجه داشته باشید که فراخوانی showConfirmationDialog() در این حالت باعث به‌روزرسانی برنامه می‌شود. پس از به روز رسانی، باید دوباره دارایی ها را درخواست کنید.

در زیر نمونه ای از اجرای شنونده است:

کاتلین

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

جاوا

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 در دایرکتوری ریشه بسته دارایی ذخیره می شوند. با استفاده از روش راحتی assetsPath() می توانید مسیر دایرکتوری assets را دریافت کنید. برای رسیدن به یک دارایی خاص از روش زیر استفاده کنید:

کاتلین

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

جاوا

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 آزمایش کنید .