Aller au contenu principal

Gestionnaire d'assets dynamiques (OtaAssetManager)

Le moteur natif C++/Rust applique des patchs binaires différentiels sur le code Dart compilé (binaire d'application sur Android, artefact de patch sur iOS), mais il ne réécrit pas l'archive en lecture seule flutter_assets/ empaquetée dans l'APK ou l'IPA d'origine lors de l'installation depuis le Store.

Pour mettre à jour à chaud des ressources statiques (fichiers de traduction JSON i18n, bannières promotionnelles, icônes SVG, polices ou configurations locales) sans soumettre une nouvelle version sur les Stores, le SDK fournit OtaAssetManager (lib/src/assets/ota_asset_manager.dart).

Architecture et fonctionnement​

Processus de chargement des assets dynamiques
  1. Vérification du cache local : Le SDK vérifie si l'asset est déjà présent sur le disque du terminal.
  2. Contrôle d'intégrité : L'empreinte du fichier local est comparée à l'empreinte attendue.
  3. Téléchargement si nécessaire : En cas d'absence ou de non-concordance, l'asset est téléchargé et mis en cache.
  4. Mise à disposition : L'asset est immédiatement utilisable par les widgets Flutter.

1. Décrire un asset distant (OtaRemoteAssetDescriptor)​

Chaque ressource téléchargeable est représentée par une instance immuable de OtaRemoteAssetDescriptor :

const descriptor = OtaRemoteAssetDescriptor(
assetKey: 'assets/i18n/fr_CM.json',
downloadUrl: 'https://ota.saramoney.internal/assets/fr_CM_v2.json',
expectedSha256: '99e887f4f4b6bf9d3abb767d3b5f215e0009e4d26cb7b958497e8b291e1b96dc',
);
ChampTypeDescription
assetKeyStringClé logique de l'asset (ex: assets/images/promo.png ou assets/i18n/fr_CM.json).
downloadUrlStringURL HTTP/HTTPS complète permettant de télécharger le fichier sur le serveur On-Premise.
expectedSha256StringEmpreinte SHA-256 hexadécimale attendue (insensible à la casse, normalisée en minuscules).

2. Télécharger, vérifier et mettre en cache (OtaAssetManager)​

Initialisation​

OtaAssetManager prend en paramètre un Directory cacheDirectory (où seront stockés les fichiers vérifiés) et un http.Client? httpClient optionnel (utile pour injecter un client avec timeouts personnalisés ou un MockClient en test) :

import 'dart:io';
import 'package:path/path.dart' as p;
import 'package:tk_ota_push/tk_ota_push.dart';

final cacheDir = Directory(p.join(Directory.systemTemp.path, 'ota_assets_cache'));
final assetManager = OtaAssetManager(cacheDirectory: cacheDir);

Structure du cache sur disque (cachedFileFor)​

La méthode cachedFileFor(OtaRemoteAssetDescriptor descriptor) construit un chemin déterministe isolé par empreinte SHA-256 :

  1. Elle nettoie assetKey en remplaçant tout caractère hors [a-zA-Z0-9._/-] par _ (safeKey).
  2. Elle normalise expectedSha256 en minuscules (cleanHash).
  3. Le fichier est stocké sous : <cacheDirectory.path>/<cleanHash>/<safeKey>.

Ainsi, dès que vous publiez une nouvelle version d'un asset avec un nouveau expectedSha256, son chemin en cache change automatiquement, évitant tout conflit d'invalidation de cache.

Récupération avec vérification SHA-256 (getOrDownloadAsset)​

try {
final File localAssetFile = await assetManager.getOrDownloadAsset(
descriptor,
headers: const {'Accept': 'application/json'},
);

final String jsonContent = await localAssetFile.readAsString();
// Utiliser le fichier local validé
} on OtaAssetIntegrityException catch (e) {
// L'empreinte SHA-256 reçue diffère de expectedSha256
debugPrint('Alerte intégrité asset ${e.assetKey} : attendu=${e.expectedSha256}, reçu=${e.actualSha256}');
} on HttpException catch (e) {
// Le serveur a répondu avec un code HTTP != 200
debugPrint('Erreur réseau téléchargement asset : $e');
}

Comportement détaillé de getOrDownloadAsset​

  1. Vérification du cache existant :
    • Si targetFile.existsSync() est vrai, getOrDownloadAsset relit les octets du fichier local et recalcule son condensat sha256.
    • Si le condensat correspond à expectedSha256, le fichier est retourné immédiatement (0 appel réseau).
    • Si le fichier en cache a été altéré sur le disque (cachedSha != expected), il est immédiatement supprimé (targetFile.deleteSync()) et re-téléchargé.
  2. Téléchargement HTTP :
    • Effectue un GET sur Uri.parse(descriptor.downloadUrl) avec les headers optionnels.
    • Si response.statusCode != 200, lève une HttpException contenant le code HTTP et l'URI.
  3. Validation cryptographique SHA-256 :
    • Calcule sha256.convert(response.bodyBytes).
    • Si actualSha != expected, lève une OtaAssetIntegrityException avant toute écriture sur le disque.
  4. Persistance atomique :
    • Crée l'arborescence parente (targetFile.parent.createSync(recursive: true)), écrit les octets vérifiés et retourne le File.

3. Garde-fou de cohérence des assets embarqués (computeAssetsManifestHash)​

En complément de OtaAssetManager, la CLI tk-ota-push calcule lors de chaque release et patch l'empreinte canonique SHA-256 de tous les fichiers déclarés dans la section flutter.assets de votre pubspec.yaml via FlutterProject.computeAssetsManifestHash() (lib/src/cli/project.dart).

Ce hash est transmis au serveur dans le champ assetsManifestHash (assets_manifest_hash) pour tracer toute modification des assets embarqués entre une Release Store et ses Patchs.