Fichiers d'extension pour APK

Google Play exige que les APK compressés téléchargés ne dépassent pas 100 Mo. Pour la plupart des applications, cet espace est largement suffisant pour le code et les éléments de l'application. Cependant, certaines applications nécessitent davantage d'espace pour des graphismes haute-fidélité, des fichiers multimédias ou d'autres éléments volumineux. Auparavant, si la taille de téléchargement de votre application compressée dépassait 100 Mo, vous deviez héberger et télécharger vous-même les ressources supplémentaires lorsque l'utilisateur ouvrait l'application. L'hébergement et le téléchargement des fichiers supplémentaires peuvent s'avérer coûteux et l'expérience utilisateur n'est souvent pas optimale. Pour que ce processus soit plus simple pour vous et plus agréable pour les utilisateurs, Google Play vous permet de joindre deux fichiers d'extension volumineux qui complètent votre APK.

Google Play héberge les fichiers d'extension pour votre application et les diffuse sur l'appareil, sans frais. Les fichiers d'extension sont enregistrés sur l'espace de stockage partagé de l'appareil (une carte SD ou une partition sur une mémoire de stockage USB, également appelée stockage "externe") où ils sont accessibles pour votre application. Sur la plupart des appareils, Google Play télécharge le ou les fichiers d'extension en même temps que l'APK. L'application dispose ainsi de tout ce dont elle a besoin lorsque l'utilisateur l'ouvre pour la première fois. Toutefois, dans certains cas, votre application doit télécharger les fichiers sur Google Play au démarrage.

Si vous souhaitez éviter d'utiliser des fichiers d'extension et que la taille de téléchargement de votre application compressée dépasse 100 Mo, importez plutôt votre application au format Android App Bundles et bénéficiez d'une taille de téléchargement compressée de 200 Mo. De plus, comme les app bundles diffèrent la génération et la signature d'APK à Google Play, les utilisateurs téléchargent des APK optimisés contenant uniquement le code et les ressources nécessaires pour exécuter votre application. Vous n'avez pas besoin de créer, de signer, ni de gérer plusieurs APK ou fichiers d'extension, et les utilisateurs bénéficient de téléchargements plus petits et plus optimisés.

Présentation

Chaque fois que vous importez un APK à l'aide de la Google Play Console, vous pouvez ajouter un ou deux fichiers d'extension à l'APK. La taille de chaque fichier ne doit pas dépasser 2 Mo. Vous pouvez choisir le format de votre choix, mais nous vous recommandons d'utiliser un fichier compressé pour économiser de la bande passante pendant le téléchargement. D'un point de vue conceptuel, chaque fichier d'extension joue un rôle différent :

  • Le fichier d'extension principal est le fichier d'extension principal des ressources supplémentaires requises par votre application.
  • Le fichier d'extension de correctif est facultatif et prévu pour les petites mises à jour du fichier d'extension principal.

Bien que vous puissiez utiliser les deux fichiers d'extension comme vous le souhaitez, nous vous recommandons de placer les éléments principaux dans le fichier d'extension principal. Ces éléments sont rarement, voire jamais, mis à jour. Le fichier d'extension de correctif doit être plus léger et servir d'opérateur de correctif. Il doit être mis à jour à chaque version majeure ou selon les besoins.

Toutefois, même si la mise à jour de votre application ne nécessite qu'un seul nouveau fichier d'extension de correctif, vous devez toujours importer un nouvel APK avec un versionCode mis à jour dans le fichier manifeste. (La Play Console ne vous permet pas d'importer un fichier d'extension dans un APK existant.)

Remarque : le fichier d'extension de correctif est sémantiquement identique au fichier d'extension principal. Vous pouvez utiliser chaque fichier comme vous le souhaitez.

Format du nom de fichier

Chaque fichier d'extension que vous importez peut utiliser n'importe quel format (ZIP, PDF, MP4, etc.). Vous pouvez également utiliser l'outil JOBB pour encapsuler et chiffrer un ensemble de fichiers de ressources et de correctifs ultérieurs. Quel que soit le type de fichier, Google Play les considère comme des blobs binaires opaques et renomme les fichiers selon le schéma suivant :

[main|patch].<expansion-version>.<package-name>.obb

Ce schéma comporte trois composants :

