Configurer la distribution à la demande

Les modules de fonctionnalités vous permettent de séparer certaines fonctionnalités et ressources du module de base de votre application et de les inclure à votre app bundle. Avec Play Feature Delivery, les utilisateurs peuvent, par exemple, télécharger et installer ces composants ultérieurement à la demande, après avoir installé au préalable l'APK de base de votre application.

Prenons l'exemple d'une application de messagerie qui inclut une fonctionnalité permettant de capturer et d'envoyer des messages photo, en sachant que cela ne concerne qu'un faible pourcentage d'utilisateurs. Il peut être judicieux d'inclure des messages illustrés en tant que module de fonctionnalité téléchargeable. Ainsi, le téléchargement initial de l'application est moins volumineux pour tous les utilisateurs, et seuls ceux qui envoient des messages photo doivent télécharger ce composant supplémentaire.

N'oubliez pas que cette modularisation nécessite davantage d'efforts et, parfois, une refactorisation du code existant de votre application. Par conséquent, réfléchissez bien aux fonctionnalités de votre application qui bénéficieraient le plus d'un accès à la demande. Pour mieux comprendre les cas d'utilisation optimaux et les consignes concernant les fonctionnalités à la demande, consultez la section Bonnes pratiques concernant l'expérience utilisateur pour la distribution à la demande.

Si vous souhaitez modulariser progressivement les fonctionnalités de l'application au fil du temps, sans activer les options de distribution avancées, telles que la distribution à la demande, configurez plutôt la distribution au moment de l'installation.

Cette page vous aide à ajouter un module de fonctionnalités à votre projet d'application et à le configurer pour la distribution à la demande. Avant de commencer, assurez-vous d'utiliser Android Studio 3.5 ou une version ultérieure, ainsi que le plug-in Android Gradle 3.5.0 ou version ultérieure.

Configurer un nouveau module de distribution à la demande

Le moyen le plus simple de créer un module de fonctionnalité consiste à utiliser Android Studio 3.5 ou une version ultérieure. Étant donné que les modules de fonctionnalité ont une dépendance inhérente au module d'application de base, vous ne pouvez les ajouter qu'aux projets d'application existants.

Pour ajouter un module de fonctionnalité à votre projet d'application à l'aide d'Android Studio, procédez comme suit :

  1. Si ce n'est pas déjà fait, ouvrez votre projet d'application dans l'IDE.
  2. Sélectionnez Fichier > Nouveau > Nouveau module dans la barre de menu.
  3. Dans la boîte de dialogue Créer un module, sélectionnez Module de fonctionnalités dynamiques, puis cliquez sur Suivant.
  4. Dans la section Configurer votre nouveau module, procédez comme suit :
    1. Sélectionnez le module d'application de base pour votre projet d'application dans le menu déroulant.
    2. Spécifiez un nom de module. L'IDE utilise ce nom pour identifier le module en tant que sous-projet Gradle dans votre fichier de paramètres Gradle. Lorsque vous créez votre app bundle, Gradle utilise le dernier élément du nom du sous-projet pour injecter l'attribut <manifest split> dans le fichier manifeste du module de fonctionnalité.
    3. Spécifiez le nom du package du module. Par défaut, Android Studio suggère un nom de package qui combine le nom du package racine du module de base et le nom du module que vous avez spécifié à l'étape précédente.
    4. Sélectionnez le niveau d'API minimal que le module doit accepter. Cette valeur doit correspondre à celle du module de base.
  5. Appuyez sur Suivant.
  6. Dans la section Options de téléchargement du module, procédez comme suit :

    1. Spécifiez le titre du module en utilisant jusqu'à 50 caractères. La plate-forme utilisera ce titre pour que les utilisateurs puissent identifier le module, par exemple pour confirmer le téléchargement. C'est pourquoi le module de base de votre application doit inclure le titre du module en tant que ressource de chaîne, que vous pouvez traduire. Lors de la création du module à l'aide d'Android Studio, l'IDE ajoute la ressource de chaîne au module de base et injecte l'entrée suivante dans le fichier manifeste du module de fonctionnalité :

      <dist:module
          ...
          dist:title="@string/feature_title">
      </dist:module>
      
    2. Dans le menu déroulant Inclusion à l'installation, sélectionnez Ne pas inclure le module au moment de l'installation. Android Studio injecte les éléments suivants dans le fichier manifeste du module pour refléter votre choix :

      <dist:module ... >
        <dist:delivery>
            <dist:on-demand/>
        </dist:delivery>
      </dist:module>
      
    3. Cochez la case Fusion si vous souhaitez que ce module soit disponible pour les appareils équipés d'Android 4.4 (niveau d'API 20) ou version antérieure et inclus dans plusieurs APK. Autrement dit, vous pouvez activer le comportement à la demande pour ce module et désactiver la fusion pour les appareils qui ne sont pas compatibles avec le téléchargement et l'installation d'APK divisés. Android Studio injecte les éléments suivants dans le fichier manifeste du module pour refléter votre choix :

      <dist:module ...>
          <dist:fusing dist:include="true | false" />
      </dist:module>
      
  7. Cliquez sur Terminer.

Une fois qu'Android Studio a terminé de créer votre module, examinez vous-même son contenu à partir du volet Projet. Pour y accéder, sélectionnez Affichage > Fenêtres d'outils > Projet dans la barre de menu. Le code, les ressources et l'organisation par défaut doivent être semblables à ceux du module d'application standard.

Vous devez ensuite mettre en œuvre la fonctionnalité d'installation à la demande à l'aide de la bibliothèque Play Feature Delivery.

Inclure la bibliothèque Play Feature Delivery dans votre projet

Avant de commencer, vous devez ajouter la bibliothèque Play Feature Delivery à votre projet.

Demander un module à la demande

Lorsque votre application doit utiliser un module de fonctionnalités, elle peut en demander un lorsqu'elle est exécutée au premier plan via la classe SplitInstallManager. Lorsque vous effectuez une requête, votre application doit spécifier le nom du module tel que défini par l'élément split dans le fichier manifeste du module cible. Lorsque vous créez un module de fonctionnalités à l'aide d'Android Studio, le système de compilation utilise le nom du module que vous avez fourni pour injecter cette propriété dans le fichier manifeste du module au moment de la compilation. Pour en savoir plus, consultez la section sur les fichiers manifestes pour les modules de fonctionnalités.

