As described in the Overview of Google Play services article, SDKs powered by Google Play services are backed by on-device services on Google-certified Android devices. To conserve storage and memory across the entire fleet of devices, some services are provided as modules that are installed on-demand when your app requires the relevant functionality. For example, ML Kit provides this option when using models in Google Play services.
در بیشتر موارد، وقتی برنامه شما از API ای استفاده میکند که به ماژولهای لازم نیاز دارد، SDK سرویسهای گوگل پلی به طور خودکار آنها را دانلود و نصب میکند. با این حال، ممکن است بخواهید کنترل بیشتری بر این فرآیند داشته باشید، مانند زمانی که میخواهید با نصب ماژول از قبل، تجربه کاربری را بهبود بخشید.
The ModuleInstallClient API gives you the ability to:
- Check if the modules are already installed on the device.
- Request to install the modules.
- Monitor the installation progress.
- Handle errors during the installation process.
This guide shows you how to use ModuleInstallClient to manage modules in your app. Note that the following code snippets use the TensorFlow Lite SDK ( play-services-tflite-java ) as an example, but these steps are applicable for any library that's integrated with OptionalModuleApi .
قبل از اینکه شروع کنی
برای آمادهسازی برنامه خود، مراحل بخشهای زیر را تکمیل کنید.
پیشنیازهای برنامه
مطمئن شوید که فایل ساخت برنامه شما از مقادیر زیر استفاده میکند:
- A
minSdkVersionof23or higher
برنامه خود را پیکربندی کنید
In your top-level
settings.gradlefile, include Google's Maven repository and the Maven central repository within thedependencyResolutionManagementblock:dependencyResolutionManagement { repositories { google() mavenCentral() } }In your module's Gradle build file (usually
app/build.gradle), add the Google Play services dependencies forplay-services-baseandplay-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.5.0' }
Check if modules are available
Before you try to install a module, you can check if it's already installed on the device. This helps you avoid unnecessary installation requests.
Get an instance of
ModuleInstallClient:کاتلین
val moduleInstallClient = ModuleInstall.getClient(context)
جاوا
ModuleInstallClient moduleInstallClient = ModuleInstall.getClient(context);
Check the availability of a module using its
OptionalModuleApi. This API is provided by the Google Play services SDK that you are using.کاتلین
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… });
Request a deferred install
اگر فوراً به ماژول نیاز ندارید، میتوانید درخواست نصب معوق را بدهید. این به سرویسهای گوگل پلی اجازه میدهد تا ماژول را در پسزمینه نصب کنند، احتمالاً زمانی که دستگاه بیکار است و به وایفای متصل است.
Get an instance of
ModuleInstallClient:کاتلین
val moduleInstallClient = ModuleInstall.getClient(context)
جاوا
ModuleInstallClient moduleInstallClient = ModuleInstall.getClient(context);
Send the deferred request:
کاتلین
val optionalModuleApi = TfLite.getClient(context) moduleInstallClient.deferredInstall(optionalModuleApi)
جاوا
OptionalModuleApi optionalModuleApi = TfLite.getClient(context); moduleInstallClient.deferredInstall(optionalModuleApi);
Request an urgent module install
If your app needs the module immediately, you can request an urgent install. This will try to install the module as quickly as possible, even if it means using mobile data.
Get an instance of
ModuleInstallClient:کاتلین
val moduleInstallClient = ModuleInstall.getClient(context)
جاوا
ModuleInstallClient moduleInstallClient = ModuleInstall.getClient(context);
(Optional) Create an
InstallStatusListenerto monitor the install progress.If you want to display the download progress in your app's UI (for example, with a progress bar), you can create an
InstallStatusListenerto receive updates.کاتلین
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();
Configure the
ModuleInstallRequestand add theOptionalModuleApito the request:کاتلین
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();
Send the install request:
کاتلین
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... });
Test your app with FakeModuleInstallClient
Google Play services SDKs provide the FakeModuleInstallClient to allow you to simulate the results of the module install APIs in tests using dependency injection. This helps you to test your app's behavior in different scenarios without needing to deploy it to a real device.
پیشنیازهای برنامه
Configure your app to use Hilt dependency injection framework .
Replace ModuleInstallClient with FakeModuleInstallClient in test
To use FakeModuleInstallClient in your tests, you need to replace the ModuleInstallClient binding with the fake implementation.
Add dependency:
In your module's Gradle build file (usually
app/build.gradle), add the Google Play services dependencies forplay-services-base-testingin your test.dependencies { // other dependencies... testImplementation 'com.google.android.gms:play-services-base-testing:16.2.0' }Create a Hilt module to provide
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); } }
Inject the
ModuleInstallClientin the activity:کاتلین
@AndroidEntryPoint class MyActivity: AppCompatActivity() { @Inject lateinit var moduleInstallClient: ModuleInstallClient ... }
جاوا
@AndroidEntryPoint public class MyActivity extends AppCompatActivity { @Inject ModuleInstallClient moduleInstallClient; ... }
Replace the binding in test:
کاتلین
@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; ... }
Simulate different scenarios
With FakeModuleInstallClient , you can simulate different scenarios, such as:
- Modules are already installed.
- Modules are not available on the device.
- The installation process fails.
- The deferred install request is successful or fails.
- The urgent install request is successful or fails.
کاتلین
@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... }
Simulate result for a deferred install request
کاتلین
@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... }
Simulate result for an urgent 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... }