main ou patch
Indique si le fichier est le fichier d'extension principal ou de correctif. Il ne peut y avoir qu'un seul fichier principal et qu'un seul fichier correctif pour chaque APK.
<expansion-version>
Il s'agit d'un nombre entier qui correspond au code de version de l'APK auquel l'extension est associée en premier (il correspond à la valeur android:versionCode de l'application).

"En premier" est mis en avant, car bien que la Play Console vous permette de réutiliser un fichier d'extension importé avec un nouvel APK, le nom du fichier d'extension ne change pas (il conserve la version qui lui a été appliquée lors de votre première importation du fichier).

<package-name>
Nom du package Java de votre application.

Par exemple, supposons que la version de votre APK soit 314159 et que le nom de votre package soit com.example.app. Si vous importez un fichier d'extension principal, le fichier est renommé comme suit :

main.314159.com.example.app.obb

Espace de stockage

Lorsque Google Play télécharge vos fichiers d'extension sur un appareil, il les enregistre sur l'espace de stockage partagé du système. Pour garantir un comportement approprié, vous ne devez pas supprimer, déplacer, ni renommer les fichiers d'extension. Si votre application doit effectuer le téléchargement depuis Google Play, vous devez enregistrer les fichiers exactement au même emplacement.

La méthode getObbDir() renvoie l'emplacement spécifique de vos fichiers d'extension au format suivant :

<shared-storage>/Android/obb/<package-name>/
  • <shared-storage> est le chemin d'accès à l'espace de stockage partagé, disponible à partir de getExternalStorageDirectory().
  • <package-name> est le nom du package de style Java de votre application. Il est disponible dans getPackageName().

Chaque application ne contient jamais plus de deux fichiers d'extension dans ce répertoire. Le premier est le fichier d'extension principal et le second est le fichier d'extension de correctif (si nécessaire). Les versions précédentes sont remplacées lorsque vous mettez à jour votre application avec de nouveaux fichiers d'extension. Depuis Android 4.4 (niveau d'API 19), les applications peuvent lire les fichiers d'extension OBB sans autorisation de stockage externe. Cependant, certaines implémentations d'Android 6.0 (niveau d'API 23) et ultérieures nécessitent toujours une autorisation. Vous devez donc déclarer l'autorisation READ_EXTERNAL_STORAGE dans le fichier manifeste de l'application et demander l'autorisation au moment de l'exécution :

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

Pour Android 6 ou version ultérieure, l'autorisation de stockage externe doit être demandée au moment de l'exécution. Cependant, certaines implémentations d'Android ne nécessitent pas d'autorisation pour lire les fichiers OBB. L'extrait de code suivant montre comment vérifier l'accès en lecture avant de demander l'autorisation d'accès au stockage externe :

Kotlin

val obb = File(obb_filename)
var open_failed = false

try {
    BufferedReader(FileReader(obb)).also { br ->
        ReadObbFile(br)
    }
} catch (e: IOException) {
    open_failed = true
}

if (open_failed) {
    // request READ_EXTERNAL_STORAGE permission before reading OBB file
    ReadObbFileWithPermission()
}

Java

File obb = new File(obb_filename);
 boolean open_failed = false;

 try {
     BufferedReader br = new BufferedReader(new FileReader(obb));
     open_failed = false;
     ReadObbFile(br);
 } catch (IOException e) {
     open_failed = true;
 }

 if (open_failed) {
     // request READ_EXTERNAL_STORAGE permission before reading OBB file
     ReadObbFileWithPermission();
 }

Si vous devez décompresser le contenu de vos fichiers d'extension, ne supprimez pas les fichiers d'extension OBB par la suite et n'enregistrez pas les données non compressées dans le même répertoire. Vous devez enregistrer vos fichiers non compressés dans le répertoire spécifié par getExternalFilesDir(). Toutefois, dans la mesure du possible, il est préférable d'utiliser un format de fichier d'extension qui vous permet de lire directement les données plutôt que d'avoir à les décompresser. Par exemple, nous avons fourni un projet de bibliothèque appelée Bibliothèque ZIP d'extensions d'APK qui lit vos données directement à partir du fichier ZIP.

Attention : Contrairement aux fichiers APK, tous les fichiers enregistrés sur l'espace de stockage partagé peuvent être lus par l'utilisateur et les autres applications.

Conseil : Si vous compressez des fichiers multimédias dans un ZIP, vous pouvez utiliser des appels de lecture multimédia sur les fichiers avec des contrôles de décalage et de longueur (tels que MediaPlayer.setDataSource() et SoundPool.load()) sans avoir à décompresser le fichier ZIP. Pour cela, vous ne devez pas effectuer de compression supplémentaire sur les fichiers multimédias lors de la création des packages ZIP. Par exemple, si vous utilisez l'outil zip, vous devez utiliser l'option -n pour spécifier les suffixes des fichiers ne devant pas être compressés :
zip -n .mp4;.ogg main_expansion media_files

Processus de téléchargement

La plupart du temps, Google Play télécharge et enregistre vos fichiers d'extension en même temps qu'il télécharge l'APK sur l'appareil. Toutefois, dans certains cas, Google Play ne peut pas télécharger les fichiers d'extension ou l'utilisateur peut avoir supprimé les fichiers d'extension précédemment téléchargés. Pour gérer ces situations, votre application doit pouvoir télécharger elle-même les fichiers au démarrage de l'activité principale, à l'aide d'une URL fournie par Google Play.

De manière générale, le processus de téléchargement se présente comme suit :

  1. L'utilisateur choisit d'installer votre application depuis Google Play.
  2. Si Google Play parvient à télécharger les fichiers d'extension (ce qui est le cas pour la plupart des appareils), il les télécharge avec l'APK.

    Si Google Play ne peut pas télécharger les fichiers d'extension, il ne télécharge que l'APK.

  3. Lorsque l'utilisateur lance votre application, celle-ci doit vérifier si les fichiers d'extension sont déjà enregistrés sur l'appareil.
    1. Si c'est le cas, votre application est prête à l'emploi.
    2. Si ce n'est pas le cas, votre application doit télécharger les fichiers d'extension via HTTP à partir de Google Play. Votre application doit envoyer une requête au client Google Play via le service de gestion des licences d'applications de Google Play, qui répond avec le nom, la taille et l'URL de chaque fichier d'extension. Avec ces informations, téléchargez ensuite les fichiers et enregistrez-les sur l'espace de stockage approprié.

Attention : Il est essentiel d'inclure le code nécessaire au téléchargement des fichiers d'extension depuis Google Play, au cas où les fichiers ne seraient pas déjà présents sur l'appareil au démarrage de votre application. Comme indiqué dans la section suivante concernant le téléchargement des fichiers d'extension, nous mettons à votre disposition une bibliothèque qui simplifie considérablement ce processus et effectue le téléchargement depuis un service sans grand effort de programmation de votre part.

Checklist de développement

Voici un récapitulatif des étapes à suivre pour utiliser des fichiers d'extension avec votre application :

  1. Commencez par déterminer si la taille de téléchargement de votre application compressée doit être supérieure à 100 Mo. L'espace est précieux et la taille de téléchargement totale doit être la plus petite possible. Si votre application utilise plus de 100 Mo afin de fournir différentes versions de vos assets graphiques pour plusieurs densités d'écran, envisagez plutôt de publier plusieurs APK, chacun ne contenant que les assets requis pour les écrans qu'il cible. Pour des résultats optimaux lors de la publication sur Google Play, importez un Android App Bundle qui inclut l'ensemble du code et des ressources compilés de votre application, mais qui diffère la génération et la signature de l'APK dans Google Play.
  2. Déterminez les ressources d'application à séparer de votre APK et compressez-les dans un fichier à utiliser comme fichier d'extension principal.

    Normalement, vous ne devez utiliser le second fichier d'extension de correctif que pour mettre à jour le fichier d'extension principal. Toutefois, si vos ressources dépassent la limite de 2 Go pour le fichier d'extension principal, vous pouvez utiliser le fichier correctif pour le reste de vos éléments.

  3. Développez votre application de sorte qu'elle utilise les éléments de vos fichiers d'extension situés dans l'espace de stockage partagé de l'appareil.

    N'oubliez pas que vous ne devez pas supprimer, déplacer ni renommer les fichiers d'extension.

    Si votre application ne nécessite pas de format spécifique, nous vous suggérons de créer des fichiers ZIP pour vos fichiers d'extension, puis de les lire à l'aide de la bibliothèque ZIP d'extensions d'APK.

  4. Ajoutez une logique à l'activité principale de votre application pour vérifier si les fichiers d'extension se trouvent sur l'appareil au démarrage. Si les fichiers ne figurent pas sur l'appareil, utilisez le service de gestion des licences d'applications de Google Play pour demander les URL des fichiers d'extension, puis téléchargez-les et enregistrez-les.

    Pour réduire considérablement la quantité de code à écrire et garantir une bonne expérience utilisateur lors du téléchargement, nous vous recommandons d'utiliser la bibliothèque de téléchargements pour implémenter le comportement de téléchargement.

    Si vous créez votre propre service de téléchargement au lieu d'utiliser la bibliothèque, sachez que vous ne devez pas modifier le nom des fichiers d'extension et les enregistrer dans l'espace de stockage approprié.

Une fois le développement de votre application terminé, suivez le guide pour tester vos fichiers d'extension.

Règles et restrictions

L'ajout de fichiers d'extension pour APK est une fonctionnalité disponible lorsque vous importez votre application à l'aide de la Play Console. Lorsque vous importez votre application pour la première fois ou que vous mettez à jour une application qui utilise des fichiers d'extension, vous devez connaître les règles et limites suivantes :

  1. La taille de chaque fichier d'extension ne doit pas dépasser 2 Go.
  2. Pour télécharger vos fichiers d'extension depuis Google Play, l'utilisateur doit s'être procuré votre application sur Google Play. Google Play ne fournit pas les URL de vos fichiers d'extension si l'application a été installée par d'autres moyens.
  3. Lorsque vous effectuez le téléchargement à partir de votre application, l'URL fournie par Google Play pour chaque fichier est unique pour chaque téléchargement. Chaque URL expire peu de temps après avoir été envoyée à votre application.
  4. Si vous mettez à jour votre application avec un nouvel APK ou importez plusieurs APK pour la même application, vous pouvez sélectionner les fichiers d'extension importés pour un APK précédent. Le nom du fichier d'extension ne change pas : il conserve la version reçue par l'APK auquel le fichier était initialement associé.
  5. Si vous utilisez des fichiers d'extension avec plusieurs fichiers APK afin de fournir des fichiers d'extension différents pour plusieurs appareils, vous devez quand même importer des APK distincts pour chaque appareil et fournir une valeur de versionCode unique et déclarer différents filtres pour chaque APK.
  6. Vous ne pouvez pas émettre de mise à jour de votre application en modifiant uniquement les fichiers d'extension. Vous devez importer un nouvel APK. Si vos modifications ne concernent que les éléments de vos fichiers d'extension, vous pouvez mettre à jour votre APK simplement en modifiant versionCode (et peut-être aussi versionName).

  7. N'enregistrez pas d'autres données dans votre répertoire obb/. Si vous devez décompresser certaines données, enregistrez-les à l'emplacement spécifié par getExternalFilesDir().
  8. Ne supprimez pas et ne renommez pas le fichier d'extension .obb (sauf si vous effectuez une mise à jour). Cela entraînerait le téléchargement répété du fichier d'extension par Google Play (ou par votre application).
  9. Lorsque vous mettez à jour un fichier d'extension manuellement, vous devez supprimer le fichier d'extension précédent.

Télécharger les fichiers d'extension

Dans la plupart des cas, Google Play télécharge et enregistre vos fichiers d'extension sur l'appareil au moment de l'installation ou de la mise à jour de l'APK. De cette façon, les fichiers d'extension sont disponibles lorsque vous lancez votre application pour la première fois. Toutefois, dans certains cas, votre application doit télécharger les fichiers d'extension elle-même en les demandant depuis une URL qui vous est fournie dans une réponse du service de gestion des licences d'applications de Google Play.

Pour télécharger vos fichiers d'extension, suivez les étapes suivantes :

  1. Lorsque l'application démarre, recherchez les fichiers d'extension dans l'espace de stockage partagé (dans le répertoire Android/obb/<package-name>/).
    1. Si les fichiers d'extension sont présents, vous n'avez rien à faire et votre application peut continuer.
    2. Si les fichiers d'extension ne sont pas présents :
      1. Effectuez une requête à l'aide du service de gestion des licences d'applications de Google Play pour obtenir les noms, les tailles et les URL des fichiers d'extension de votre application.
      2. Utilisez les URL fournies par Google Play pour télécharger les fichiers d'extension et les enregistrer. Vous devez enregistrer les fichiers sur l'espace de stockage partagé (Android/obb/<package-name>/) et utiliser le nom exact du fichier fourni par la réponse de Google Play.

        Remarque : L'URL fournie par Google Play pour vos fichiers d'extension est unique pour chaque téléchargement et expire très rapidement.

Si votre application est disponible sans frais (non payante), vous n'avez alors probablement pas utilisé le service degestions des licences d'applications. Ce service est conçu pour vous permettre d'appliquer des politiques de licences de votre application et pour vous assurer que l'utilisateur a le droit de l'utiliser (il ou elle l'a légitimement achetée sur Google Play). Afin de faciliter le fonctionnement du fichier d'extension, le service de gestion des licences a été amélioré pour fournir une réponse à votre application, qui inclut l'URL des fichiers d'extension de votre application hébergés sur Google Play. Ainsi, même si votre application est disponible sans frais pour les utilisateurs, vous devez inclure la bibliothèque LVL (License Verification Library) pour pouvoir utiliser les fichiers d'extension pour APK. Bien sûr, si votre application est disponible sans frais, vous n'avez pas besoin de vérifier la licence. Vous avez simplement besoin de la bibliothèque pour effectuer la requête qui renvoie l'URL de vos fichiers d'extension.

