در دسترس بودن ماژول‌های خدمات Google Play درخواستی را مدیریت کنید

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

در بیشتر موارد، وقتی برنامه شما از API ای استفاده می‌کند که به ماژول‌های لازم نیاز دارد، SDK سرویس‌های گوگل پلی به طور خودکار آنها را دانلود و نصب می‌کند. با این حال، ممکن است بخواهید کنترل بیشتری بر این فرآیند داشته باشید، مانند زمانی که می‌خواهید با نصب ماژول از قبل، تجربه کاربری را بهبود بخشید.

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

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

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

قبل از اینکه شروع کنی

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

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

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

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

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

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

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

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

بررسی کنید که آیا ماژول‌ها در دسترس هستند یا خیر

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

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

    کاتلین

    val moduleInstallClient = ModuleInstall.getClient(context)

    جاوا

    ModuleInstallClient moduleInstallClient = ModuleInstall.getClient(context);
  2. با استفاده از OptionalModuleApi موجود بودن یک ماژول را بررسی کنید. این API توسط SDK سرویس‌های گوگل پلی که شما استفاده می‌کنید، ارائه می‌شود.

    کاتلین

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

درخواست نصب با تأخیر

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

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

    کاتلین

    val moduleInstallClient = ModuleInstall.getClient(context)

    جاوا

    ModuleInstallClient moduleInstallClient = ModuleInstall.getClient(context);
  2. ارسال درخواست معوق:

    کاتلین

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

    جاوا

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

درخواست نصب فوری ماژول

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

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

    کاتلین

    val moduleInstallClient = ModuleInstall.getClient(context)

    جاوا

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

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

    کاتلین

    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 را به درخواست اضافه کنید:

    کاتلین

    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. ارسال درخواست نصب:

    کاتلین

    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 تست کنید

کیت‌های توسعه نرم‌افزار (SDK) سرویس‌های گوگل پلی، FakeModuleInstallClient را ارائه می‌دهند تا به شما امکان دهند نتایج APIهای نصب ماژول را در تست‌ها با استفاده از تزریق وابستگی شبیه‌سازی کنید. این به شما کمک می‌کند تا رفتار برنامه خود را در سناریوهای مختلف بدون نیاز به استقرار آن در یک دستگاه واقعی آزمایش کنید.

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

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

در تست، ModuleInstallClient با FakeModuleInstallClient جایگزین کنید.

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

  1. وابستگی اضافه کنید:

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

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

    کاتلین

    @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 در اکتیویتی تزریق کنید:

    کاتلین

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

    جاوا

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

    کاتلین

    @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 ، می‌توانید سناریوهای مختلفی مانند موارد زیر را شبیه‌سازی کنید:

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

کاتلین

@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...
}

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

کاتلین

@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...
}

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

کاتلین

@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...
}