Prenons l'exemple d'une application qui dispose d'un module à la demande pour capturer et envoyer des messages photo à l'aide de la caméra de l'appareil. Ce module à la demande spécifie split="pictureMessages" dans son fichier manifeste. L'exemple suivant utilise SplitInstallManager pour demander le module pictureMessages (ainsi qu'un module supplémentaire pour certains filtres promotionnels) :

Kotlin

// Creates an instance of SplitInstallManager.
val splitInstallManager = SplitInstallManagerFactory.create(context)

// Creates a request to install a module.
val request =
    SplitInstallRequest
        .newBuilder()
        // You can download multiple on demand modules per
        // request by invoking the following method for each
        // module you want to install.
        .addModule("pictureMessages")
        .addModule("promotionalFilters")
        .build()

splitInstallManager
    // Submits the request to install the module through the
    // asynchronous startInstall() task. Your app needs to be
    // in the foreground to submit the request.
    .startInstall(request)
    // You should also be able to gracefully handle
    // request state changes and errors. To learn more, go to
    // the section about how to Monitor the request state.
    .addOnSuccessListener { sessionId -> ... }
    .addOnFailureListener { exception ->  ... }

Java

// Creates an instance of SplitInstallManager.
SplitInstallManager splitInstallManager =
    SplitInstallManagerFactory.create(context);

// Creates a request to install a module.
SplitInstallRequest request =
    SplitInstallRequest
        .newBuilder()
        // You can download multiple on demand modules per
        // request by invoking the following method for each
        // module you want to install.
        .addModule("pictureMessages")
        .addModule("promotionalFilters")
        .build();

splitInstallManager
    // Submits the request to install the module through the
    // asynchronous startInstall() task. Your app needs to be
    // in the foreground to submit the request.
    .startInstall(request)
    // You should also be able to gracefully handle
    // request state changes and errors. To learn more, go to
    // the section about how to Monitor the request state.
    .addOnSuccessListener(sessionId -> { ... })
    .addOnFailureListener(exception -> { ... });

Lorsque votre application sollicite un module à la demande, la bibliothèque Play Feature Delivery utilise une stratégie "fire-and-forget". Autrement dit, il envoie la requête de téléchargement du module à la plate-forme, mais ne surveille pas si l'installation a réussi. Pour faire progresser le parcours utilisateur après l'installation ou pour gérer correctement les erreurs, veillez à surveiller l'état de la requête.

Remarque : Vous pouvez demander un module de fonctionnalité déjà installé sur l'appareil. L'API considère instantanément la requête comme terminée si elle détecte que le module est déjà installé. En outre, une fois le module installé, Google Play le met à jour automatiquement. Autrement dit, lorsque vous importez une nouvelle version de votre app bundle, la plate-forme met à jour tous les APK installés appartenant à votre application. Pour en savoir plus, consultez Gérer la mise à jour des applications.

Pour pouvoir accéder au code et aux ressources du module, votre application doit activer SplitCompat. Notez que SplitCompat n'est pas nécessaire pour les applis instantanées Android.

Reporter l'installation des modules à la demande

S'il n'est pas nécessaire que le module à la demande soit téléchargé et installé immédiatement, vous pouvez différer l'installation jusqu'à ce que l'application se lance en arrière-plan. Par exemple, si vous souhaitez précharger certains supports promotionnels pour un lancement ultérieur de votre application.

Vous pouvez spécifier un module à télécharger ultérieurement à l'aide de la méthode deferredInstall(), comme indiqué ci-dessous. Contrairement à SplitInstallManager.startInstall(), votre application n'a pas besoin d'être au premier plan pour lancer une requête d'installation différée.

Kotlin

// Requests an on demand module to be downloaded when the app enters
// the background. You can specify more than one module at a time.
splitInstallManager.deferredInstall(listOf("promotionalFilters"))

Java

// Requests an on demand module to be downloaded when the app enters
// the background. You can specify more than one module at a time.
splitInstallManager.deferredInstall(Arrays.asList("promotionalFilters"));

Les demandes d'installations différées sont traitées aussi efficacement que possible, et vous ne pouvez pas suivre leur progression. Par conséquent, avant d'essayer d'accéder à un module que vous avez spécifié pour l'installation différée, vous devez vérifier qu'il a bien été installé. Si vous souhaitez que le module soit disponible immédiatement, utilisez plutôt SplitInstallManager.startInstall() pour le demander, comme indiqué dans la section précédente.

Surveiller l'état de la requête

Pour remplir une barre de progression, déclencher un intent après l'installation ou gérer correctement une erreur de requête, vous devez écouter les notifications d'état de la tâche asynchrone SplitInstallManager.startInstall(). Avant de pouvoir recevoir les notifications de votre demande d'installation, enregistrez un écouteur et obtenez l'ID de session correspondant à la requête, comme indiqué ci-dessous.

Kotlin

// Initializes a variable to later track the session ID for a given request.
var mySessionId = 0

// Creates a listener for request status updates.
val listener = SplitInstallStateUpdatedListener { state ->
    if (state.sessionId() == mySessionId) {
      // Read the status of the request to handle the state update.
    }
}

// Registers the listener.
splitInstallManager.registerListener(listener)

...

splitInstallManager
    .startInstall(request)
    // When the platform accepts your request to download
    // an on demand module, it binds it to the following session ID.
    // You use this ID to track further status updates for the request.
    .addOnSuccessListener { sessionId -> mySessionId = sessionId }
    // You should also add the following listener to handle any errors
    // processing the request.
    .addOnFailureListener { exception ->
        // Handle request errors.
    }

// When your app no longer requires further updates, unregister the listener.
splitInstallManager.unregisterListener(listener)

Java

// Initializes a variable to later track the session ID for a given request.
int mySessionId = 0;

// Creates a listener for request status updates.
SplitInstallStateUpdatedListener listener = state -> {
    if (state.sessionId() == mySessionId) {
      // Read the status of the request to handle the state update.
    }
};

// Registers the listener.
splitInstallManager.registerListener(listener);

...

splitInstallManager
    .startInstall(request)
    // When the platform accepts your request to download
    // an on demand module, it binds it to the following session ID.
    // You use this ID to track further status updates for the request.
    .addOnSuccessListener(sessionId -> { mySessionId = sessionId; })
    // You should also add the following listener to handle any errors
    // processing the request.
    .addOnFailureListener(exception -> {
        // Handle request errors.
    });

// When your app no longer requires further updates, unregister the listener.
splitInstallManager.unregisterListener(listener);

Gérer les erreurs de requête

Gardez à l'esprit que l'installation à la demande de modules de fonctionnalités peut parfois échouer, tout comme l'installation d'applications n'aboutit pas toujours. L'échec peut être dû à des problèmes tels qu'un espace de stockage insuffisant sur l'appareil, une mauvaise connexion réseau ou un utilisateur non connecté au Google Play Store. Pour obtenir des suggestions sur la gestion de ces situations du point de vue de l'utilisateur, consultez nos consignes relatives à l'expérience utilisateur pour la distribution à la demande.

En termes de code, vous devez gérer les échecs de téléchargement ou d'installation d'un module via addOnFailureListener(), comme indiqué ci-dessous :

Kotlin

splitInstallManager
    .startInstall(request)
    .addOnFailureListener { exception ->
        when ((exception as SplitInstallException).errorCode) {
            SplitInstallErrorCode.NETWORK_ERROR -> {
                // Display a message that requests the user to establish a
                // network connection.
            }
            SplitInstallErrorCode.ACTIVE_SESSIONS_LIMIT_EXCEEDED -> checkForActiveDownloads()
            ...
        }
    }

fun checkForActiveDownloads() {
    splitInstallManager
        // Returns a SplitInstallSessionState object for each active session as a List.
        .sessionStates
        .addOnCompleteListener { task ->
            if (task.isSuccessful) {
                // Check for active sessions.
                for (state in task.result) {
                    if (state.status() == SplitInstallSessionStatus.DOWNLOADING) {
                        // Cancel the request, or request a deferred installation.
                    }
                }
            }
        }
}

Java

splitInstallManager
    .startInstall(request)
    .addOnFailureListener(exception -> {
        switch (((SplitInstallException) exception).getErrorCode()) {
            case SplitInstallErrorCode.NETWORK_ERROR:
                // Display a message that requests the user to establish a
                // network connection.
                break;
            case SplitInstallErrorCode.ACTIVE_SESSIONS_LIMIT_EXCEEDED:
                checkForActiveDownloads();
            ...
    });

void checkForActiveDownloads() {
    splitInstallManager
        // Returns a SplitInstallSessionState object for each active session as a List.
        .getSessionStates()
        .addOnCompleteListener( task -> {
            if (task.isSuccessful()) {
                // Check for active sessions.
                for (SplitInstallSessionState state : task.getResult()) {
                    if (state.status() == SplitInstallSessionStatus.DOWNLOADING) {
                        // Cancel the request, or request a deferred installation.
                    }
                }
            }
        });
}

Le tableau ci-dessous décrit les états d'erreur que votre application peut avoir besoin de gérer :

Code d'erreur Description Action recommandée
ACTIVE_SESSIONS_LIMIT_EXCEEDED La requête a été refusée, car au moins une de vos requêtes est en cours de téléchargement. Vérifiez si des requêtes sont toujours en cours de téléchargement, comme indiqué dans l'exemple ci-dessus.
MODULE_UNAVAILABLE Google Play ne trouve pas le module demandé en fonction de la version actuellement installée de l'application, de l'appareil et du compte Google Play de l'utilisateur. Si l'utilisateur n'a pas accès au module, informez-le.
INVALID_REQUEST Google Play a reçu votre demande, mais elle n'est pas valide. Vérifiez que les informations fournies dans la requête sont complètes et exactes.
SESSION_NOT_FOUND Impossible de trouver une session pour un ID de session donné. Si vous essayez de surveiller l'état d'une requête par son ID de session, assurez-vous que celui-ci est correct.
API_NOT_AVAILABLE La bibliothèque Play Feature Delivery n'est pas compatible avec l'appareil actuel. Autrement dit, l'appareil ne peut pas télécharger ni installer de fonctionnalités à la demande. Pour les appareils équipés d'Android 4.4 (niveau d'API 20) ou d'une version antérieure, vous devez inclure des modules de fonctionnalités au moment de l'installation à l'aide de la propriété de fichier manifeste dist:fusing. Pour en savoir plus, consultez le guide du fichier manifeste des modules de fonctionnalités.
NETWORK_ERROR La requête a échoué en raison d'une erreur réseau. Invitez l'utilisateur à établir une connexion réseau ou à se connecter à un autre réseau.
ACCESS_DENIED L'application ne peut pas enregistrer la requête en raison d'autorisations insuffisantes. Cela se produit généralement lorsque l'application est exécutée en arrière-plan. Renvoyez la requête lorsque l'application revient au premier plan.
INCOMPATIBLE_WITH_EXISTING_SESSION La requête contient un ou plusieurs modules déjà demandés, mais pas encore installés. Créez une requête n'incluant pas les modules déjà demandés par votre application ou attendez la fin de l'installation de tous les modules actuellement demandés avant de relancer la requête.

Notez que la demande d'un module déjà installé ne résout pas une erreur.

SERVICE_DIED Le service en charge du traitement de la requête a expiré. Réessayez d'envoyer la requête.

Votre SplitInstallStateUpdatedListener reçoit un message SplitInstallSessionState avec ce code d'erreur, l'état FAILED et l'ID de session -1.

INSUFFICIENT_STORAGE L'appareil ne dispose pas d'un espace de stockage suffisant pour installer le module de fonctionnalités. Informez-en l'utilisateur.
SPLITCOMPAT_VERIFICATION_ERROR, SPLITCOMPAT_EMULATION_ERROR, SPLITCOMPAT_COPY_ERROR SplitCompat n'a pas pu charger le module de fonctionnalité. Ces erreurs devraient se résoudre automatiquement après le prochain redémarrage de l'application.
PLAY_STORE_NOT_FOUND L'application Play Store n'est pas installée sur l'appareil. Indiquez à l'utilisateur que l'application Play Store est requise pour télécharger cette fonctionnalité.
APP_NOT_OWNED L'application n'a pas été installée par Google Play et la fonctionnalité ne peut pas être téléchargée. Cette erreur ne peut se produire que pour les installations différées. Si vous souhaitez que l'utilisateur acquière l'application sur Google Play, utilisez startInstall(), qui peut obtenir la confirmation de l'utilisateur nécessaire.
INTERNAL_ERROR Une erreur interne s'est produite sur le Play Store. Réessayez d'envoyer la requête.

Si un utilisateur demande le téléchargement d'un module à la demande et qu'une erreur se produit, vous pouvez afficher une boîte de dialogue qui lui fournit deux options : Réessayer (qui tente à nouveau d'exécuter la requête) et Annuler (qui l'abandonne). Pour obtenir une assistance supplémentaire, vous devez également fournir un lien d'aide qui redirige les utilisateurs vers le Centre d'aide Google Play.

Gérer les notifications d'état

Après avoir enregistré un écouteur et l'ID de session pour votre requête, utilisez StateUpdatedListener.onStateUpdate() pour gérer les changements d'état, comme indiqué ci-dessous.

Kotlin

override fun onStateUpdate(state : SplitInstallSessionState) {
    if (state.status() == SplitInstallSessionStatus.FAILED
        && state.errorCode() == SplitInstallErrorCode.SERVICE_DIED) {
       // Retry the request.
       return
    }
    if (state.sessionId() == mySessionId) {
        when (state.status()) {
            SplitInstallSessionStatus.DOWNLOADING -> {
              val totalBytes = state.totalBytesToDownload()
              val progress = state.bytesDownloaded()
              // Update progress bar.
            }
            SplitInstallSessionStatus.INSTALLED -> {

              // After a module is installed, you can start accessing its content or
              // fire an intent to start an activity in the installed module.
              // For other use cases, see access code and resources from installed modules.

              // If the request is an on demand module for an Android Instant App
              // running on Android 8.0 (API level 26) or higher, you need to
              // update the app context using the SplitInstallHelper API.
            }
        }
    }
}

Java

@Override
public void onStateUpdate(SplitInstallSessionState state) {
    if (state.status() == SplitInstallSessionStatus.FAILED
        && state.errorCode() == SplitInstallErrorCode.SERVICE_DIES) {
       // Retry the request.
       return;
    }
    if (state.sessionId() == mySessionId) {
        switch (state.status()) {
            case SplitInstallSessionStatus.DOWNLOADING:
              int totalBytes = state.totalBytesToDownload();
              int progress = state.bytesDownloaded();
              // Update progress bar.
              break;

            case SplitInstallSessionStatus.INSTALLED:

              // After a module is installed, you can start accessing its content or
              // fire an intent to start an activity in the installed module.
              // For other use cases, see access code and resources from installed modules.

              // If the request is an on demand module for an Android Instant App
              // running on Android 8.0 (API level 26) or higher, you need to
              // update the app context using the SplitInstallHelper API.
        }
    }
}

Les états possibles sont décrits dans le tableau ci-dessous.

État de la requête Description Action recommandée
PENDING La demande a été acceptée et le téléchargement devrait bientôt commencer. Initialisez les composants de l'interface utilisateur, tels qu'une barre de progression, pour fournir des informations sur le téléchargement à l'utilisateur.
REQUIRES_USER_CONFIRMATION Le téléchargement nécessite une confirmation de l'utilisateur. Le plus souvent, cet état s'affiche si l'application n'a pas été installée via Google Play. Invitez l'utilisateur à confirmer le téléchargement de la fonctionnalité via Google Play. Pour en savoir plus, consultez la section sur l'obtention de la confirmation d'un utilisateur.
DOWNLOADING Le téléchargement est en cours. Si vous fournissez une barre de progression pour le téléchargement, mettez à jour l'interface utilisateur à l'aide des méthodes SplitInstallSessionState.bytesDownloaded() et SplitInstallSessionState.totalBytesToDownload() (consultez l'exemple de code au-dessus de ce tableau).
DOWNLOADED L'appareil a téléchargé le module, mais l'installation n'a pas encore commencé. Les applications doivent activer SplitCompat pour avoir accès aux modules téléchargés et éviter d'afficher cet état. Cette étape est obligatoire pour accéder au code et aux ressources du module de fonctionnalité.
INSTALLING L'appareil installe actuellement le module. Mettez à jour la barre de progression. Cet état est généralement bref.
INSTALLED Le module est installé sur l'appareil. Accédez au code et à la ressource du module pour poursuivre le parcours utilisateur.

Si le module est destiné à une appli instantanée Android s'exécutant sous Android 8.0 (API de niveau 26) ou une version ultérieure, vous devez utiliser splitInstallHelper pour mettre à jour les composants d'application avec le nouveau module.

FAILED La requête a échoué avant l'installation du module sur l'appareil. Invitez l'utilisateur à relancer ou annuler la requête.
CANCELING L'appareil annule la demande. Pour en savoir plus, consultez la section expliquant comment annuler une demande d'installation.
CANCELED La demande a été annulée.

Obtenir la confirmation de l'utilisateur

Dans certains cas, Google Play peut exiger une confirmation de l'utilisateur avant de répondre à une requête de téléchargement (par exemple, si votre appli n'a pas été installée par Google Play ou si vous essayez d'effectuer un téléchargement volumineux via les données mobiles). Dans ce cas, l'état de la requête indique REQUIRES_USER_CONFIRMATION, et votre application doit obtenir une confirmation de l'utilisateur avant que l'appareil puisse télécharger et installer les modules dans la requête. Pour obtenir la confirmation, votre application doit inviter l'utilisateur comme suit :

Kotlin

override fun onSessionStateUpdate(state: SplitInstallSessionState) {
    if (state.status() == SplitInstallSessionStatus.REQUIRES_USER_CONFIRMATION) {
        // Displays a confirmation for the user to confirm the request.
        splitInstallManager.startConfirmationDialogForResult(
          state,
          // an activity result launcher registered via registerForActivityResult
          activityResultLauncher)
    }
    ...
 }

Java

@Override void onSessionStateUpdate(SplitInstallSessionState state) {
    if (state.status() == SplitInstallSessionStatus.REQUIRES_USER_CONFIRMATION) {
        // Displays a confirmation for the user to confirm the request.
        splitInstallManager.startConfirmationDialogForResult(
          state,
          // an activity result launcher registered via registerForActivityResult
          activityResultLauncher);
    }
    ...
 }

Vous pouvez enregistrer un lanceur de résultats d'activité à l'aide du contrat ActivityResultContracts.StartIntentSenderForResult intégré. Consultez la section API Activity Result.

L'état de la requête est mis à jour en fonction de la réponse de l'utilisateur :

  • Si l'utilisateur accepte la confirmation, l'état de la requête passe à PENDING et le téléchargement se poursuit.
  • Si l'utilisateur refuse la confirmation, l'état de la requête passe à CANCELED.
  • Si l'utilisateur n'effectue pas de sélection avant la disparition de la boîte de dialogue, l'état de la requête reste REQUIRES_USER_CONFIRMATION. Votre application peut de nouveau inviter l'utilisateur à finaliser la requête.

Pour recevoir un rappel avec la réponse de l'utilisateur, vous pouvez ignorer ActivityResultCallback, comme indiqué ci-dessous.

Kotlin

registerForActivityResult(StartIntentSenderForResult()) { result: ActivityResult -> {
        // Handle the user's decision. For example, if the user selects "Cancel",
        // you may want to disable certain functionality that depends on the module.
    }
}

Java

registerForActivityResult(
    new ActivityResultContracts.StartIntentSenderForResult(),
    new ActivityResultCallback<ActivityResult>() {
        @Override
        public void onActivityResult(ActivityResult result) {
            // Handle the user's decision. For example, if the user selects "Cancel",
            // you may want to disable certain functionality that depends on the module.
        }
    });

Annuler une demande d'installation

Si votre application doit annuler une requête avant de l'installer, elle peut appeler la méthode cancelInstall() à l'aide de l'ID de session de la requête, comme indiqué ci-dessous.

Kotlin

splitInstallManager
    // Cancels the request for the given session ID.
    .cancelInstall(mySessionId)

Java

splitInstallManager
    // Cancels the request for the given session ID.
    .cancelInstall(mySessionId);

Modules d'accès

Pour accéder au code et aux ressources d'un module téléchargé, votre application doit activer la bibliothèque SplitCompat pour votre application et pour chaque activité des modules de fonctionnalités que votre application télécharge.

Toutefois, notez que les restrictions suivantes s'appliquent à l'accès au contenu d'un module après un certain temps (parfois quelques jours) :

  • La plate-forme ne peut pas appliquer de nouvelles entrées de fichier manifeste introduites par le module.
  • La plate-forme ne peut pas accéder aux ressources du module pour les composants d'UI du système tels que les notifications. Si vous devez utiliser ces ressources immédiatement, envisagez de les inclure dans le module de base de votre application.

Activer SplitCompat

Pour que votre application puisse accéder au code et aux ressources d'un module téléchargé, vous devez activer SplitCompat à l'aide de l'une des méthodes décrites dans les sections suivantes.

Après avoir activé SplitCompat pour votre application, vous devez également activer SplitCompat pour chaque activité dans les modules de fonctionnalités auxquels votre application doit avoir accès.

Déclarer SplitCompatApplication dans le fichier manifeste

Le moyen le plus simple pour activer SplitCompat consiste à déclarer SplitCompatApplication en tant que sous-classe Application dans le fichier manifeste de votre application, comme indiqué ci-dessous :

<application
    ...
    android:name="com.google.android.play.core.splitcompat.SplitCompatApplication">
</application>

Une fois l'application installée sur un appareil, vous pouvez accéder automatiquement au code et aux ressources des modules de fonctionnalités téléchargés.

Appeler SplitCompat au moment de l'exécution

Vous pouvez également activer SplitCompat dans des activités ou des services spécifiques au moment de l'exécution. Vous devez activer SplitCompat pour lancer les activités incluses dans les modules de fonctionnalités. Pour ce faire, remplacez attachBaseContext comme indiqué ci-dessous.

Si vous disposez d'une classe Application personnalisée, demandez-lui plutôt d'étendre SplitCompatApplication afin d'activer SplitCompat, comme indiqué ci-dessous :

Kotlin

class MyApplication : SplitCompatApplication() {
    ...
}

Java

public class MyApplication extends SplitCompatApplication {
    ...
}

SplitCompatApplication remplace simplement ContextWrapper.attachBaseContext() pour inclure SplitCompat.install(Context applicationContext). Si vous ne souhaitez pas que votre classe Application étende SplitCompatApplication, vous pouvez remplacer la méthode attachBaseContext() manuellement, comme suit :

Kotlin

override fun attachBaseContext(base: Context) {
    super.attachBaseContext(base)
    // Emulates installation of future on demand modules using SplitCompat.
    SplitCompat.install(this)
}

Java

@Override
protected void attachBaseContext(Context base) {
    super.attachBaseContext(base);
    // Emulates installation of future on demand modules using SplitCompat.
    SplitCompat.install(this);
}

Si votre module à la demande est compatible avec les applications instantanées et les applications installées, vous pouvez appeler SplitCompat de manière conditionnelle, comme suit :

Kotlin

override fun attachBaseContext(base: Context) {
    super.attachBaseContext(base)
    if (!InstantApps.isInstantApp(this)) {
        SplitCompat.install(this)
    }
}

Java

@Override
protected void attachBaseContext(Context base) {
    super.attachBaseContext(base);
    if (!InstantApps.isInstantApp(this)) {
        SplitCompat.install(this);
    }
}

Activer SplitCompat pour les activités de module

Après avoir activé SplitCompat pour votre application de base, vous devez faire de même pour chaque activité téléchargée par votre appli dans un module de fonctionnalité. Pour ce faire, utilisez la méthode SplitCompat.installActivity() comme suit :

Kotlin

override fun attachBaseContext(base: Context) {
    super.attachBaseContext(base)
    // Emulates installation of on demand modules using SplitCompat.
    SplitCompat.installActivity(this)
}

Java

@Override
protected void attachBaseContext(Context base) {
    super.attachBaseContext(base);
    // Emulates installation of on demand modules using SplitCompat.
    SplitCompat.installActivity(this);
}

Accéder aux composants définis dans les modules de fonctionnalités

Démarrer une activité définie dans un module de fonctionnalité

Vous pouvez lancer des activités définies dans des modules de fonctionnalités avec startActivity() après avoir activé SplitCompat.

Kotlin

startActivity(Intent()
  .setClassName("com.package", "com.package.module.MyActivity")
  .setFlags(...))

Java

startActivity(new Intent()
  .setClassName("com.package", "com.package.module.MyActivity")
  .setFlags(...));

Le premier paramètre pour setClassName est le nom de package de l'application. Le second correspond au nom complet de la classe de l'activité.

Lorsqu'une activité se trouve dans un module de fonctionnalité que vous avez téléchargé à la demande, vous devez activer SplitCompat dans l'activité.

Démarrer un service défini dans un module de fonctionnalité

Vous pouvez lancer des services définis dans des modules de fonctionnalités à l'aide de startService() après avoir activé SplitCompat.

Kotlin

startService(Intent()
  .setClassName("com.package", "com.package.module.MyService")
  .setFlags(...))

Java

startService(new Intent()
  .setClassName("com.package", "com.package.module.MyService")
  .setFlags(...));

Exporter un composant défini dans un module de fonctionnalité

Vous ne devez pas inclure de composants Android exportés dans des modules facultatifs.

Le système de compilation fusionne les entrées du fichier manifeste de tous les modules dans le module de base. Si un module facultatif contenait un composant exporté, il serait accessible avant même l'installation du module et pourrait entraîner un plantage dû à l'absence de code lors d'un appel depuis une autre application.

Ce problème ne se pose pas pour les composants internes, accessible uniquement par l'application, celle-ci pouvant vérifier que le module est installé avant d'accéder au composant.

Si vous avez besoin d'un composant exporté et que son contenu se trouve dans un module facultatif, envisagez d'implémenter un modèle de proxy. Vous pouvez le faire en ajoutant un composant de proxy exporté dans la base. Lorsqu'il est utilisé, le composant de proxy peut vérifier la présence du module qui contient le contenu. Si le module est présent, le composant proxy peut démarrer le composant interne à partir du module via un Intent, en appliquant l'intent de l'application appelante. Si le module n'est pas présent, le composant peut le télécharger ou renvoyer un message d'erreur approprié à l'application appelante.

Accéder au code et aux ressources des modules installés

Si vous activez SplitCompat pour votre contexte d'application de base et les activités de votre module de fonctionnalité, vous pouvez utiliser le code et les ressources d'un module de fonctionnalité comme s'il faisait partie de l'APK de base, une fois le module facultatif installé.

Accéder au code depuis un autre module

Accéder au code de base depuis un module

Le code intégré à votre module de base peut être utilisé directement par d'autres modules. Il vous suffit pour cela d'importer et d'utiliser les classes dont vous avez besoin.

Accéder au code d'un module depuis un autre module

Un objet ou une classe d'un module ne sont pas accessibles directement de manière statique à partir d'un autre module, mais le sont indirectement par reflet.

Méfiez-vous de cette fréquence, en raison des coûts de performance liées à la réflexion. Pour les cas d'utilisation complexes, utilisez des frameworks d'injection de dépendances tels que Dagger 2 pour garantir un seul appel de réflexion par durée de vie de l'application.

Pour simplifier les interactions avec l'objet après instanciation, il est recommandé de définir une interface dans le module de base et sa mise en œuvre dans le module de fonctionnalités. Par exemple :

Kotlin

// In the base module
interface MyInterface {
  fun hello(): String
}

// In the feature module
object MyInterfaceImpl : MyInterface {
  override fun hello() = "Hello"
}

// In the base module, where we want to access the feature module code
val stringFromModule = (Class.forName("com.package.module.MyInterfaceImpl")
    .kotlin.objectInstance as MyInterface).hello();

Java

// In the base module
public interface MyInterface {
  String hello();
}

// In the feature module
public class MyInterfaceImpl implements MyInterface {
  @Override
  public String hello() {
    return "Hello";
  }
}

// In the base module, where we want to access the feature module code
String stringFromModule =
   ((MyInterface) Class.forName("com.package.module.MyInterfaceImpl").getConstructor().newInstance()).hello();

Accéder aux ressources et aux éléments d'un autre module

Une fois qu'un module est installé, vous pouvez accéder aux ressources et aux éléments qu'il contient de manière standard, tout en prenant conscience de ces deux mises en garde :

  • Si vous accédez à une ressource depuis un autre module, celui-ci n'aura pas accès à l'identifiant de ressource, bien que la ressource puisse toujours être accessible par son nom. Notez que le package à utiliser pour référencer la ressource correspond à celui du module dans lequel la ressource est définie.
  • Si vous souhaitez accéder à des éléments ou à des ressources qui existent dans un module nouvellement installé à partir d'un autre module de votre application, vous devez le faire à l'aide du contexte de l'application. Le contexte du composant qui tente d'accéder aux ressources ne sera pas encore mis à jour. Vous pouvez également recréer ce composant (par exemple, en appelant Activity.recreate()) ou réinstaller SplitCompat après l'installation du module de fonctionnalité.

Charger un code natif dans une appli à l'aide de la distribution à la demande

Nous vous recommandons d'utiliser ReLinker pour charger toutes vos bibliothèques natives lorsque vous utilisez la distribution à la demande de modules de fonctionnalité. ReLinker résout un problème de chargement des bibliothèques natives après l'installation d'un module de fonctionnalité. Pour en savoir plus sur ReLinker, consultez les conseils JNI Android.

Charger du code natif depuis un module facultatif

Une fois qu'une division est installée, nous vous recommandons de charger son code natif via ReLinker. Pour les applis instantanées, vous devez utiliser cette méthode spéciale.

Si vous utilisez System.loadLibrary() pour charger votre code natif et que votre bibliothèque native dépend d'une autre bibliothèque du module, vous devez d'abord charger manuellement cette autre bibliothèque. Si vous utilisez ReLinker, l'opération équivalente est Relinker.recursively().loadLibrary().

Si vous utilisez dlopen() dans le code natif pour charger une bibliothèque définie dans un module facultatif, elle ne fonctionnera pas avec les chemins d'accès des bibliothèques relatives. La meilleure solution consiste à récupérer le chemin absolu de la bibliothèque à partir du code Java via ClassLoader.findLibrary(), puis à l'utiliser dans votre appel dlopen(). Effectuez cette opération avant de saisir le code natif ou d'utiliser un appel JNI depuis votre code natif vers Java.

Accéder aux applis instantanées Android installées

Une fois qu'un module d'appli instantanée Android indique INSTALLED, vous pouvez accéder à son code et à ses ressources à l'aide d'un contexte d'application actualisé. Un contexte créé par votre application avant d'installer un module (par exemple, un module déjà stocké dans une variable) ne possède pas le contenu du nouveau module. Mais un contexte récent le permet, par exemple à l'aide de createPackageContext.

Kotlin

// Generate a new context as soon as a request for a new module
// reports as INSTALLED.
override fun onStateUpdate(state: SplitInstallSessionState ) {
    if (state.sessionId() == mySessionId) {
        when (state.status()) {
            ...
            SplitInstallSessionStatus.INSTALLED -> {
                val newContext = context.createPackageContext(context.packageName, 0)
                // If you use AssetManager to access your app’s raw asset files, you’ll need
                // to generate a new AssetManager instance from the updated context.
                val am = newContext.assets
            }
        }
    }
}

Java

// Generate a new context as soon as a request for a new module
// reports as INSTALLED.
@Override
public void onStateUpdate(SplitInstallSessionState state) {
    if (state.sessionId() == mySessionId) {
        switch (state.status()) {
            ...
            case SplitInstallSessionStatus.INSTALLED:
                Context newContext = context.createPackageContext(context.getPackageName(), 0);
                // If you use AssetManager to access your app’s raw asset files, you’ll need
                // to generate a new AssetManager instance from the updated context.
                AssetManager am = newContext.getAssets();
        }
    }
}

Applis instantanées Android sur Android 8.0 ou version ultérieure

Lorsque vous sollicitez un module à la demande pour une appli instantanée Android sur Android 8.0 (API niveau 26) ou une version ultérieure, après une demande d'installation signalée comme INSTALLED, vous devez mettre à jour l'application avec le contexte du nouveau module via un appel à SplitInstallHelper.updateAppInfo(Context context). À défaut, l'application n'aura pas encore connaissance du code et des ressources du module. Après avoir mis à jour les métadonnées de l'application, vous devez charger le contenu du module lors du prochain événement principal du thread en appelant un nouveau Handler, comme indiqué ci-dessous :

Kotlin

override fun onStateUpdate(state: SplitInstallSessionState ) {
    if (state.sessionId() == mySessionId) {
        when (state.status()) {
            ...
            SplitInstallSessionStatus.INSTALLED -> {
                // You need to perform the following only for Android Instant Apps
                // running on Android 8.0 (API level 26) and higher.
                if (BuildCompat.isAtLeastO()) {
                    // Updates the app’s context with the code and resources of the
                    // installed module.
                    SplitInstallHelper.updateAppInfo(context)
                    Handler().post {
                        // Loads contents from the module using AssetManager
                        val am = context.assets
                        ...
                    }
                }
            }
        }
    }
}

Java

@Override
public void onStateUpdate(SplitInstallSessionState state) {
    if (state.sessionId() == mySessionId) {
        switch (state.status()) {
            ...
            case SplitInstallSessionStatus.INSTALLED:
            // You need to perform the following only for Android Instant Apps
            // running on Android 8.0 (API level 26) and higher.
            if (BuildCompat.isAtLeastO()) {
                // Updates the app’s context with the code and resources of the
                // installed module.
                SplitInstallHelper.updateAppInfo(context);
                new Handler().post(new Runnable() {
                    @Override public void run() {
                        // Loads contents from the module using AssetManager
                        AssetManager am = context.getAssets();
                        ...
                    }
                });
            }
        }
    }
}

Charger des bibliothèques C/C++

Si vous souhaitez charger des bibliothèques C/C++ à partir d'un module que l'appareil a déjà téléchargé dans une appli instantanée, utilisez SplitInstallHelper.loadLibrary(Context context, String libName), comme indiqué ci-dessous :

Kotlin

override fun onStateUpdate(state: SplitInstallSessionState) {
    if (state.sessionId() == mySessionId) {
        when (state.status()) {
            SplitInstallSessionStatus.INSTALLED -> {
                // Updates the app’s context as soon as a module is installed.
                val newContext = context.createPackageContext(context.packageName, 0)
                // To load C/C++ libraries from an installed module, use the following API
                // instead of System.load().
                SplitInstallHelper.loadLibrary(newContext, “my-cpp-lib”)
                ...
            }
        }
    }
}

Java

public void onStateUpdate(SplitInstallSessionState state) {
    if (state.sessionId() == mySessionId) {
        switch (state.status()) {
            case SplitInstallSessionStatus.INSTALLED:
                // Updates the app’s context as soon as a module is installed.
                Context newContext = context.createPackageContext(context.getPackageName(), 0);
                // To load C/C++ libraries from an installed module, use the following API
                // instead of System.load().
                SplitInstallHelper.loadLibrary(newContext, “my-cpp-lib”);
                ...
        }
    }
}

Limites connues

  • Il n'est pas possible d'utiliser Android WebView dans une activité qui accède à des ressources ou à des éléments à partir d'un module facultatif. Cela est dû à une incompatibilité entre WebView et SplitCompat pour les API Android de niveau 28 ou inférieur.
  • Vous ne pouvez pas mettre en cache des objets Android ApplicationInfo, leur contenu ou des objets qui les contiennent dans votre application. Vous devez toujours récupérer ces objets si nécessaire depuis un contexte d'application. La mise en cache de ces objets peut entraîner le plantage de l'application lors de l'installation d'un module de fonctionnalités.

Gérer les modules installés

Pour vérifier quels modules de fonctionnalités sont actuellement installés sur l'appareil, vous pouvez appeler SplitInstallManager.getInstalledModules(), qui renvoie une valeur Set<String> du nom des modules installés, comme illustré ci-dessous.

Kotlin

val installedModules: Set<String> = splitInstallManager.installedModules

Java

Set<String> installedModules = splitInstallManager.getInstalledModules();

Désinstaller des modules

Vous pouvez demander à l'appareil de désinstaller des modules en appelant SplitInstallManager.deferredUninstall(List<String> moduleNames), comme indiqué ci-dessous.

Kotlin

// Specifies two feature modules for deferred uninstall.
splitInstallManager.deferredUninstall(listOf("pictureMessages", "promotionalFilters"))

Java

// Specifies two feature modules for deferred uninstall.
splitInstallManager.deferredUninstall(Arrays.asList("pictureMessages", "promotionalFilters"));

La désinstallation des modules ne s'effectue pas immédiatement. Autrement dit, l'appareil procède en arrière-plan si nécessaire pour économiser de l'espace de stockage. Vous pouvez vérifier que l'appareil a supprimé un module en appelant SplitInstallManager.getInstalledModules() et en inspectant le résultat, comme décrit dans la section précédente.

Télécharger des ressources linguistiques supplémentaires

Avec les app bundles, les appareils ne téléchargent que le code et les ressources dont ils ont besoin pour exécuter votre application. Ainsi, pour les ressources linguistiques, l'appareil de l'utilisateur ne télécharge que les ressources linguistiques de votre application correspondant à une ou plusieurs langues actuellement sélectionnées dans les paramètres de l'appareil.

Si vous souhaitez que votre application ait accès à des ressources linguistiques supplémentaires, par exemple pour implémenter un outil de sélection de langue dans l'application, vous pouvez utiliser la bibliothèque Play Feature Delivery pour les télécharger à la demande. Le processus est semblable à celui du téléchargement d'un module de fonctionnalité, comme indiqué ci-dessous.

Kotlin

// Captures the user’s preferred language and persists it
// through the app’s SharedPreferences.
sharedPrefs.edit().putString(LANGUAGE_SELECTION, "fr").apply()
...

// Creates a request to download and install additional language resources.
val request = SplitInstallRequest.newBuilder()
        // Uses the addLanguage() method to include French language resources in the request.
        // Note that country codes are ignored. That is, if your app
        // includes resources for “fr-FR” and “fr-CA”, resources for both
        // country codes are downloaded when requesting resources for "fr".
        .addLanguage(Locale.forLanguageTag(sharedPrefs.getString(LANGUAGE_SELECTION)))
        .build()

// Submits the request to install the additional language resources.
splitInstallManager.startInstall(request)

Java

// Captures the user’s preferred language and persists it
// through the app’s SharedPreferences.
sharedPrefs.edit().putString(LANGUAGE_SELECTION, "fr").apply();
...

// Creates a request to download and install additional language resources.
SplitInstallRequest request =
    SplitInstallRequest.newBuilder()
        // Uses the addLanguage() method to include French language resources in the request.
        // Note that country codes are ignored. That is, if your app
        // includes resources for “fr-FR” and “fr-CA”, resources for both
        // country codes are downloaded when requesting resources for "fr".
        .addLanguage(Locale.forLanguageTag(sharedPrefs.getString(LANGUAGE_SELECTION)))
        .build();

// Submits the request to install the additional language resources.
splitInstallManager.startInstall(request);

La requête est traitée comme s'il s'agissait d'une demande de module de fonctionnalité. Autrement dit, vous pouvez surveiller l'état de la requête comme vous le feriez normalement.

Si votre application n'a pas besoin de ressources linguistiques supplémentaires immédiatement, vous pouvez différer l'installation lorsque l'application est exécutée en arrière-plan, comme indiqué ci-dessous.

Kotlin

splitInstallManager.deferredLanguageInstall(
    Locale.forLanguageTag(sharedPrefs.getString(LANGUAGE_SELECTION)))

Java

splitInstallManager.deferredLanguageInstall(
    Locale.forLanguageTag(sharedPrefs.getString(LANGUAGE_SELECTION)));

Accéder aux ressources linguistiques téléchargées

Pour pouvoir accéder aux ressources de langue téléchargées, votre application doit exécuter la méthode SplitCompat.installActivity() dans la méthode attachBaseContext() de chaque activité nécessitant l'accès à ces ressources, comme indiqué ci-dessous.

Kotlin

override fun attachBaseContext(base: Context) {
  super.attachBaseContext(base)
  SplitCompat.installActivity(this)
}

Java

@Override
protected void attachBaseContext(Context base) {
  super.attachBaseContext(base);
  SplitCompat.installActivity(this);
}

Pour chaque activité dans laquelle vous souhaitez utiliser les ressources linguistiques que votre application a téléchargées, mettez à jour le contexte de base et définissez de nouveaux paramètres régionaux via l'élément Configuration associé :

Kotlin

override fun attachBaseContext(base: Context) {
  val configuration = Configuration()
  configuration.setLocale(Locale.forLanguageTag(sharedPrefs.getString(LANGUAGE_SELECTION)))
  val context = base.createConfigurationContext(configuration)
  super.attachBaseContext(context)
  SplitCompat.install(this)
}

Java

@Override
protected void attachBaseContext(Context base) {
  Configuration configuration = new Configuration();
  configuration.setLocale(Locale.forLanguageTag(sharedPrefs.getString(LANGUAGE_SELECTION)));
  Context context = base.createConfigurationContext(configuration);
  super.attachBaseContext(context);
  SplitCompat.install(this);
}

Pour que ces modifications soient prises en compte, vous devez recréer votre activité une fois le nouveau langage installé et prêt à l'emploi. Vous pouvez utiliser la méthode Activity#recreate().

Kotlin

when (state.status()) {
  SplitInstallSessionStatus.INSTALLED -> {
      // Recreates the activity to load resources for the new language
      // preference.
      activity.recreate()
  }
  ...
}

Java

switch (state.status()) {
  case SplitInstallSessionStatus.INSTALLED:
      // Recreates the activity to load resources for the new language
      // preference.
      activity.recreate();
  ...
}

Désinstaller des ressources linguistiques supplémentaires

Comme pour les modules de fonctionnalités, vous pouvez désinstaller des ressources supplémentaires à tout moment. Avant de demander une désinstallation, vous pouvez d'abord déterminer quelles sont les langues installées, comme suit.

Kotlin

val installedLanguages: Set<String> = splitInstallManager.installedLanguages

Java

Set<String> installedLanguages = splitInstallManager.getInstalledLanguages();

Vous pouvez ensuite choisir les langues à désinstaller à l'aide de la méthode deferredLanguageUninstall(), comme indiqué ci-dessous.

Kotlin

splitInstallManager.deferredLanguageUninstall(
    Locale.forLanguageTag(sharedPrefs.getString(LANGUAGE_SELECTION)))

Java

splitInstallManager.deferredLanguageUninstall(
    Locale.forLanguageTag(sharedPrefs.getString(LANGUAGE_SELECTION)));

Tester localement les installations de modules

La bibliothèque Play Feature Delivery vous permet de tester localement les fonctionnalités suivantes de votre application, sans vous connecter au Play Store :

Cette page explique comment déployer les APK divisés de votre application sur votre appareil de test afin que Play Feature Delivery les utilise automatiquement pour simuler la demande, le téléchargement et l'installation de modules à partir du Play Store.

Bien que vous n'ayez pas besoin de modifier la logique de votre application, vous devez répondre aux exigences suivantes :

  • Téléchargez et installez la dernière version de bundletool. Vous avez besoin de bundletool pour créer un ensemble d'APK installables à partir du package de votre application.

Créer un ensemble d'APK

Si vous ne l'avez pas déjà fait, créez les APK divisés de votre application comme suit :

  1. Créez un app bundle en utilisant l'une des méthodes suivantes :
  2. Utilisez bundletool pour générer un ensemble d'APK pour toutes les configurations d'appareil à l'aide de la commande suivante :

    bundletool build-apks --local-testing
      --bundle my_app.aab
      --output my_app.apks
    

L'option --local-testing inclut des métadonnées dans les fichiers manifestes de vos APK qui permettent à la bibliothèque Play Feature Delivery d'utiliser les APK divisés locaux pour tester l'installation de modules de fonctionnalités, sans connexion au Play Store.

Déployer votre application sur l'appareil

Après avoir créé un ensemble de fichiers APK à l'aide de l'option --local-testing, utilisez bundletool pour installer la version de base de votre application et transférer les fichiers APK supplémentaires vers l'espace de stockage local de votre appareil. Vous pouvez effectuer ces deux actions à l'aide de la commande suivante :

bundletool install-apks --apks my_app.apks

Désormais, lorsque vous démarrez votre application et terminez le parcours utilisateur pour télécharger et installer un module de fonctionnalité, la bibliothèque Play Feature Delivery utilise les APK bundletool transférés vers l'espace de stockage local de l'appareil.

Simuler une erreur réseau

Pour simuler des installations de modules à partir du Play Store, la bibliothèque Play Feature Delivery utilise une alternative à SplitInstallManager, appelée FakeSplitInstallManager, pour demander le module. Lorsque vous utilisez bundletool avec l'option --local-testing pour créer un ensemble d'APK et les déployer sur votre appareil de test, celui-ci inclut des métadonnées qui indiquent à la bibliothèque Play Feature Delivery de basculer automatiquement les appels d'API de votre application vers FakeSplitInstallManager, au lieu de SplitInstallManager.

FakeSplitInstallManager inclut une option booléenne que vous pouvez activer pour simuler une erreur réseau la prochaine fois que votre application demande l'installation d'un module. Pour accéder à FakeSplitInstallManager dans vos tests, vous pouvez obtenir une instance de celui-ci à l'aide de FakeSplitInstallManagerFactory, comme indiqué ci-dessous :

Kotlin

// Creates an instance of FakeSplitInstallManager with the app's context.
val fakeSplitInstallManager = FakeSplitInstallManagerFactory.create(context)
// Tells Play Feature Delivery Library to force the next module request to
// result in a network error.
fakeSplitInstallManager.setShouldNetworkError(true)

Java

// Creates an instance of FakeSplitInstallManager with the app's context.
FakeSplitInstallManager fakeSplitInstallManager =
    FakeSplitInstallManagerFactory.create(context);
// Tells Play Feature Delivery Library to force the next module request to
// result in a network error.
fakeSplitInstallManager.setShouldNetworkError(true);