Remarque : Que votre application soit disponible sans frais ou non, Google Play ne renvoie les URL des fichiers d'extension que si l'utilisateur a acheté votre application sur Google Play.

En plus de la bibliothèque LVL, vous avez besoin d'un ensemble de code qui télécharge les fichiers d'extension via une connexion HTTP et les enregistre à l'emplacement approprié sur l'espace de stockage partagé de l'appareil. Lorsque vous créez cette procédure dans votre application, vous devez prendre en compte plusieurs problèmes :

  • Il est possible que l'appareil n'ait pas assez d'espace pour les fichiers d'extension. Vous devez donc procéder à cette vérification avant le début du téléchargement et avertir l'utilisateur en cas d'espace insuffisant.
  • Les fichiers téléchargés devraient s'exécuter dans un service d'arrière-plan afin d'éviter de bloquer l'interaction de l'utilisateur et de lui permettre de quitter votre application pendant la durée du téléchargement.
  • Plusieurs erreurs peuvent se produire pendant la requête et le téléchargement, et vous devez les résoudre correctement.
  • La connectivité réseau peut changer pendant le téléchargement. Vous devez donc gérer ces modifications et, le cas échéant, reprendre le téléchargement si possible.
  • Pendant que le téléchargement s'effectue en arrière-plan, vous devez fournir une notification indiquant la progression du téléchargement et la fin de ce dernier à l'utilisateur, qui le redirige vers votre application une fois sélectionnée.

Pour vous faciliter la tâche, nous avons créé la bibliothèque de téléchargements, qui demande les URL des fichiers d'extension via le service de gestion des licences et les télécharge. Elle effectue toutes les tâches indiquées ci-dessus et permet même à votre activité de suspendre et de reprendre le téléchargement. Il vous suffit d'ajouter la bibliothèque de téléchargements et quelques hooks de code à votre application. Le code pour le téléchargement des fichiers d'extension est presque entièrement programmé. Par conséquent, afin de proposer une expérience utilisateur optimale sans trop d'effort de votre part, nous vous recommandons d'utiliser la bibliothèque de téléchargements pour télécharger vos fichiers d'extension. Les informations des sections suivantes expliquent comment intégrer la bibliothèque à votre application.

Si vous préférez développer votre propre solution pour télécharger les fichiers d'extension à l'aide des URL Google Play, vous devez suivre la documentation sur la gestion des licences d'applications pour effectuer une demande de licence, puis récupérer les noms des extensions de fichiers, leurs tailles et URL depuis les réponses supplémentaires. Vous devez utiliser la classe APKExpansionPolicy (incluse dans la bibliothèque LVL) comme politique de licences. Elle permet de stocker les noms, tailles et URL des fichiers d'extension du service de gestion des licences.

À propos de la bibliothèque de téléchargements

Pour utiliser des fichiers d'extension pour APK avec votre application et proposer une expérience utilisateur optimale en toute simplicité, nous vous recommandons d'utiliser la bibliothèque de téléchargements incluse dans le package Bibliothèque d'extensions APK Google Play. Cette bibliothèque télécharge vos fichiers d'extension dans un service d'arrière-plan, affiche une notification utilisateur avec l'état du téléchargement, gère la perte de connectivité réseau, reprend le téléchargement si possible, et plus encore.

Pour implémenter les téléchargements de fichiers d'extension à l'aide de la bibliothèque de téléchargements, procédez comme suit :

  • Étendez une sous-classe Service spéciale et la sous-classe BroadcastReceiver en saisissant quelques lignes de code.
  • Ajoutez à votre activité principale une logique qui vérifie si les fichiers d'extension ont déjà été téléchargés et, si ce n'est pas le cas, qui appelle le processus de téléchargement et affiche une UI de progression.
  • Implémentez une interface de rappel avec quelques méthodes dans votre activité principale qui reçoit des mises à jour sur la progression du téléchargement.

Les sections suivantes expliquent comment configurer votre application à l'aide de la bibliothèque de téléchargements.

Préparer l'utilisation de la bibliothèque de téléchargements

Pour utiliser la bibliothèque de téléchargements, vous devez télécharger deux packages à partir de SDK Manager et ajouter les bibliothèques appropriées à votre application.

Commencez par ouvrir le SDK Manager Android (Outils > SDK Manager) et sous Apparence et comportement > Paramètres système > SDK Android, sélectionnez l'icône SDK Tools pour sélectionner et télécharger :

  • Package Bibliothèque de gestion des licences de Google Play
  • Package Bibliothèque d'extensions d'APK de Google Play

Créez un nouveau module de bibliothèque pour la LVL et pour la bibliothèque de téléchargements. Pour chaque bibliothèque :

  1. Sélectionnez Fichier > Nouveau > Nouveau module.
  2. Dans la fenêtre Créer un module, sélectionnez Bibliothèque Android , puis Suivant.
  3. Indiquez un nom d'application ou de bibliothèque, tel que "Bibliothèque de licences Google Play" ou "Bibliothèque de téléchargements Google Play", sélectionnez Niveau de SDK minimal, puis sélectionnez Terminer.
  4. Sélectionnez Fichier > Structure du projet.
  5. Sélectionnez l'onglet Propriétés, puis dans le dépôt de la bibliothèque, saisissez la bibliothèque du répertoire <sdk>/extras/google/ (play_licensing/ pour la bibliothèque de vérification des licences ou play_apk_expansion/downloader_library/ pour la bibliothèque de téléchargements).
  6. Sélectionnez OK pour créer le nouveau module.

