مدیریت دردسترس بودن برای واحدهای خدمات Google Play درخواستی

همان‌طور که در مقاله نمای کلی خدمات Google Play توضیح داده شده است، کیت‌های توسعه نرم‌افزار با پشتیبانی «خدمات Google Play» توسط سرویس‌های درون‌دستگاهی در دستگاه‌های Android دارای گواهی Google پشتیبانی می‌شوند. برای صرفه‌جویی در فضای ذخیره‌سازی و حافظه در کل ناوگان دستگاه‌ها، برخی‌از سرویس‌ها به‌صورت پیمانه‌هایی ارائه می‌شوند که وقتی برنامه‌تان به عملکرد مربوطه نیاز دارد، به‌صورت درخواستی نصب می‌شوند. برای مثال، ML Kit این گزینه را هنگام استفاده از مدل‌ها در خدمات Google Play ارائه می‌دهد.

در بیشتر موارد، وقتی برنامه‌تان از میانای برنامه‌سازی کاربردی‌ای استفاده می‌کند که به این ماژول‌ها نیاز دارد، کیت توسعه نرم‌افزار خدمات Google Play ماژول‌های لازم را به‌طور خودکار بارگیری و نصب می‌کند. بااین‌حال، ممکن است بخواهید کنترل بیشتری بر این فرایند داشته باشید، مثلاً وقتی که می‌خواهید با نصب کردن ماژول ازقبل، تجربه کاربری را بهبود ببخشید.

‫ModuleInstallClient API به شما امکان می‌دهد:

  • بررسی کنید که آیا پودمان‌ها قبلاً در دستگاه نصب شده‌اند یا نه.
  • درخواست نصب واحدها.
  • پیشرفت نصب را پایش کنید.
  • خطاهای حین فرایند نصب را مدیریت کنید.

این راهنما به شما نشان می‌دهد که چگونه از ModuleInstallClient برای مدیریت کردن ماژول‌ها در برنامه‌تان استفاده کنید. توجه داشته باشید که تکه‌کدهای زیر از کیت توسعه نرم‌افزار TensorFlow Lite (play-services-tflite-java) به‌عنوان نمونه استفاده می‌کنند، اما این مراحل برای هر کتابخانه‌ای که با OptionalModuleApi ادغام شده است قابل‌اجرا است.

قبل‌از شروع

برای آماده‌سازی برنامه‌تان، مراحل بخش‌های زیر را تکمیل کنید.

پیش‌نیازهای برنامه

مطمئن شوید فایل ساخت برنامه‌تان از مقادیر زیر استفاده می‌کند:

  • ‫minSdkVersion با 23 یا بالاتر

پیکربندی برنامه

  1. در فایل settings.gradle سطح بالا، مخزن Maven متعلق به Google و مخزن مرکزی Maven را در بلوک dependencyResolutionManagement اضافه کنید:

    dependencyResolutionManagement {
        repositories {
            google()
            mavenCentral()
        }
    }
    
  2. در فایل ساخت Gradle ماژول (معمولاً app/build.gradle)، وابستگی‌های «خدمات Google Play» را برای play-services-base و play-services-tflite-java اضافه کنید:

    dependencies {
      implementation 'com.google.android.gms:play-services-base:18.11.1'
      implementation 'com.google.android.gms:play-services-tflite-java:16.5.0'
    }
    

بررسی دردسترس بودن واحدها

