Come descritto nell'articolo Panoramica di Google Play Services, gli SDK basati su Google Play Services sono supportati da servizi sul dispositivo su dispositivi Android certificati da Google. Per risparmiare spazio di archiviazione e memoria nell'intera flotta di dispositivi, alcuni servizi vengono forniti come moduli che vengono installati su richiesta quando l'app richiede la funzionalità pertinente. Ad esempio, ML Kit fornisce questa opzione quando utilizzi i modelli in Google Play Services.
Nella maggior parte dei casi, l'SDK Google Play Services scarica e installa automaticamente i moduli necessari quando la tua app utilizza un'API che li richiede. Tuttavia, potresti voler avere un maggiore controllo sul processo, ad esempio quando vuoi migliorare l'esperienza utente installando il modulo in anticipo.
L'API ModuleInstallClient
ti consente di:
- Controlla se i moduli sono già installati sul dispositivo.
- Richiedi l'installazione dei moduli.
- Monitora l'avanzamento dell'installazione.
- Gestire gli errori durante il processo di installazione.
Questa guida mostra come utilizzare ModuleInstallClient
per gestire i moduli nell'app. Tieni presente che i seguenti snippet di codice utilizzano l'SDK TensorFlow Lite (play-services-tflite-java
) come esempio, ma questi passaggi sono applicabili a qualsiasi libreria integrata con OptionalModuleApi
.
Prima di iniziare
Per preparare l'app, completa i passaggi descritti nelle sezioni seguenti.
Prerequisiti per le app
Assicurati che il file di build dell'app utilizzi i seguenti valori:
- Un
minSdkVersion
di23
o superiore
Configura la tua app
Nel file
settings.gradle
di primo livello, includi il repository Maven di Google e il repository centrale Maven all'interno del bloccodependencyResolutionManagement
:dependencyResolutionManagement { repositories { google() mavenCentral() } }
Nel file di build Gradle del modulo (di solito
app/build.gradle
), aggiungi le dipendenze dei servizi Google Play perplay-services-base
eplay-services-tflite-java
:dependencies { implementation 'com.google.android.gms:play-services-base:18.7.2' implementation 'com.google.android.gms:play-services-tflite-java:16.4.0' }
Controllare se i moduli sono disponibili
Prima di provare a installare un modulo, puoi verificare se è già installato sul dispositivo. In questo modo, eviti richieste di installazione non necessarie.
Ottieni un'istanza di
ModuleInstallClient
:Kotlin
val moduleInstallClient = ModuleInstall.getClient(context)
Java
ModuleInstallClient moduleInstallClient = ModuleInstall.getClient(context);
Controlla la disponibilità di un modulo utilizzando il relativo
OptionalModuleApi
. Questa API è fornita dall'SDK Google Play Services che stai utilizzando.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... }
Java
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… });
Richiedere un'installazione posticipata
Se non hai bisogno del modulo immediatamente, puoi richiedere un'installazione posticipata. In questo modo, Google Play Services può installare il modulo in background, potenzialmente quando il dispositivo è inattivo e connesso alla rete Wi-Fi.
Ottieni un'istanza di
ModuleInstallClient
:Kotlin
val moduleInstallClient = ModuleInstall.getClient(context)
Java
ModuleInstallClient moduleInstallClient = ModuleInstall.getClient(context);
Invia la richiesta posticipata:
Kotlin
val optionalModuleApi = TfLite.getClient(context) moduleInstallClient.deferredInstall(optionalModuleApi)
Java
OptionalModuleApi optionalModuleApi = TfLite.getClient(context); moduleInstallClient.deferredInstall(optionalModuleApi);
Richiedere l'installazione urgente di un modulo
Se la tua app ha bisogno immediatamente del modulo, puoi richiedere un'installazione urgente. In questo modo, il modulo verrà installato il più rapidamente possibile, anche se ciò significa utilizzare i dati mobili.
Ottieni un'istanza di
ModuleInstallClient
:Kotlin
val moduleInstallClient = ModuleInstall.getClient(context)
Java
ModuleInstallClient moduleInstallClient = ModuleInstall.getClient(context);
(Facoltativo) Crea un
InstallStatusListener
per monitorare l'avanzamento dell'installazione.Se vuoi visualizzare l'avanzamento del download nella UI della tua app (ad esempio, con una barra di avanzamento), puoi creare un
InstallStatusListener
per ricevere aggiornamenti.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()
Java
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();
Configura
ModuleInstallRequest
e aggiungiOptionalModuleApi
alla richiesta: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()
Java
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();
Invia la richiesta di installazione:
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… }
Java
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... });
Testare l'app con FakeModuleInstallClient
Gli SDK di Google Play Services forniscono FakeModuleInstallClient
per consentirti di simulare i risultati delle API di installazione dei moduli nei test utilizzando l'inserimento delle dipendenze. In questo modo puoi testare il comportamento della tua app in diversi scenari
senza doverla distribuire su un dispositivo reale.
Prerequisiti per le app
Configura la tua app per utilizzare Hilt dependency injection framework.
Sostituisci ModuleInstallClient
con FakeModuleInstallClient
nel test
Per utilizzare FakeModuleInstallClient
nei test, devi sostituire il binding ModuleInstallClient
con l'implementazione fittizia.
Aggiungi dipendenza:
Nel file di build Gradle del modulo (di solito
app/build.gradle
), aggiungi le dipendenze di Google Play Services perplay-services-base-testing
nel test.dependencies { // other dependencies... testImplementation 'com.google.android.gms:play-services-base-testing:16.1.0' }
Crea un modulo Hilt per fornire
ModuleInstallClient
:Kotlin
@Module @InstallIn(ActivityComponent::class) object ModuleInstallModule { @Provides fun provideModuleInstallClient( @ActivityContext context: Context ): ModuleInstallClient = ModuleInstall.getClient(context) }
Java
@Module @InstallIn(ActivityComponent.class) public class ModuleInstallModule { @Provides public static ModuleInstallClient provideModuleInstallClient( @ActivityContext Context context) { return ModuleInstall.getClient(context); } }
Inserisci
ModuleInstallClient
nell'attività:Kotlin
@AndroidEntryPoint class MyActivity: AppCompatActivity() { @Inject lateinit var moduleInstallClient: ModuleInstallClient ... }
Java
@AndroidEntryPoint public class MyActivity extends AppCompatActivity { @Inject ModuleInstallClient moduleInstallClient; ... }
Sostituisci l'associazione nel test:
Kotlin
@UninstallModules(ModuleInstallModule::class) @HiltAndroidTest class MyActivityTest { ... private val context:Context = ApplicationProvider.getApplicationContext() private val fakeModuleInstallClient = FakeModuleInstallClient(context) @BindValue @JvmField val moduleInstallClient: ModuleInstallClient = fakeModuleInstallClient ... }
Java
@UninstallModules(ModuleInstallModule.class) @HiltAndroidTest class MyActivityTest { ... private static final Context context = ApplicationProvider.getApplicationContext(); private final FakeModuleInstallClient fakeModuleInstallClient = new FakeModuleInstallClient(context); @BindValue ModuleInstallClient moduleInstallClient = fakeModuleInstallClient; ... }
Simulare diversi scenari
Con FakeModuleInstallClient
puoi simulare diversi scenari, ad esempio:
- I moduli sono già installati.
- I moduli non sono disponibili sul dispositivo.
- La procedura di installazione non riesce.
- La richiesta di installazione posticipata ha esito positivo o negativo.
- La richiesta di installazione urgente ha esito positivo o negativo.
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... }
Java
@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... }
Simula il risultato per una richiesta di installazione posticipata
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... }
Java
@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... }
Simula il risultato per una richiesta di installazione urgente
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... }
Java
@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... }