Remarque : La bibliothèque de téléchargements dépend de la bibliothèque LVL. Veillez à ajouter la bibliothèque LVL aux propriétés de projet de la bibliothèque de téléchargements.

Vous pouvez également mettre à jour votre projet à partir d'une ligne de commande pour inclure les bibliothèques :

  1. Remplacez les répertoires par le répertoire <sdk>/tools/.
  2. Exécutez android update project avec l'option --library pour ajouter à la fois la bibliothèque LVL et celle de téléchargements à votre projet. Par exemple :
    android update project --path ~/Android/MyApp \
    --library ~/android_sdk/extras/google/market_licensing \
    --library ~/android_sdk/extras/google/market_apk_expansion/downloader_library
    

Une fois ces bibliothèques ajoutées à votre application, vous pourrez rapidement intégrer la fonctionnalité de téléchargement des fichiers d'extension depuis Google Play. Le format que vous choisissez pour les fichiers d'extension et la manière dont vous les lisez à partir du stockage partagé est une implémentation distincte que vous devez penser en fonction des besoins de votre application.

Conseil : le package d'extensions d'APK inclut une application exemple qui montre comment utiliser la bibliothèque de téléchargements dans une application. L'exemple utilise une troisième bibliothèque disponible dans le package d'extensions d'APK, appelée bibliothèque ZIP d'extensions d'APK. Si vous prévoyez d'utiliser des fichiers ZIP pour vos fichiers d'extension, nous vous suggérons également d'ajouter la bibliothèque ZIP d'extensions d'APK à votre application. Pour plus d'informations, consultez la section Utiliser la bibliothèque ZIP d'extensions d'APK ci-dessous.

Déclarer des autorisations de l'utilisateur

Pour télécharger les fichiers d'extension, la bibliothèque de téléchargements a besoin que vous déclariez plusieurs autorisations dans le fichier manifeste de votre application, à savoir :

<manifest ...>
    <!-- Required to access Google Play Licensing -->
    <uses-permission android:name="com.android.vending.CHECK_LICENSE" />

    <!-- Required to download files from Google Play -->
    <uses-permission android:name="android.permission.INTERNET" />

    <!-- Required to keep CPU alive while downloading files
        (NOT to keep screen awake) -->
    <uses-permission android:name="android.permission.WAKE_LOCK" />

    <!-- Required to poll the state of the network connection
        and respond to changes -->
    <uses-permission
        android:name="android.permission.ACCESS_NETWORK_STATE" />

    <!-- Required to check whether Wi-Fi is enabled -->
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>

    <!-- Required to read and write the expansion files on shared storage -->
    <uses-permission
        android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    ...
</manifest>

Remarque : Par défaut, la bibliothèque de téléchargements nécessite le niveau d'API 4, mais la bibliothèque ZIP d'extensions d'APK doit utiliser le niveau d'API 5.

Implémenter le service de téléchargement

Pour effectuer des téléchargements en arrière-plan, la bibliothèque de téléchargements fournit sa propre sous-classe Service appelée DownloaderService que vous devez étendre. En plus de télécharger les fichiers d'extension à votre place, DownloaderService :

  • enregistre un BroadcastReceiver qui écoute les modifications de la connectivité réseau de l'appareil (diffusion CONNECTIVITY_ACTION) afin de suspendre le téléchargement si besoin (par exemple, en raison d'une perte de connectivité) et de le reprendre lorsque cela est possible (la connectivité est rétablie) ;
  • programme une alarme RTC_WAKEUP pour relancer le téléchargement en cas de fermeture du service ;
  • crée un objet Notification personnalisé qui affiche la progression du téléchargement, ainsi que les erreurs ou les changements d'état ;
  • permet à votre application de suspendre et reprendre manuellement le téléchargement ;
  • vérifie que l'espace de stockage partagé est installé et disponible, que les fichiers n'existent pas déjà et que l'espace est suffisant avant de télécharger les fichiers d'extension. Il avertit ensuite l'utilisateur si l'une de ces conditions n'est pas remplie.

Il vous suffit de créer une classe dans votre application qui étend la classe DownloaderService et ignore trois méthodes pour fournir des informations spécifiques sur l'application :

getPublicKey()
Doit renvoyer une chaîne correspondant à la clé publique RSA codée en base64 pour votre compte éditeur, disponible sur la page de profil de la Play Console (voir Configurer la gestion des licences).
getSALT()
Doit renvoyer un tableau d'octets aléatoires utilisé par la gestion des licences Policy pour créer un Obfuscator. Le salage garantit que votre fichier SharedPreferences obscurci dans lequel vos données de licence sont enregistrées soit unique et non visible.
getAlarmReceiverClassName()
Doit renvoyer le nom de classe de BroadcastReceiver dans votre application, qui doit recevoir l'alarme indiquant que le téléchargement doit être redémarré (ce qui peut se produire si le service de téléchargement s'arrête de manière inattendue).

Par exemple, voici une implémentation complète de DownloaderService :

Kotlin

// You must use the public key belonging to your publisher account
const val BASE64_PUBLIC_KEY = "YourLVLKey"
// You should also modify this salt
val SALT = byteArrayOf(
        1, 42, -12, -1, 54, 98, -100, -12, 43, 2,
        -8, -4, 9, 5, -106, -107, -33, 45, -1, 84
)

class SampleDownloaderService : DownloaderService() {

    override fun getPublicKey(): String = BASE64_PUBLIC_KEY

    override fun getSALT(): ByteArray = SALT

    override fun getAlarmReceiverClassName(): String = SampleAlarmReceiver::class.java.name
}

Java

public class SampleDownloaderService extends DownloaderService {
    // You must use the public key belonging to your publisher account
    public static final String BASE64_PUBLIC_KEY = "YourLVLKey";
    // You should also modify this salt
    public static final byte[] SALT = new byte[] { 1, 42, -12, -1, 54, 98,
            -100, -12, 43, 2, -8, -4, 9, 5, -106, -107, -33, 45, -1, 84
    };

    @Override
    public String getPublicKey() {
        return BASE64_PUBLIC_KEY;
    }

    @Override
    public byte[] getSALT() {
        return SALT;
    }

    @Override
    public String getAlarmReceiverClassName() {
        return SampleAlarmReceiver.class.getName();
    }
}

Avertissement : Vous devez mettre à jour la valeur BASE64_PUBLIC_KEY pour qu'elle corresponde à la clé publique de votre compte éditeur. Vous trouverez la clé dans la Play Console sous les informations de votre profil. Cette étape est nécessaire même lorsque vous testez vos téléchargements.

N'oubliez pas de déclarer le service dans votre fichier manifeste :

<app ...>
    <service android:name=".SampleDownloaderService" />
    ...
</app>

Implémenter le récepteur d'alarme

Afin de surveiller la progression du téléchargement du fichier et de le redémarrer si nécessaire, DownloaderService planifie une alarme RTC_WAKEUP qui envoie un Intent à un BroadcastReceiver dans votre application. Vous devez définir le BroadcastReceiver pour appeler une API depuis la bibliothèque de téléchargements, qui vérifie l'état du téléchargement et le redémarre si nécessaire.

Il vous suffit de remplacer la méthode onReceive() pour appeler DownloaderClientMarshaller.startDownloadServiceIfRequired().

Par exemple :

Kotlin

class SampleAlarmReceiver : BroadcastReceiver() {

    override fun onReceive(context: Context, intent: Intent) {
        try {
            DownloaderClientMarshaller.startDownloadServiceIfRequired(
                    context,
                    intent,
                    SampleDownloaderService::class.java
            )
        } catch (e: PackageManager.NameNotFoundException) {
            e.printStackTrace()
        }
    }
}