قبل‌از اینکه برای نصب کردن ماژول اقدام کنید، می‌توانید بررسی کنید که آیا قبلاً در دستگاه نصب شده است یا نه. این کار به شما کمک می‌کند از درخواست‌های نصب غیرضروری اجتناب کنید.

  1. نمونه‌ای از ModuleInstallClient را دریافت کنید:

    Kotlin

    val moduleInstallClient = ModuleInstall.getClient(context)

    جاوا

    ModuleInstallClient moduleInstallClient = ModuleInstall.getClient(context);
  2. دردسترس بودن ماژول را بااستفاده از OptionalModuleApi آن بررسی کنید. این API توسط کیت توسعه نرم‌افزار خدمات Google Play که استفاده می‌کنید ارائه می‌شود.

    Kotlin

    val optionalModuleApi = TfLite.getClient(context)
    moduleInstallClient
      .areModulesAvailable(optionalModuleApi)
      .addOnSuccessListener {
        if (it.areModulesAvailable()) {
          // Modules are present on the device...
        } else {
          // Modules are not present on the device...
        }
      }
      .addOnFailureListener {
        // Handle failure...
      }

    جاوا

    OptionalModuleApi optionalModuleApi = TfLite.getClient(context);
    moduleInstallClient
        .areModulesAvailable(optionalModuleApi)
        .addOnSuccessListener(
            response -> {
              if (response.areModulesAvailable()) {
                // Modules are present on the device...
              } else {
                // Modules are not present on the device...
              }
            })
        .addOnFailureListener(
            e -> {
              // Handle failure…
            });

درخواست نصب معوق

اگر بلافاصله به ماژول نیاز ندارید، می‌توانید درخواست نصب معوق کنید. این کار به «خدمات Google Play» اجازه می‌دهد تا ماژول را در پس‌زمینه نصب کند، احتمالاً زمانی که دستگاه غیرفعال است و به Wi-Fi متصل است.

  1. نمونه‌ای از ModuleInstallClient را دریافت کنید:

    Kotlin

    val moduleInstallClient = ModuleInstall.getClient(context)

    جاوا

    ModuleInstallClient moduleInstallClient = ModuleInstall.getClient(context);
  2. ارسال درخواست به‌تعویق‌افتاده:

    Kotlin

    val optionalModuleApi = TfLite.getClient(context)
    moduleInstallClient.deferredInstall(optionalModuleApi)

    جاوا

    OptionalModuleApi optionalModuleApi = TfLite.getClient(context);
    moduleInstallClient.deferredInstall(optionalModuleApi);

درخواست نصب فوری واحد