Java

public class SampleAlarmReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        try {
            DownloaderClientMarshaller.startDownloadServiceIfRequired(context,
                intent, SampleDownloaderService.class);
        } catch (NameNotFoundException e) {
            e.printStackTrace();
        }
    }
}

Notez qu'il s'agit de la classe pour laquelle vous devez renvoyer le nom dans la méthode getAlarmReceiverClassName() de votre service (voir la section précédente).

N'oubliez pas de déclarer le destinataire dans votre fichier manifeste :

<app ...>
    <receiver android:name=".SampleAlarmReceiver" />
    ...
</app>

Lancer le téléchargement

L'activité principale de votre application (celle démarrée par votre icône de lanceur) est de vérifier si les fichiers d'extension se trouvent déjà sur l'appareil et de lancer le téléchargement, si ce n'est pas le cas.

Pour lancer le téléchargement à l'aide de la bibliothèque de téléchargements, procédez comme suit :

  1. Vérifiez si les fichiers ont été téléchargés.

    La bibliothèque de téléchargements comprend des API dans la classe Helper pour faciliter ce processus :

    • getExpansionAPKFileName(Context, c, boolean mainFile, int versionCode)
    • doesFileExist(Context c, String fileName, long fileSize)

    Par exemple, l'application exemple fourni dans le package d'extensions d'APK appelle la méthode suivante dans la méthode onCreate() de l'activité pour vérifier si les fichiers d'extension existent déjà sur l'appareil :

    Kotlin

    fun expansionFilesDelivered(): Boolean {
        xAPKS.forEach { xf ->
            Helpers.getExpansionAPKFileName(this, xf.isBase, xf.fileVersion).also { fileName ->
                if (!Helpers.doesFileExist(this, fileName, xf.fileSize, false))
                    return false
            }
        }
        return true
    }
    

    Java

    boolean expansionFilesDelivered() {
        for (XAPKFile xf : xAPKS) {
            String fileName = Helpers.getExpansionAPKFileName(this, xf.isBase,
                xf.fileVersion);
            if (!Helpers.doesFileExist(this, fileName, xf.fileSize, false))
                return false;
        }
        return true;
    }
    

    Dans ce cas, chaque objet XAPKFile contient le numéro de version et la taille d'un fichier d'extension connu, ainsi qu'une valeur booléenne indiquant s'il s'agit du fichier d'extension principal. (Consultez la classe SampleDownloaderActivity de l'application exemple pour accéder à ces informations.)

    Si cette méthode renvoie "false" (faux), l'application doit lancer le téléchargement.

  2. Lancez le téléchargement en appelant la méthode statique DownloaderClientMarshaller.startDownloadServiceIfRequired(Context c, PendingIntent notificationClient, Class<?> serviceClass).

    La méthode utilise les paramètres suivants :

    • context : le Context (Contexte) de l'application.
    • notificationClient : un élément PendingIntent pour démarrer votre activité principale et utilisé dans la Notification créée par DownloaderService pour afficher la progression du téléchargement. Lorsque l'utilisateur sélectionne la notification, le système appelle l'élément PendingIntent pour ouvrir l'activité qui affiche la progression du téléchargement (généralement la même activité que celle qui a lancé le téléchargement).
    • serviceClass : l'objet Class pour votre implémentation de DownloaderService, requis pour démarrer le service et lancer le téléchargement si nécessaire

    La méthode renvoie un entier indiquant si le téléchargement est nécessaire ou non. Les valeurs possibles sont :

    • NO_DOWNLOAD_REQUIRED : retournée si les fichiers existent déjà ou si un téléchargement est déjà en cours
    • LVL_CHECK_REQUIRED : retournée si une vérification de licence est requise pour obtenir les URL des fichiers d'extension
    • DOWNLOAD_REQUIRED : retournée si les URL des fichiers d'extension sont déjà connues, mais n'ont pas été téléchargées

    Le comportement de LVL_CHECK_REQUIRED et DOWNLOAD_REQUIRED est essentiellement le même, et vous n'avez normalement pas à vous en soucier. Dans votre activité principale qui appelle startDownloadServiceIfRequired(), vous pouvez simplement vérifier si la réponse est NO_DOWNLOAD_REQUIRED. Si la réponse n'est pas NO_DOWNLOAD_REQUIRED, la bibliothèque de téléchargements lance le téléchargement et vous devez mettre à jour votre UI d'activité pour afficher la progression du téléchargement (voir l'étape suivante). Si la réponse est NO_DOWNLOAD_REQUIRED, les fichiers sont disponibles et votre application peut démarrer.

    Par exemple :

    Kotlin

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
    
        // Check if expansion files are available before going any further
        if (!expansionFilesDelivered()) {
            val pendingIntent =
                    // Build an Intent to start this activity from the Notification
                    Intent(this, MainActivity::class.java).apply {
                        flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
                    }.let { notifierIntent ->
                        PendingIntent.getActivity(
                                this,
                                0,
                                notifierIntent,
                                PendingIntent.FLAG_UPDATE_CURRENT
                        )
                    }
    
    
            // Start the download service (if required)
            val startResult: Int = DownloaderClientMarshaller.startDownloadServiceIfRequired(
                    this,
                    pendingIntent,
                    SampleDownloaderService::class.java
            )
            // If download has started, initialize this activity to show
            // download progress
            if (startResult != DownloaderClientMarshaller.NO_DOWNLOAD_REQUIRED) {
                // This is where you do set up to display the download
                // progress (next step)
                ...
                return
            } // If the download wasn't necessary, fall through to start the app
        }
        startApp() // Expansion files are available, start the app
    }
    

    Java

    @Override
    public void onCreate(Bundle savedInstanceState) {
        // Check if expansion files are available before going any further
        if (!expansionFilesDelivered()) {
            // Build an Intent to start this activity from the Notification
            Intent notifierIntent = new Intent(this, MainActivity.getClass());
            notifierIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
                                    Intent.FLAG_ACTIVITY_CLEAR_TOP);
            ...
            PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,
                    notifierIntent, PendingIntent.FLAG_UPDATE_CURRENT);
    
            // Start the download service (if required)
            int startResult =
                DownloaderClientMarshaller.startDownloadServiceIfRequired(this,
                            pendingIntent, SampleDownloaderService.class);
            // If download has started, initialize this activity to show
            // download progress
            if (startResult != DownloaderClientMarshaller.NO_DOWNLOAD_REQUIRED) {
                // This is where you do set up to display the download
                // progress (next step)
                ...
                return;
            } // If the download wasn't necessary, fall through to start the app
        }
        startApp(); // Expansion files are available, start the app
    }
    
  3. Lorsque la méthode startDownloadServiceIfRequired() renvoie autre chose que NO_DOWNLOAD_REQUIRED, créez une instance de IStub en appelant DownloaderClientMarshaller.CreateStub(IDownloaderClient client, Class<?> downloaderService). IStub fournit une liaison entre votre activité et le service de téléchargement afin que votre activité reçoive des rappels concernant la progression du téléchargement.

    Pour instancier votre IStub en appelant CreateStub(), vous devez lui transmettre une implémentation de l'interface IDownloaderClient et de votre DownloaderService. La section suivante intitulée Recevoir la progression du téléchargement décrit l'interface IDownloaderClient, que vous devez généralement implémenter dans votre classe Activity pour mettre à jour l'interface utilisateur de l'activité lorsque l'état du téléchargement change.

    Nous vous recommandons d'appeler CreateStub() pour instancier IStub lors de la méthode onCreate() de votre activité, une fois que startDownloadServiceIfRequired() a lancé le téléchargement.

    Par exemple, dans l'exemple de code précédent pour onCreate(), vous pouvez répondre au résultat startDownloadServiceIfRequired() comme suit :

    Kotlin

            // Start the download service (if required)
            val startResult = DownloaderClientMarshaller.startDownloadServiceIfRequired(
                    this@MainActivity,
                    pendingIntent,
                    SampleDownloaderService::class.java
            )
            // If download has started, initialize activity to show progress
            if (startResult != DownloaderClientMarshaller.NO_DOWNLOAD_REQUIRED) {
                // Instantiate a member instance of IStub
                downloaderClientStub =
                        DownloaderClientMarshaller.CreateStub(this, SampleDownloaderService::class.java)
                // Inflate layout that shows download progress
                setContentView(R.layout.downloader_ui)
                return
            }
    

    Java

            // Start the download service (if required)
            int startResult =
                DownloaderClientMarshaller.startDownloadServiceIfRequired(this,
                            pendingIntent, SampleDownloaderService.class);
            // If download has started, initialize activity to show progress
            if (startResult != DownloaderClientMarshaller.NO_DOWNLOAD_REQUIRED) {
                // Instantiate a member instance of IStub
                downloaderClientStub = DownloaderClientMarshaller.CreateStub(this,
                        SampleDownloaderService.class);
                // Inflate layout that shows download progress
                setContentView(R.layout.downloader_ui);
                return;
            }
    

    Une fois la méthode onCreate() renvoyée, votre activité reçoit un appel à onResume(), où vous devez appeler connect() sur IStub, en lui transmettant le Context de votre application. À l'inverse, vous devez appeler disconnect() dans le rappel onStop() de votre activité.

    Kotlin

    override fun onResume() {
        downloaderClientStub?.connect(this)
        super.onResume()
    }
    
    override fun onStop() {
        downloaderClientStub?.disconnect(this)
        super.onStop()
    }
    

    Java

    @Override
    protected void onResume() {
        if (null != downloaderClientStub) {
            downloaderClientStub.connect(this);
        }
        super.onResume();
    }
    
    @Override
    protected void onStop() {
        if (null != downloaderClientStub) {
            downloaderClientStub.disconnect(this);
        }
        super.onStop();
    }
    

    L'appel de connect() sur IStub lie votre activité à DownloaderService, de sorte que votre activité reçoit des rappels concernant les modifications de l'état de téléchargement via l'interface IDownloaderClient.

Recevoir la progression du téléchargement

Pour recevoir des informations sur la progression du téléchargement et pour interagir avec DownloaderService, vous devez implémenter l'interface IDownloaderClient de la bibliothèque de téléchargements. En règle générale, l'activité que vous utilisez pour lancer le téléchargement doit implémenter cette interface afin d'afficher la progression du téléchargement et d'envoyer des requêtes au service.

Les méthodes d'interface requises pour IDownloaderClient sont les suivantes :

onServiceConnected(Messenger m)
Après avoir instancié IStub dans votre activité, vous recevrez un appel à cette méthode, qui transmet un objet Messenger connecté à votre instance DownloaderService. Pour envoyer des requêtes au service, par exemple pour suspendre ou reprendre des téléchargements, vous devez appeler DownloaderServiceMarshaller.CreateProxy() pour recevoir l'interface IDownloaderService connectée au service.

Voici une implémentation recommandée :

Kotlin

private var remoteService: IDownloaderService? = null
...

override fun onServiceConnected(m: Messenger) {
    remoteService = DownloaderServiceMarshaller.CreateProxy(m).apply {
        downloaderClientStub?.messenger?.also { messenger ->
            onClientUpdated(messenger)
        }
    }
}

Java

private IDownloaderService remoteService;
...

@Override
public void onServiceConnected(Messenger m) {
    remoteService = DownloaderServiceMarshaller.CreateProxy(m);
    remoteService.onClientUpdated(downloaderClientStub.getMessenger());
}

Une fois l'objet IDownloaderService initialisé, vous pouvez envoyer des commandes au service de téléchargement, par exemple pour suspendre et reprendre le téléchargement (requestPauseDownload() et requestContinueDownload()).

onDownloadStateChanged(int newState)
Le service de téléchargement appelle cette méthode lorsqu'un changement d'état se produit, par exemple quand le téléchargement commence ou se termine.

La valeur newState sera l'une des nombreuses valeurs possibles spécifiées dans l'une des constantes STATE_* de la classe IDownloaderClient.

Pour proposer un message utile à vos utilisateurs, vous pouvez demander une chaîne correspondant à chaque état en appelant Helpers.getDownloaderStringResourceIDFromState(). Cela renvoie l'ID de ressource de l'une des chaînes fournies avec la bibliothèque de téléchargements. Par exemple, la chaîne "Téléchargement suspendu, car vous êtes en itinérance" correspond à STATE_PAUSED_ROAMING.

onDownloadProgress(DownloadProgressInfo progress)
Le service de téléchargement appelle cette méthode pour fournir un objet DownloadProgressInfo, qui décrit différentes informations sur la progression du téléchargement, y compris le temps restant estimé, la vitesse actuelle, la progression globale et le total afin que vous puissiez mettre à jour l'interface utilisateur de la progression du téléchargement.

Conseil : Pour des exemples de rappels qui mettent à jour l'interface utilisateur de la progression des téléchargements, consultez SampleDownloaderActivity dans l'application exemple fournie avec le package d'extensions d'APK.

Voici quelques méthodes publiques de l'interface IDownloaderService qui pourraient vous être utiles :

requestPauseDownload()
Interrompt le téléchargement.
requestContinueDownload()
Reprend un téléchargement suspendu.
setDownloadFlags(int flags)
Définit les préférences de l'utilisateur pour les types de réseaux sur lesquels il est possible de télécharger les fichiers. L'implémentation actuelle accepte un indicateur, FLAGS_DOWNLOAD_OVER_CELLULAR, mais vous pouvez en ajouter d'autres. Par défaut, cet indicateur n'est pas activé. L'utilisateur doit donc être connecté au Wi-Fi pour télécharger les fichiers d'extension. Vous souhaitez peut-être autoriser l'utilisateur à autoriser les téléchargements sur le réseau mobile. Dans ce cas, vous pouvez appeler :

Kotlin

remoteService = DownloaderServiceMarshaller.CreateProxy(m).apply {
    ...
    setDownloadFlags(IDownloaderService.FLAGS_DOWNLOAD_OVER_CELLULAR)
}

Java

remoteService
    .setDownloadFlags(IDownloaderService.FLAGS_DOWNLOAD_OVER_CELLULAR);

Utiliser APKExpansionPolicy

Si vous décidez de créer votre propre service de téléchargement au lieu d'utiliser la bibliothèque de téléchargements de Google Play, vous devez toujours utiliser APKExpansionPolicy, fourni dans la bibliothèque LVL. La classe APKExpansionPolicy est presque identique à ServerManagedPolicy (disponible dans la bibliothèque LVL Google Play), mais inclut une gestion supplémentaire pour les réponses supplémentaires du fichier d'extension pour APK.

Remarque : Si vous utilisez la bibliothèque de téléchargements comme indiqué dans la section précédente, la bibliothèque effectue toutes les interactions avec APKExpansionPolicy. Vous n'avez donc pas besoin d'utiliser cette classe directement.

La classe comprend des méthodes permettant d'obtenir les informations nécessaires sur les fichiers d'extension disponibles :

  • getExpansionURLCount()
  • getExpansionURL(int index)
  • getExpansionFileName(int index)
  • getExpansionFileSize(int index)

Pour plus d'informations sur l'utilisation de APKExpansionPolicy lorsque vous n'utilisez pas la bibliothèque de téléchargements, consultez la documentation pour Ajouter des licences à votre application, qui explique comment implémenter une politique de licence de ce type.

Lire le fichier d'extension

Une fois les fichiers d'extension pour APK enregistrés sur l'appareil, leur lecture dépend du type de fichier que vous avez utilisé. Comme indiqué dans la présentation, vos fichiers d'extension peuvent correspondre à n'importe quel type de fichier, mais ils sont renommés selon un format de nom de fichier particulier et sont enregistrés sur <shared-storage>/Android/obb/<package-name>/.