اگر برنامه‌تان فوراً به واحد نیاز دارد، می‌توانید درخواست نصب فوری کنید. این کار تلاش می‌کند تا ماژول را در اسرع وقت نصب کند، حتی اگر به‌معنای استفاده از داده تلفن همراه باشد.

  1. نمونه‌ای از ModuleInstallClient را دریافت کنید:

    Kotlin

    val moduleInstallClient = ModuleInstall.getClient(context)

    جاوا

    ModuleInstallClient moduleInstallClient = ModuleInstall.getClient(context);
  2. (اختیاری) برای نظارت بر پیشرفت نصب، InstallStatusListener ایجاد کنید.

    اگر می‌خواهید پیشرفت بارگیری را در میانای کاربر برنامه‌تان نمایش دهید (برای مثال، با نوار پیشرفت)، می‌توانید InstallStatusListener ایجاد کنید تا به‌روزرسانی‌ها را دریافت کنید.

    Kotlin

    inner class ModuleInstallProgressListener : InstallStatusListener {
      override fun onInstallStatusUpdated(update: ModuleInstallStatusUpdate) {
        // Progress info is only set when modules are in the progress of downloading.
        update.progressInfo?.let {
          val progress = (it.bytesDownloaded * 100 / it.totalBytesToDownload).toInt()
          // Set the progress for the progress bar.
          progressBar.setProgress(progress)
        }
    
        if (isTerminateState(update.installState)) {
          moduleInstallClient.unregisterListener(this)
        }
      }
    
      fun isTerminateState(@InstallState state: Int): Boolean {
        return state == STATE_CANCELED || state == STATE_COMPLETED || state == STATE_FAILED
      }
    }
    
    val listener = ModuleInstallProgressListener()

    جاوا

    static final class ModuleInstallProgressListener implements InstallStatusListener {
        @Override
        public void onInstallStatusUpdated(ModuleInstallStatusUpdate update) {
          ProgressInfo progressInfo = update.getProgressInfo();
          // Progress info is only set when modules are in the progress of downloading.
          if (progressInfo != null) {
            int progress =
                (int)
                    (progressInfo.getBytesDownloaded() * 100 / progressInfo.getTotalBytesToDownload());
            // Set the progress for the progress bar.
            progressBar.setProgress(progress);
          }
          // Handle failure status maybe…
    
          // Unregister listener when there are no more install status updates.
          if (isTerminateState(update.getInstallState())) {
    
            moduleInstallClient.unregisterListener(this);
          }
        }
    
        public boolean isTerminateState(@InstallState int state) {
          return state == STATE_CANCELED || state == STATE_COMPLETED || state == STATE_FAILED;
        }
      }
    
    InstallStatusListener listener = new ModuleInstallProgressListener();
  3. ModuleInstallRequest را پیکربندی کنید و OptionalModuleApi را به درخواست اضافه کنید:

    Kotlin

    val optionalModuleApi = TfLite.getClient(context)
    val moduleInstallRequest =
      ModuleInstallRequest.newBuilder()
        .addApi(optionalModuleApi)
        // Add more APIs if you would like to request multiple modules.
        // .addApi(...)
        // Set the listener if you need to monitor the download progress.
        // .setListener(listener)
        .build()

    جاوا

    OptionalModuleApi optionalModuleApi = TfLite.getClient(context);
    ModuleInstallRequest moduleInstallRequest =
        ModuleInstallRequest.newBuilder()
            .addApi(optionalModuleApi)
            // Add more API if you would like to request multiple modules
            //.addApi(...)
            // Set the listener if you need to monitor the download progress
            //.setListener(listener)
            .build();
  4. ارسال درخواست نصب:

    Kotlin

    moduleInstallClient
      .installModules(moduleInstallRequest)
      .addOnSuccessListener {
        if (it.areModulesAlreadyInstalled()) {
          // Modules are already installed when the request is sent.
        }
        // The install request has been sent successfully. This does not mean
        // the installation is completed. To monitor the install status, set an
        // InstallStatusListener to the ModuleInstallRequest.
      }
      .addOnFailureListener {
        // Handle failure…
      }

    جاوا

    moduleInstallClient.installModules(moduleInstallRequest)
        .addOnSuccessListener(
            response -> {
              if (response.areModulesAlreadyInstalled()) {
                // Modules are already installed when the request is sent.
              }
              // The install request has been sent successfully. This does not
              // mean the installation is completed. To monitor the install
              // status, set an InstallStatusListener to the
              // ModuleInstallRequest.
            })
        .addOnFailureListener(
            e -> {
              // Handle failure...
            });

آزمایش کردن برنامه با FakeModuleInstallClient

کیت‌های توسعه نرم‌افزار خدمات Google Play FakeModuleInstallClient را ارائه می‌دهند تا به شما اجازه دهند نتایج میاناهای برنامه‌سازی کاربردی نصب واحد را در آزمایش‌ها بااستفاده از تزریق وابستگی شبیه‌سازی کنید. این کار به شما کمک می‌کند رفتار برنامه‌تان را در سناریوهای مختلف بدون نیاز به استقرار آن در دستگاه واقعی آزمایش کنید.

پیش‌نیازهای برنامه

برنامه‌تان را برای استفاده از چارچوب تزریق وابستگی Hilt پیکربندی کنید.

جایگزین کردن ModuleInstallClient با FakeModuleInstallClient در آزمایش