Quelle que soit la façon dont vous lisez vos fichiers, vous devez toujours vérifier que le stockage externe est disponible en lecture. Il est possible que l'espace de stockage de l'utilisateur soit installé sur un ordinateur via USB ou que la carte SD ait été retirée.

Remarque : Lorsque l'application démarre, vous devez toujours vérifier si l'espace de stockage externe est disponible et lisible en appelant getExternalStorageState(). Cette commande renvoie l'une des nombreuses chaînes possibles représentant l'état de la mémoire du stockage externe. Pour qu'elle soit lisible par votre application, sa valeur doit être MEDIA_MOUNTED.

Obtenir les noms des fichiers

Comme décrit dans la présentation, les fichiers d'extension pour APK sont enregistrés sous un format de nom de fichier spécifique :

[main|patch].<expansion-version>.<package-name>.obb

Pour obtenir l'emplacement et le nom de vos fichiers d'extension, vous devez utiliser les méthodes getExternalStorageDirectory() et getPackageName() pour construire le chemin d'accès à vos fichiers.

Voici une méthode que vous pouvez utiliser dans votre application pour obtenir un tableau contenant le chemin d'accès complet à vos fichiers d'extension :

Kotlin

fun getAPKExpansionFiles(ctx: Context, mainVersion: Int, patchVersion: Int): Array<String> {
    val packageName = ctx.packageName
    val ret = mutableListOf<String>()
    if (Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED) {
        // Build the full path to the app's expansion files
        val root = Environment.getExternalStorageDirectory()
        val expPath = File(root.toString() + EXP_PATH + packageName)

        // Check that expansion file path exists
        if (expPath.exists()) {
            if (mainVersion > 0) {
                val strMainPath = "$expPath${File.separator}main.$mainVersion.$packageName.obb"
                val main = File(strMainPath)
                if (main.isFile) {
                    ret += strMainPath
                }
            }
            if (patchVersion > 0) {
                val strPatchPath = "$expPath${File.separator}patch.$mainVersion.$packageName.obb"
                val main = File(strPatchPath)
                if (main.isFile) {
                    ret += strPatchPath
                }
            }
        }
    }
    return ret.toTypedArray()
}

Java

// The shared path to all app expansion files
private final static String EXP_PATH = "/Android/obb/";

static String[] getAPKExpansionFiles(Context ctx, int mainVersion,
      int patchVersion) {
    String packageName = ctx.getPackageName();
    Vector<String> ret = new Vector<String>();
    if (Environment.getExternalStorageState()
          .equals(Environment.MEDIA_MOUNTED)) {
        // Build the full path to the app's expansion files
        File root = Environment.getExternalStorageDirectory();
        File expPath = new File(root.toString() + EXP_PATH + packageName);

        // Check that expansion file path exists
        if (expPath.exists()) {
            if ( mainVersion > 0 ) {
                String strMainPath = expPath + File.separator + "main." +
                        mainVersion + "." + packageName + ".obb";
                File main = new File(strMainPath);
                if ( main.isFile() ) {
                        ret.add(strMainPath);
                }
            }
            if ( patchVersion > 0 ) {
                String strPatchPath = expPath + File.separator + "patch." +
                        mainVersion + "." + packageName + ".obb";
                File main = new File(strPatchPath);
                if ( main.isFile() ) {
                        ret.add(strPatchPath);
                }
            }
        }
    }
    String[] retArray = new String[ret.size()];
    ret.toArray(retArray);
    return retArray;
}

Vous pouvez appeler cette méthode en lui transmettant le Context de votre application et la version du fichier d'extension souhaitée.

Il existe de nombreuses façons de déterminer le numéro de version du fichier d'extension. Une méthode simple consiste à enregistrer la version dans un fichier SharedPreferences au début du téléchargement, en utilisant la méthode getExpansionFileName(int index) de la classe APKExpansionPolicy pour demander le nom du fichier d'extension. Vous pouvez ensuite obtenir le code de version en lisant le fichier SharedPreferences lorsque vous souhaitez accéder au fichier d'extension.

Pour en savoir plus sur la lecture depuis le stockage partagé, consultez la documentation sur le stockage de données.

Utiliser la bibliothèque ZIP d'extensions d'APK

Le package d'extensions d'APK Google Market comprend une bibliothèque appelée bibliothèque ZIP d'extensions d'APK (située dans <sdk>/extras/google/google_market_apk_expansion/zip_file/). Cette bibliothèque facultative vous permet de lire vos fichiers d'extension lorsqu'ils sont enregistrés en tant que fichiers ZIP. Cette bibliothèque vous permet de lire facilement les ressources de vos fichiers d'extension ZIP en tant que système de fichiers virtuel.

La bibliothèque ZIP d'extensions d'APK inclut les classes et les API suivantes :

APKExpansionSupport
Fournit plusieurs méthodes pour accéder aux noms des fichiers d'extension et aux fichiers ZIP :
getAPKExpansionFiles()
La méthode ci-dessus renvoie la totalité du chemin d'accès aux deux fichiers d'extension.
getAPKExpansionZipFile(Context ctx, int mainVersion, int patchVersion)
Renvoie ZipResourceFile, représentant la somme du fichier principal et du fichier correctif. Autrement dit, si vous spécifiez à la fois mainVersion et patchVersion, ZipResourceFile est renvoyé et fournit un accès en lecture à toutes les données (les données du fichier correctif étant fusionnées avec celles du fichier principal).
ZipResourceFile
Représente un fichier ZIP dans l'espace de stockage partagé et effectue toutes les tâches requises pour fournir un système de fichiers virtuel basé sur vos fichiers ZIP. Vous pouvez obtenir une instance à l'aide de APKExpansionSupport.getAPKExpansionZipFile() ou de ZipResourceFile, en transmettant le chemin d'accès à votre fichier d'extension. Cette classe comprend diverses méthodes utiles, mais vous n'avez généralement pas besoin d'accéder à la plupart d'entre elles. Voici quelques méthodes importantes :
getInputStream(String assetPath)
Fournit InputStream, permettant de lire un fichier dans le fichier ZIP. assetPath doit être le chemin d'accès au fichier souhaité, par rapport à la racine des contenus du fichier ZIP.
getAssetFileDescriptor(String assetPath)
Fournit un AssetFileDescriptor pour un fichier du fichier ZIP. assetPath doit être le chemin d'accès au fichier souhaité, par rapport à la racine des contenus du fichier ZIP. Ceci est utile pour certaines API Android nécessitant un AssetFileDescriptor, comme certaines API MediaPlayer.
APEZProvider
La plupart des applications n'ont pas besoin d'utiliser cette classe. Cette classe définit un ContentProvider qui regroupe les données des fichiers ZIP via un fournisseur de contenu Uri afin de fournir un accès aux fichiers pour certaines API Android qui ont besoin d'un accès Uri aux fichiers multimédias. Ceci peut s'avérer utile si vous souhaitez lire une vidéo avec VideoView.setVideoURI(), par exemple.

Ignorer la compression des fichiers multimédias au format ZIP

Si vous utilisez vos fichiers d'extension pour stocker des fichiers multimédias, un fichier ZIP vous permet quand même d'utiliser les appels de lecture de contenus multimédias Android qui offrent des contrôles de décalage et de longueur (tels que MediaPlayer.setDataSource() et SoundPool.load()). Pour cela, vous ne devez pas effectuer de compression supplémentaire sur les fichiers multimédias lors de la création des packages ZIP. Par exemple, si vous utilisez l'outil zip, vous devez utiliser l'option -n pour spécifier les suffixes des fichiers ne devant pas être compressés :

zip -n .mp4;.ogg main_expansion media_files

Lire à partir d'un fichier ZIP

Lorsque vous utilisez la bibliothèque ZIP d'extensions d'APK, la lecture d'un fichier à partir de votre ZIP nécessite généralement les éléments suivants :

Kotlin

// Get a ZipResourceFile representing a merger of both the main and patch files
val expansionFile =
        APKExpansionSupport.getAPKExpansionZipFile(appContext, mainVersion, patchVersion)

// Get an input stream for a known file inside the expansion file ZIPs
expansionFile.getInputStream(pathToFileInsideZip).use {
    ...
}

Java

// Get a ZipResourceFile representing a merger of both the main and patch files
ZipResourceFile expansionFile =
    APKExpansionSupport.getAPKExpansionZipFile(appContext,
        mainVersion, patchVersion);

// Get an input stream for a known file inside the expansion file ZIPs
InputStream fileStream = expansionFile.getInputStream(pathToFileInsideZip);

Le code ci-dessus permet d'accéder à n'importe quel fichier de votre fichier d'extension principal ou de votre fichier d'extension de correctif, en lisant une map fusionnée de tous les fichiers des deux fichiers. Pour fournir la méthode getAPKExpansionFile(), vous n'avez besoin que du android.content.Context de votre application et du numéro de version du fichier d'extension principal et du fichier d'extension de correctif.

Si vous préférez lire un fichier d'extension spécifique, vous pouvez utiliser le constructeur ZipResourceFile en indiquant le chemin d'accès au fichier d'extension souhaité :

Kotlin

// Get a ZipResourceFile representing a specific expansion file
val expansionFile = ZipResourceFile(filePathToMyZip)

// Get an input stream for a known file inside the expansion file ZIPs
expansionFile.getInputStream(pathToFileInsideZip).use {
    ...
}

Java

// Get a ZipResourceFile representing a specific expansion file
ZipResourceFile expansionFile = new ZipResourceFile(filePathToMyZip);

// Get an input stream for a known file inside the expansion file ZIPs
InputStream fileStream = expansionFile.getInputStream(pathToFileInsideZip);

Pour en savoir plus sur l'utilisation de cette bibliothèque pour vos fichiers d'extension, consultez la classe SampleDownloaderActivity de l'application exemple, qui inclut du code supplémentaire pour vérifier les fichiers téléchargés à l'aide de CRC. Sachez que si vous utilisez cet exemple comme base pour votre propre implémentation, vous devez déclarer la taille en octets de vos fichiers d'extension dans le tableau xAPKS.

Tester vos fichiers d'extension

Avant de publier votre application, vous devez tester deux choses : lire les fichiers d'extension et télécharger les fichiers.

Tester les lectures de fichiers

Avant d'importer votre application sur Google Play, vous devez tester sa capacité à lire les fichiers à partir de l'espace de stockage partagé. Il vous suffit d'ajouter les fichiers à l'emplacement approprié dans l'espace de stockage partagé de l'appareil et de lancer votre application :

  1. Sur votre appareil, créez le répertoire approprié sur l'espace de stockage partagé dans lequel Google Play enregistrera vos fichiers.

    Par exemple, si le nom du package est com.example.android, vous devez créer le répertoire Android/obb/com.example.android/ sur l'espace de stockage partagé. (Branchez votre appareil de test sur votre ordinateur pour installer le stockage partagé et créez manuellement ce répertoire.)

  2. Ajoutez manuellement les fichiers d'extension à ce répertoire. Veillez à renommer vos fichiers selon le format de nom de fichier que Google Play utilisera.

    Par exemple, quel que soit le type de fichier, le fichier d'extension principal de l'application com.example.android doit être main.0300110.com.example.android.obb. Le code de version peut correspondre à n'importe quelle valeur. N'oubliez pas :

    • Le fichier d'extension principal commence toujours par main et le fichier correctif par patch.
    • Le nom du package correspond toujours à celui de l'APK auquel le fichier est joint sur Google Play.
  3. Maintenant que le ou les fichiers d'extension se trouvent sur l'appareil, vous pouvez installer et exécuter votre application pour tester ces fichiers.

Voici quelques rappels sur la gestion des fichiers d'extension :

  • Ne supprimez pas et ne renommez pas les fichiers d'extension .obb (même si vous décompressez les données à un autre emplacement). Cela entraînera le téléchargement répété du fichier d'extension par Google Play (ou par votre application).
  • N'enregistrez pas d'autres données dans votre répertoire obb/. Si vous devez décompresser certaines données, enregistrez-les à l'emplacement spécifié par getExternalFilesDir().

Tester les téléchargements de fichiers

Étant donné que votre application doit parfois télécharger manuellement les fichiers d'extension lors de la première ouverture, il est important de tester ce processus pour vous assurer que l'application peut interroger les URL, télécharger les fichiers et les enregistrer sur l'appareil.

Pour tester l'implémentation de la procédure de téléchargement manuel de votre application, vous pouvez la publier dans le canal de test interne. Ainsi, seuls les testeurs autorisés y auront accès. Si tout fonctionne comme prévu, votre application devrait commencer à télécharger les fichiers d'extension dès le début de l'activité principale.

Remarque : Auparavant, vous pouviez tester une application en important une version provisoire non publiée de celle-ci. Il n'est désormais plus possible de procéder de cette manière. Vous devez la publier dans un canal de test interne, fermé ou ouvert. Pour en savoir plus, consultez la page Les versions provisoires d'applications ne sont plus acceptées.

Mettre à jour votre application

L'un des principaux avantages d'utiliser des fichiers d'extension sur Google Play est la possibilité de mettre à jour votre application sans télécharger à nouveau tous les éléments d'origine. Étant donné que Google Play vous permet de fournir deux fichiers d'extension avec chaque APK, vous pouvez utiliser le second fichier comme un "correctif" fournissant des mises à jour et de nouveaux éléments. Cela évite d'avoir à télécharger à nouveau le fichier d'extension principal, qui peut être volumineux et s'avérer coûteux pour les utilisateurs.

D'un point de vue technique, le fichier d'extension de correctif est identique au fichier d'extension principal. Ni le système Android ni Google Play n'effectuent de correctifs réels entre vos fichiers d'extension principal et de correctif. Votre code d'application doit effectuer tous les correctifs nécessaires.

Si vous utilisez des fichiers ZIP en tant que fichiers d'extension, la bibliothèque ZIP d'extensions d'APK incluse dans le package d'extension d'APK permet de fusionner votre fichier correctif et votre fichier d'extension principale.

Remarque : Même si vous ne devez modifier que le fichier d'extension de correctif, vous devez tout de même mettre à jour l'APK pour que Google Play effectue une mise à jour. Si vous n'avez pas besoin de modifier le code dans l'application, il vous suffit de mettre à jour versionCode dans le fichier manifeste.

Tant que vous ne modifiez pas le fichier d'extension principal associé à l'APK dans la Play Console, les utilisateurs qui ont déjà installé votre application ne téléchargeront pas le fichier d'extension principal. Les utilisateurs existants ne reçoivent que l'APK mis à jour et le nouveau fichier d'extension de correctif (et conservent le fichier d'extension principal précédent).

Voici quelques points à retenir concernant les mises à jour des fichiers d'extension :

  • Il ne peut y avoir que deux fichiers d'extension à la fois pour votre application. Un fichier d'extension principal et un fichier d'extension de correctif. Lors de la mise à jour d'un fichier, Google Play supprime la version précédente (de même pour votre application pour les mises à jour manuelles).
  • Lors de l'ajout d'un fichier d'extension de correctif, le système Android n'applique pas le correctif à votre application ou à votre fichier d'extension principal. Vous devez concevoir votre application pour qu'elle puisse recevoir les données des correctifs. Toutefois, le package d'extensions d'APK inclut une bibliothèque permettant d'utiliser des fichiers ZIP en tant que fichiers d'extension, qui fusionnent les données du fichier correctif avec celles du fichier d'extension principal pour que vous puissiez facilement lire toutes les données du fichier d'extension.