برای استفاده از FakeModuleInstallClient در آزمایش‌هایتان، باید اتصال ModuleInstallClient را با پیاده‌سازی جعلی جایگزین کنید.

  1. افزودن وابستگی:

    در فایل ساخت Gradle ماژول (معمولاً app/build.gradle)، وابستگی‌های خدمات Google Play را برای play-services-base-testing در آزمایشتان اضافه کنید.

      dependencies {
        // other dependencies...
    
        testImplementation 'com.google.android.gms:play-services-base-testing:16.2.0'
      }
    
  2. یک واحد Hilt برای ارائه ModuleInstallClient ایجاد کنید:

    Kotlin

    @Module
    @InstallIn(ActivityComponent::class)
    object ModuleInstallModule {
    
      @Provides
      fun provideModuleInstallClient(
        @ActivityContext context: Context
      ): ModuleInstallClient = ModuleInstall.getClient(context)
    }

    جاوا

    @Module
    @InstallIn(ActivityComponent.class)
    public class ModuleInstallModule {
      @Provides
      public static ModuleInstallClient provideModuleInstallClient(
        @ActivityContext Context context) {
        return ModuleInstall.getClient(context);
      }
    }
  3. ModuleInstallClient را در فعالیت تزریق کنید:

    Kotlin

    @AndroidEntryPoint
    class MyActivity: AppCompatActivity() {
      @Inject lateinit var moduleInstallClient: ModuleInstallClient
    
      ...
    }

    جاوا

    @AndroidEntryPoint
    public class MyActivity extends AppCompatActivity {
      @Inject ModuleInstallClient moduleInstallClient;
    
      ...
    }
  4. جایگزین کردن اتصال در آزمایش:

    Kotlin

    @UninstallModules(ModuleInstallModule::class)
    @HiltAndroidTest
    class MyActivityTest {
      ...
      private val context:Context = ApplicationProvider.getApplicationContext()
      private val fakeModuleInstallClient = FakeModuleInstallClient(context)
      @BindValue @JvmField
      val moduleInstallClient: ModuleInstallClient = fakeModuleInstallClient
    
      ...
    }

    جاوا

    @UninstallModules(ModuleInstallModule.class)
    @HiltAndroidTest
    class MyActivityTest {
      ...
      private static final Context context = ApplicationProvider.getApplicationContext();
      private final FakeModuleInstallClient fakeModuleInstallClient = new FakeModuleInstallClient(context);
      @BindValue ModuleInstallClient moduleInstallClient = fakeModuleInstallClient;
    
      ...
    }

شبیه‌سازی سناریوهای مختلف

با FakeModuleInstallClient می‌توانید سناریوهای مختلفی را شبیه‌سازی کنید، ازجمله:

  • پیمانه‌ها قبلاً نصب شده‌اند.
  • ماژول‌ها در دستگاه دردسترس نیستند.
  • فرایند نصب ناموفق است.
  • درخواست نصب معوق موفقیت‌آمیز یا ناموفق باشد.
  • درخواست نصب فوری موفقیت‌آمیز باشد یا ناموفق.

Kotlin

@Test
fun checkAvailability_available() {
  // Reset any previously installed modules.
  fakeModuleInstallClient.reset()

  val availableModule = TfLite.getClient(context)
  fakeModuleInstallClient.setInstalledModules(api)

  // Verify the case where modules are already available...
}

@Test
fun checkAvailability_unavailable() {
  // Reset any previously installed modules.
  fakeModuleInstallClient.reset()

  // Do not set any installed modules in the test.

  // Verify the case where modules unavailable on device...
}

@Test
fun checkAvailability_failed() {
  // Reset any previously installed modules.
  fakeModuleInstallClient.reset()

  fakeModuleInstallClient.setModulesAvailabilityTask(Tasks.forException(RuntimeException()))

  // Verify the case where an RuntimeException happened when trying to get module's availability...
}

جاوا

@Test
public void checkAvailability_available() {
  // Reset any previously installed modules.
  fakeModuleInstallClient.reset();

  OptionalModuleApi optionalModuleApi = TfLite.getClient(context);
  fakeModuleInstallClient.setInstalledModules(api);

  // Verify the case where modules are already available...
}

@Test
public void checkAvailability_unavailable() {
  // Reset any previously installed modules.
  fakeModuleInstallClient.reset();

  // Do not set any installed modules in the test.

  // Verify the case where modules unavailable on device...
}

@Test
public void checkAvailability_failed() {
  fakeModuleInstallClient.setModulesAvailabilityTask(Tasks.forException(new RuntimeException()));

  // Verify the case where an RuntimeException happened when trying to get module's availability...
}

شبیه‌سازی نتیجه برای درخواست نصب معوق

Kotlin

@Test
fun deferredInstall_success() {
  fakeModuleInstallClient.setDeferredInstallTask(Tasks.forResult(null))

  // Verify the case where the deferred install request has been sent successfully...
}

@Test
fun deferredInstall_failed() {
  fakeModuleInstallClient.setDeferredInstallTask(Tasks.forException(RuntimeException()))

  // Verify the case where an RuntimeException happened when trying to send the deferred install request...
}

جاوا

@Test
public void deferredInstall_success() {
  fakeModuleInstallClient.setDeferredInstallTask(Tasks.forResult(null));

  // Verify the case where the deferred install request has been sent successfully...
}

@Test
public void deferredInstall_failed() {
  fakeModuleInstallClient.setDeferredInstallTask(Tasks.forException(new RuntimeException()));

  // Verify the case where an RuntimeException happened when trying to send the deferred install request...
}

شبیه‌سازی نتیجه برای درخواست نصب فوری

Kotlin

@Test
fun installModules_alreadyExist() {
  // Reset any previously installed modules.
  fakeModuleInstallClient.reset();

  OptionalModuleApi optionalModuleApi = TfLite.getClient(context);
  fakeModuleInstallClient.setInstalledModules(api);

  // Verify the case where the modules already exist when sending the install request...
}

@Test
fun installModules_withoutListener() {
  // Reset any previously installed modules.
  fakeModuleInstallClient.reset();

  // Verify the case where the urgent install request has been sent successfully...
}

@Test
fun installModules_withListener() {
  // Reset any previously installed modules.
  fakeModuleInstallClient.reset();

  // Generates a ModuleInstallResponse and set it as the result for installModules().
  val moduleInstallResponse = FakeModuleInstallUtil.generateModuleInstallResponse()
  fakeModuleInstallClient.setInstallModulesTask(Tasks.forResult(moduleInstallResponse))

  // Verify the case where the urgent install request has been sent successfully...

  // Generates some fake ModuleInstallStatusUpdate and send it to listener.
  val update = FakeModuleInstallUtil.createModuleInstallStatusUpdate(
    moduleInstallResponse.sessionId, STATE_COMPLETED)
  fakeModuleInstallClient.sendInstallUpdates(listOf(update))

  // Verify the corresponding updates are handled correctly...
}

@Test
fun installModules_failed() {
  fakeModuleInstallClient.setInstallModulesTask(Tasks.forException(RuntimeException()))

  // Verify the case where an RuntimeException happened when trying to send the urgent install request...
}

جاوا

@Test
public void installModules_alreadyExist() {
  // Reset any previously installed modules.
  fakeModuleInstallClient.reset();

  OptionalModuleApi optionalModuleApi = TfLite.getClient(context);
  fakeModuleInstallClient.setInstalledModules(api);

  // Verify the case where the modules already exist when sending the install request...
}

@Test
public void installModules_withoutListener() {
  // Reset any previously installed modules.
  fakeModuleInstallClient.reset();

  // Verify the case where the urgent install request has been sent successfully...
}

@Test
public void installModules_withListener() {
  // Reset any previously installed modules.
  fakeModuleInstallClient.reset();

  // Generates a ModuleInstallResponse and set it as the result for installModules().
  ModuleInstallResponse moduleInstallResponse =
      FakeModuleInstallUtil.generateModuleInstallResponse();
  fakeModuleInstallClient.setInstallModulesTask(Tasks.forResult(moduleInstallResponse));

  // Verify the case where the urgent install request has been sent successfully...

  // Generates some fake ModuleInstallStatusUpdate and send it to listener.
  ModuleInstallStatusUpdate update = FakeModuleInstallUtil.createModuleInstallStatusUpdate(
      moduleInstallResponse.getSessionId(), STATE_COMPLETED);
  fakeModuleInstallClient.sendInstallUpdates(ImmutableList.of(update));

  // Verify the corresponding updates are handled correctly...
}

@Test
public void installModules_failed() {
  fakeModuleInstallClient.setInstallModulesTask(Tasks.forException(new RuntimeException()));

  // Verify the case where an RuntimeException happened when trying to send the urgent install request...
}