Android Backup Service permet de sauvegarder et de restaurer dans le cloud des données clé-valeur issues de votre application Android. Lors d'une opération de sauvegarde de clé-valeur, les données de sauvegarde de l'application sont transmises au service de transfert des sauvegardes de l'appareil. Si l'appareil utilise par défaut le service de transfert des sauvegardes de Google, les données sont transmises à Android Backup Service pour archivage.
Les données sont limitées à 5 Mo par utilisateur de votre application. Le stockage des données de sauvegarde se fait sans frais.
Pour obtenir une présentation des options de sauvegarde d'Android et des conseils sur les données à sauvegarder et à restaurer, consultez la page Présentation de la sauvegarde de données.
Implémenter la sauvegarde clé-valeur
Pour sauvegarder les données de votre application, vous devez implémenter un agent de sauvegarde. Votre agent de sauvegarde est appelé par le gestionnaire de sauvegarde lors des sauvegardes et des restaurations.
Pour implémenter un agent de sauvegarde, vous devez prendre les mesures suivantes :
Déclarez votre agent de sauvegarde dans votre fichier manifeste avec l'attribut
android:backupAgent
.Définissez un agent de sauvegarde en effectuant l'une des opérations suivantes :
-
La classe
BackupAgent
fournit l'interface centrale que votre application utilise pour communiquer avec le gestionnaire de sauvegarde. Si vous étendez cette classe directement, vous devez remplaceronBackup()
etonRestore()
pour gérer les opérations de sauvegarde et de restauration de vos données. Étendre la classe
BackupAgentHelper
La classe
BackupAgentHelper
fournit un wrapper pratique pour la classeBackupAgent
, ce qui réduit la quantité de code à écrire. Dans votre classeBackupAgentHelper
, vous devez utiliser un ou plusieurs objets d'assistance, qui sauvegardent et restaurent automatiquement certains types de données, afin que vous n'ayez pas besoin d'implémenteronBackup()
etonRestore()
À moins que vous n'ayez besoin d'avoir un contrôle total sur les sauvegardes de votre application, nous vous recommandons d'utiliserBackupAgentHelper
pour gérer les sauvegardes de votre application.Android fournit actuellement des assistants de sauvegarde qui sauvegardent et restaurent des fichiers complets depuis
SharedPreferences
et la mémoire de stockage interne.
-
Déclarer l'agent de sauvegarde dans votre fichier manifeste
Une fois que vous avez choisi le nom de classe de votre agent de sauvegarde, déclarez-le dans votre fichier manifeste à l'aide de l'attribut android:backupAgent
dans la balise <application>
.
Par exemple :
<manifest ... > ... <application android:label="MyApplication" android:backupAgent="MyBackupAgent"> <meta-data android:name="com.google.android.backup.api_key" android:value="unused" /> <activity ... > ... </activity> </application> </manifest>
Pour les anciens appareils, nous vous recommandons d'ajouter la clé API <meta-data>
à votre fichier manifeste Android. Android Backup Service ne nécessite plus de clé de service, mais certains appareils plus anciens peuvent toujours rechercher une clé lors de la sauvegarde. Définissez android:name
sur com.google.android.backup.api_key
et android:value
sur unused
.
À l'aide d'une valeur booléenne, l'attribut android:restoreAnyVersion
vous permet d'indiquer si vous souhaitez restaurer les données de l'application, quelle que soit la version de l'application actuelle, ou si vous souhaitez que la restauration se fasse sur la version à l'origine des données de sauvegarde. La valeur par défaut est false
. Pour en savoir plus, consultez la section Vérifier la version des données de restauration.
Étendre la classe BackupAgentHelper
Si vous souhaitez sauvegarder des fichiers complets à partir de SharedPreferences
ou de la mémoire de stockage interne, vous devez compiler votre agent de sauvegarde à l'aide de BackupAgentHelper
.
La compilation de votre agent de sauvegarde avec BackupAgentHelper
nécessite beaucoup moins de code que d'étendre la classe BackupAgent
, car vous n'avez pas besoin d'implémenter onBackup()
et onRestore()
.
Votre implémentation de BackupAgentHelper
doit utiliser un ou plusieurs assistants de sauvegarde.
Un assistant de sauvegarde est un composant spécialisé que BackupAgentHelper
appelle pour effectuer des opérations de sauvegarde et de restauration d'un type de données particulier. Le framework Android fournit actuellement deux assistants différents :
SharedPreferencesBackupHelper
pour sauvegarder les fichiersSharedPreferences
.FileBackupHelper
pour sauvegarder les fichiers de la mémoire de stockage interne.
Vous pouvez inclure plusieurs assistants dans votre classe BackupAgentHelper
, mais un seul assistant est nécessaire par type de données. Autrement dit, si vous avez plusieurs fichiers SharedPreferences
, vous n'avez besoin que d'un seul fichier SharedPreferencesBackupHelper
.
Pour chaque assistant que vous souhaitez ajouter à votre classe BackupAgentHelper
, vous devez procéder comme suit dans la méthode onCreate()
:
- Instanciez une instance de la classe d'assistance souhaitée. Dans le constructeur de classe, vous devez spécifier le ou les fichiers que vous souhaitez sauvegarder.
- Appelez
addHelper()
pour ajouter l'assistant à votre classeBackupAgentHelper
.
Les sections suivantes décrivent comment créer un agent de sauvegarde à l'aide de chacun des assistants disponibles.
Sauvegarder SharedPreferences
Lorsque vous instanciez une classe SharedPreferencesBackupHelper
, vous devez inclure le nom d'un ou de plusieurs fichiers SharedPreferences
.
Par exemple, pour sauvegarder un fichier SharedPreferences
nommé user_preferences
, un agent de sauvegarde complet utilisant BackupAgentHelper
se présente comme suit :
Kotlin
// The name of the SharedPreferences file const val PREFS = "user_preferences" // A key to uniquely identify the set of backup data const val PREFS_BACKUP_KEY = "prefs" class MyPrefsBackupAgent : BackupAgentHelper() { override fun onCreate() { // Allocate a helper and add it to the backup agent SharedPreferencesBackupHelper(this, PREFS).also { addHelper(PREFS_BACKUP_KEY, it) } } }
Java
public class MyPrefsBackupAgent extends BackupAgentHelper { // The name of the SharedPreferences file static final String PREFS = "user_preferences"; // A key to uniquely identify the set of backup data static final String PREFS_BACKUP_KEY = "prefs"; // Allocate a helper and add it to the backup agent @Override public void onCreate() { SharedPreferencesBackupHelper helper = new SharedPreferencesBackupHelper(this, PREFS); addHelper(PREFS_BACKUP_KEY, helper); } }
La classe SharedPreferencesBackupHelper
inclut tout le code nécessaire à la sauvegarde et à la restauration d'un fichier SharedPreferences
.
Lorsque le gestionnaire de sauvegarde appelle onBackup()
et onRestore()
, la classe BackupAgentHelper
appelle vos assistants de sauvegarde pour sauvegarder et restaurer les fichiers spécifiés.
Sauvegarder d'autres fichiers
Lorsque vous instanciez un FileBackupHelper
, vous devez inclure le nom d'un ou de plusieurs fichiers enregistrés dans la mémoire de stockage interne de votre application, comme spécifié par getFilesDir()
, qui correspond au lieu où openFileOutput()
écrit des fichiers.
Par exemple, pour sauvegarder deux fichiers nommés scores
et stats
, un agent de sauvegarde utilisant BackupAgentHelper
se présente comme suit :
Kotlin
// The name of the file const val TOP_SCORES = "scores" const val PLAYER_STATS = "stats" // A key to uniquely identify the set of backup data const val FILES_BACKUP_KEY = "myfiles" class MyFileBackupAgent : BackupAgentHelper() { override fun onCreate() { // Allocate a helper and add it to the backup agent FileBackupHelper(this, TOP_SCORES, PLAYER_STATS).also { addHelper(FILES_BACKUP_KEY, it) } } }
Java
public class MyFileBackupAgent extends BackupAgentHelper { // The name of the file static final String TOP_SCORES = "scores"; static final String PLAYER_STATS = "stats"; // A key to uniquely identify the set of backup data static final String FILES_BACKUP_KEY = "myfiles"; // Allocate a helper and add it to the backup agent @Override public void onCreate() { FileBackupHelper helper = new FileBackupHelper(this, TOP_SCORES, PLAYER_STATS); addHelper(FILES_BACKUP_KEY, helper); } }
La classe FileBackupHelper
inclut tout le code nécessaire pour sauvegarder et restaurer les fichiers enregistrés dans la mémoire de stockage interne de votre application.
Cependant, la lecture et l'écriture dans des fichiers de la mémoire de stockage interne ne sont pas thread-safe. Pour vous assurer que votre agent de sauvegarde ne lit ou n'écrit pas vos fichiers en même temps que vos activités, vous devez utiliser des instructions synchronisées chaque fois que vous effectuez une lecture ou une écriture. Par exemple, dans toute activité de lecture et d'écriture du fichier, vous avez besoin d'un objet à utiliser comme verrou intrinsèque pour les instructions synchronisées :
Kotlin
// Object for intrinsic lock companion object { val sDataLock = Any() }
Java
// Object for intrinsic lock static final Object sDataLock = new Object();
Ensuite, créez une instruction synchronisée avec ce verrou chaque fois que vous lisez ou écrivez des fichiers. Par exemple, voici une déclaration synchronisée pour écrire le dernier score d'un jeu dans un fichier :
Kotlin
try { synchronized(MyActivity.sDataLock) { val dataFile = File(filesDir, TOP_SCORES) RandomAccessFile(dataFile, "rw").apply { writeInt(score) } } } catch (e: IOException) { Log.e(TAG, "Unable to write to file") }
Java
try { synchronized (MyActivity.sDataLock) { File dataFile = new File(getFilesDir(), TOP_SCORES); RandomAccessFile raFile = new RandomAccessFile(dataFile, "rw"); raFile.writeInt(score); } } catch (IOException e) { Log.e(TAG, "Unable to write to file"); }
Vous devez synchroniser vos instructions de lecture avec le même verrou.
Ensuite, dans votre classe BackupAgentHelper
, vous devez remplacer onBackup()
et onRestore()
pour synchroniser les opérations de sauvegarde et de restauration avec le même verrou intrinsèque. Par exemple, l'exemple MyFileBackupAgent
ci-dessus nécessite les méthodes suivantes :
Kotlin
@Throws(IOException::class) override fun onBackup( oldState: ParcelFileDescriptor, data: BackupDataOutput, newState: ParcelFileDescriptor ) { // Hold the lock while the FileBackupHelper performs back up synchronized(MyActivity.sDataLock) { super.onBackup(oldState, data, newState) } } @Throws(IOException::class) override fun onRestore( data: BackupDataInput, appVersionCode: Int, newState: ParcelFileDescriptor ) { // Hold the lock while the FileBackupHelper restores the file synchronized(MyActivity.sDataLock) { super.onRestore(data, appVersionCode, newState) } }
Java
@Override public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data, ParcelFileDescriptor newState) throws IOException { // Hold the lock while the FileBackupHelper performs back up synchronized (MyActivity.sDataLock) { super.onBackup(oldState, data, newState); } } @Override public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState) throws IOException { // Hold the lock while the FileBackupHelper restores the file synchronized (MyActivity.sDataLock) { super.onRestore(data, appVersionCode, newState); } }
Étendre la classe BackupAgent
La plupart des applications ne devraient pas avoir besoin d'étendre directement la classe BackupAgent
, mais devraient plutôt étendre la classe BackupAgentHelper
pour bénéficier des classes d'assistance intégrées qui sauvegardent et restaurent automatiquement vos fichiers.
Toutefois, vous pourriez étendre directement la classe BackupAgent
pour effectuer les opérations suivantes :
- Gérer la version du format de vos données. Par exemple, si vous pensez avoir besoin de réviser le format dans lequel vous écrivez les données de votre application, vous pouvez créer un agent de sauvegarde pour vérifier la version de votre application lors d'une opération de restauration et effectuer toutes les opérations de compatibilité nécessaires si la version de l'appareil est différente de celle des données de sauvegarde. Pour en savoir plus, consultez la section Vérifier la version des données de restauration.
- Spécifier les portions de données à sauvegarder. Au lieu de sauvegarder un fichier entier, vous pouvez spécifier les portions de données à sauvegarder et la manière dont chaque portion est ensuite restaurée sur l'appareil. Cela peut également vous aider à gérer différentes versions, car vous lisez et écrivez vos données en tant qu'entités uniques plutôt qu'en tant que fichiers complets.
- Sauvegarder des données dans une base de données. Si vous disposez d'une base de données SQLite que vous souhaitez restaurer lorsque l'utilisateur réinstalle votre application, vous devez créer un fichier
BackupAgent
personnalisé qui lit les données appropriées lors d'une opération de sauvegarde, puis créer votre table et insérer les données lors d'une opération de restauration.
Si vous n'avez pas besoin d'effectuer les tâches ci-dessus et que vous souhaitez sauvegarder des fichiers complets à partir de SharedPreferences
ou de la mémoire de stockage interne, consultez la section Étendre la classe BackupAgentHelper
.
Méthodes requises
Lorsque vous créez une classe BackupAgent
, vous devez implémenter les méthodes de rappel suivantes :
onBackup()
- Le gestionnaire de sauvegarde appelle cette méthode lorsque vous demandez une sauvegarde. Avec cette méthode, vous lisez les données de l'application à partir de l'appareil et transmettez les données que vous souhaitez sauvegarder au gestionnaire de sauvegarde, comme décrit dans la section Effectuer une sauvegarde .
onRestore()
Le gestionnaire de sauvegarde appelle cette méthode lors d'une opération de restauration. Cette méthode fournit vos données de sauvegarde, que votre application peut utiliser pour restaurer son état précédent, comme décrit dans la section Effectuer une restauration.
Le système appelle cette méthode pour restaurer toutes les données de sauvegarde lorsque l'utilisateur réinstalle votre application, mais votre application peut également demander une restauration.
Effectuer une sauvegarde
Une requête de sauvegarde n'appelle pas immédiatement votre méthode onBackup()
. À la place, le gestionnaire de sauvegarde attend une heure appropriée, puis effectue une sauvegarde pour toutes les applications qui ont demandé une sauvegarde depuis la dernière sauvegarde. C'est à ce stade que vous devez fournir les données de votre application au gestionnaire de sauvegarde afin qu'elles puissent être enregistrées dans le stockage cloud.
Seul le gestionnaire de sauvegarde peut appeler la méthode onBackup()
de votre agent de sauvegarde. Chaque fois que les données de votre application changent et que vous souhaitez effectuer une sauvegarde, vous devez demander une opération de sauvegarde en appelant dataChanged()
.
Pour en savoir plus, consultez Demander une sauvegarde.
Conseil : Lors du développement de votre application, vous pouvez lancer une opération de sauvegarde immédiate à partir du gestionnaire de sauvegarde à l'aide de l'outil bmgr
.
Lorsque le gestionnaire de sauvegarde appelle votre méthode onBackup()
, il transmet trois paramètres :
oldState
Un ParcelFileDescriptor
ouvert et en lecture seule pointant vers le dernier état de sauvegarde fourni par votre application. Il ne s'agit pas des données de sauvegarde issues du stockage cloud, mais d'une représentation locale des données sauvegardées lors du dernier appel deonBackup()
, comme défini parnewState
ou à partir deonRestore()
.onRestore()
est abordé dans la section suivante. Étant donné queonBackup()
ne vous permet pas de lire les données de sauvegarde existantes dans le stockage cloud, vous pouvez utiliser cette représentation locale pour déterminer si vos données ont changé depuis la dernière sauvegarde.data
- Un objet
BackupDataOutput
que vous utilisez pour transmettre vos données de sauvegarde au gestionnaire de sauvegarde. newState
Un ParcelFileDescriptor
ouvert et de lecture/écriture pointant vers un fichier dans lequel vous devez écrire une représentation des données que vous avez fournies àdata
. Cette représentation peut simplement être la date et l'heure de la dernière modification de votre fichier. Cet objet est renvoyé en tant queoldState
la prochaine fois que le gestionnaire de sauvegarde appelle votre méthodeonBackup()
. Si vous n'écrivez pas vos données de sauvegarde dansnewState
,oldState
pointera vers un fichier vide la prochaine fois que le gestionnaire de sauvegarde appelleraonBackup()
.
À l'aide de ces paramètres, implémentez votre méthode onBackup()
pour effectuer les opérations suivantes :
Vérifiez si vos données ont changé depuis la dernière sauvegarde en comparant
oldState
à vos données actuelles. La manière dont vous lisez les données dansoldState
dépend de la manière dont vous les avez écrites dansnewState
(voir l'étape 3). La méthode la plus simple pour enregistrer l'état d'un fichier consiste à utiliser la date et l'heure de sa dernière modification. Par exemple, voici comment lire et comparer une date et une heure à partir deoldState
:Kotlin
val instream = FileInputStream(oldState.fileDescriptor) val dataInputStream = DataInputStream(instream) try { // Get the last modified timestamp from the state file and data file val stateModified = dataInputStream.readLong() val fileModified: Long = dataFile.lastModified() if (stateModified != fileModified) { // The file has been modified, so do a backup // Or the time on the device changed, so be safe and do a backup } else { // Don't back up because the file hasn't changed return } } catch (e: IOException) { // Unable to read state file... be safe and do a backup }
Java
// Get the oldState input stream FileInputStream instream = new FileInputStream(oldState.getFileDescriptor()); DataInputStream in = new DataInputStream(instream); try { // Get the last modified timestamp from the state file and data file long stateModified = in.readLong(); long fileModified = dataFile.lastModified(); if (stateModified != fileModified) { // The file has been modified, so do a backup // Or the time on the device changed, so be safe and do a backup } else { // Don't back up because the file hasn't changed return; } } catch (IOException e) { // Unable to read state file... be safe and do a backup }
Si rien n'a changé et que vous n'avez pas besoin d'effectuer de sauvegarde, passez à l'étape 3.
Si vos données ont changé par rapport à
oldState
, écrivez les données actuelles dansdata
pour les sauvegarder dans le stockage cloud.Vous devez écrire chaque fragment de données en tant qu'entité dans
BackupDataOutput
. Une entité est un enregistrement de données binaires aplati qui est identifié par une chaîne de clé unique. Ainsi, conceptuellement, l'ensemble de données que vous sauvegardez est un ensemble de paires clé-valeur.Pour ajouter une entité à votre ensemble de données de sauvegarde, procédez comme suit :
Appelez
writeEntityHeader()
en transmettant une clé de chaîne unique pour les données que vous allez écrire et la taille des données.Appelez
writeEntityData()
en transmettant un tampon d'octets contenant vos données et le nombre d'octets à écrire à partir du tampon. La taille doit correspondre à la taille transmise àwriteEntityHeader()
.
Par exemple, le code suivant aplatit certaines données dans un flux d'octets et les écrit dans une seule entité :
Kotlin
val buffer: ByteArray = ByteArrayOutputStream().run { DataOutputStream(this).apply { writeInt(playerName) writeInt(playerScore) } toByteArray() } val len: Int = buffer.size data.apply { writeEntityHeader(TOPSCORE_BACKUP_KEY, len) writeEntityData(buffer, len) }
Java
// Create buffer stream and data output stream for our data ByteArrayOutputStream bufStream = new ByteArrayOutputStream(); DataOutputStream outWriter = new DataOutputStream(bufStream); // Write structured data outWriter.writeUTF(playerName); outWriter.writeInt(playerScore); // Send the data to the Backup Manager via the BackupDataOutput byte[] buffer = bufStream.toByteArray(); int len = buffer.length; data.writeEntityHeader(TOPSCORE_BACKUP_KEY, len); data.writeEntityData(buffer, len);
Procédez ainsi pour chaque donnée à sauvegarder. C'est vous qui déterminez la répartition de vos données en entités. Vous pouvez même n'utiliser qu'une seule entité.
Que vous effectuiez ou non une sauvegarde (à l'étape 2), écrivez une représentation des données actuelles dans le
ParcelFileDescriptor
newState
. Le gestionnaire de sauvegarde conserve cet objet localement en tant que représentation des données actuellement sauvegardées. Il vous renvoie cette valeur en tant queoldState
la prochaine fois qu'il appelleonBackup()
. Vous pouvez ainsi déterminer si une autre sauvegarde est nécessaire, comme traité à l'étape 1. Si vous n'écrivez pas l'état des données actuel dans ce fichier,oldState
sera vide lors du prochain rappel.L'exemple suivant enregistre une représentation des données actuelles dans
newState
à l'aide de la date et de l'heure de dernière modification du fichier :Kotlin
val modified = dataFile.lastModified() FileOutputStream(newState.fileDescriptor).also { DataOutputStream(it).apply { writeLong(modified) } }
Java
FileOutputStream outstream = new FileOutputStream(newState.getFileDescriptor()); DataOutputStream out = new DataOutputStream(outstream); long modified = dataFile.lastModified(); out.writeLong(modified);
Effectuer une restauration
Au moment de restaurer les données de votre application, le gestionnaire de sauvegarde appelle la méthode onRestore()
de votre agent de sauvegarde. Lorsque cette méthode est appelée, le gestionnaire de sauvegarde transmet vos données de sauvegarde afin que vous puissiez les restaurer sur l'appareil.
Seul le gestionnaire de sauvegarde peut appeler onRestore()
, ce qui se produit automatiquement lorsque le système installe votre application et trouve des données de sauvegarde existantes.
Lorsque le gestionnaire de sauvegarde appelle votre méthode onRestore()
, il transmet trois paramètres :
data
- Un objet
BackupDataInput
qui vous permet de lire vos données de sauvegarde. appVersionCode
- Un entier représentant la valeur de l'attribut de manifeste
android:versionCode
de votre application au moment où ces données ont été sauvegardées. Vous pouvez l'utiliser pour vérifier la version actuelle de l'application et déterminer si le format de données est compatible. Pour en savoir plus sur la manière de l'utiliser pour gérer différentes versions des données de restauration, consultez la section Vérifier la version des données de restauration. newState
- Un
ParcelFileDescriptor
ouvert et de lecture/écriture pointant vers un fichier dans lequel vous devez écrire l'état de sauvegarde final fourni avecdata
. Cet objet est renvoyé en tant queoldState
la prochaine fois queonBackup()
est appelé. Rappelez-vous que vous devez également écrire le même objetnewState
dans le rappelonBackup()
. Cela garantit ainsi que l'objetoldState
attribué àonBackup()
est valide, même la première fois queonBackup()
est appelé après la restauration de l'appareil.
Dans votre implémentation de onRestore()
, vous devez appeler readNextHeader()
sur data
pour itérer toutes les entités de l'ensemble de données. Pour chaque entité trouvée, procédez comme suit :
- Obtenez la clé de l'entité à l'aide de
getKey()
. Comparez la clé de l'entité à une liste de valeurs de clé connues que vous auriez déclarées en tant que chaînes finales statiques dans votre classe
BackupAgent
. Lorsque la clé correspond à l'une de vos chaînes de clés connues, saisissez-la dans une instruction pour extraire les données de l'entité et les enregistrer sur l'appareil :- Obtenez la taille des données de l'entité avec
getDataSize()
, puis créez un tableau d'octets de cette taille. - Appelez
readEntityData()
et transmettez-lui le tableau d'octets, c'est-à-dire l'emplacement des données. Spécifiez alors le décalage de début, ainsi que la taille à lire. - Votre tableau d'octets est maintenant plein. Lisez les données et écrivez-les sur l'appareil comme vous le souhaitez.
- Obtenez la taille des données de l'entité avec
Après avoir lu et réécrit vos données sur l'appareil, écrivez l'état de vos données dans le paramètre
newState
de la même manière que lors de l'exécution deonBackup()
.
Voici par exemple comment restaurer les données sauvegardées dans l'exemple de la section précédente :
Kotlin
@Throws(IOException::class) override fun onRestore(data: BackupDataInput, appVersionCode: Int, newState: ParcelFileDescriptor) { with(data) { // There should be only one entity, but the safest // way to consume it is using a while loop while (readNextHeader()) { when(key) { TOPSCORE_BACKUP_KEY -> { val dataBuf = ByteArray(dataSize).also { readEntityData(it, 0, dataSize) } ByteArrayInputStream(dataBuf).also { DataInputStream(it).apply { // Read the player name and score from the backup data playerName = readUTF() playerScore = readInt() } // Record the score on the device (to a file or something) recordScore(playerName, playerScore) } } else -> skipEntityData() } } } // Finally, write to the state blob (newState) that describes the restored data FileOutputStream(newState.fileDescriptor).also { DataOutputStream(it).apply { writeUTF(playerName) writeInt(mPlayerScore) } } }
Java
@Override public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState) throws IOException { // There should be only one entity, but the safest // way to consume it is using a while loop while (data.readNextHeader()) { String key = data.getKey(); int dataSize = data.getDataSize(); // If the key is ours (for saving top score). Note this key was used when // we wrote the backup entity header if (TOPSCORE_BACKUP_KEY.equals(key)) { // Create an input stream for the BackupDataInput byte[] dataBuf = new byte[dataSize]; data.readEntityData(dataBuf, 0, dataSize); ByteArrayInputStream baStream = new ByteArrayInputStream(dataBuf); DataInputStream in = new DataInputStream(baStream); // Read the player name and score from the backup data playerName = in.readUTF(); playerScore = in.readInt(); // Record the score on the device (to a file or something) recordScore(playerName, playerScore); } else { // We don't know this entity key. Skip it. (Shouldn't happen.) data.skipEntityData(); } } // Finally, write to the state blob (newState) that describes the restored data FileOutputStream outstream = new FileOutputStream(newState.getFileDescriptor()); DataOutputStream out = new DataOutputStream(outstream); out.writeUTF(playerName); out.writeInt(mPlayerScore); }
Dans cet exemple, le paramètre appVersionCode
transmis à onRestore()
n'est pas utilisé. Toutefois, il peut être utile de l'utiliser si vous avez choisi d'effectuer une sauvegarde alors que la version de l'application de l'utilisateur a été rétrogradée (par exemple, si l'utilisateur est passé de la version 1.5 à la version 1.0 de votre application). Pour en savoir plus, consultez la section suivante.
Vérifier la version des données de restauration
Lorsque le gestionnaire de sauvegarde enregistre vos données dans le stockage cloud, il inclut automatiquement la version de votre application, telle que définie par l'attribut android:versionCode
de votre fichier manifeste. Avant que le gestionnaire de sauvegarde n'appelle votre agent de sauvegarde pour restaurer vos données, il compare l'attribut android:versionCode
de l'application installée à la valeur enregistrée dans l'ensemble de données de restauration. Si la version enregistrée dans l'ensemble de données de restauration est plus récente que celle de l'application sur l'appareil, cela signifie que l'utilisateur est revenu à une version antérieure. Dans ce cas, le gestionnaire de sauvegarde annule l'opération de restauration de votre application et n'appelle pas la méthode onRestore()
, car il considère que l'ensemble de restauration n'a pas de sens pour l'ancienne version.
Vous pouvez ignorer ce comportement avec l'attribut android:restoreAnyVersion
.
Définissez cet attribut sur true
pour indiquer que vous souhaitez restaurer l'application, quelle que soit la version de l'ensemble de restauration. La valeur par défaut est false
. Si vous définissez ce paramètre sur true
, le gestionnaire de sauvegarde ignore android:versionCode
et appelle votre méthode onRestore()
dans tous les cas. En procédant ainsi, vous pouvez vérifier manuellement la différence de version dans votre méthode onRestore()
et prendre les mesures nécessaires pour rendre les données compatibles si les versions ne correspondent pas.
Pour vous aider à gérer différentes versions lors d'une opération de restauration, la méthode onRestore()
vous transmet le code de version inclus avec l'ensemble de données de restauration défini en tant que paramètre appVersionCode
. Vous pouvez ensuite interroger le code de version actuel de l'application à l'aide du champ PackageInfo.versionCode
. Par exemple :
Kotlin
val info: PackageInfo? = try { packageManager.getPackageInfo(packageName, 0) } catch (e: PackageManager.NameNotFoundException) { null } val version: Int = info?.versionCode ?: 0
Java
PackageInfo info; try { String name = getPackageName(); info = getPackageManager().getPackageInfo(name, 0); } catch (NameNotFoundException nnfe) { info = null; } int version; if (info != null) { version = info.versionCode; }
Comparez ensuite la version
acquise à partir de PackageInfo
avec le appVersionCode
transmis dans onRestore()
.
Demander une sauvegarde
Vous pouvez demander une opération de sauvegarde à tout moment en appelant dataChanged()
. Cette méthode informe le gestionnaire de sauvegarde que vous souhaitez sauvegarder vos données à l'aide de votre agent de sauvegarde. Le gestionnaire de sauvegarde appellera alors la méthode onBackup()
de votre agent de sauvegarde ultérieurement. En règle générale, vous devez demander une sauvegarde chaque fois que vos données changent (par exemple, lorsque l'utilisateur modifie une préférence d'application que vous souhaitez sauvegarder). Si vous appelez dataChanged()
plusieurs fois avant que le gestionnaire de sauvegarde ne demande une sauvegarde à votre agent, votre agent ne reçoit quand même qu'un seul appel à onBackup()
.
Demander une restauration
Au cours du cycle de vie normal de votre application, vous ne devriez pas avoir à demander d'opération de restauration. Le système recherche automatiquement les données de sauvegarde et effectue une restauration lorsque votre application est installée.
Effectuer une migration vers la sauvegarde automatique
Vous pouvez effectuer la transition de votre application vers des sauvegardes complètes de données en définissant android:fullBackupOnly
sur true
dans l'élément <application>
du fichier manifeste. Lorsque vous exécutez l'application sur un appareil équipé d'Android 5.1 (niveau d'API 22) ou version antérieure, votre application ignore cette valeur dans le fichier manifeste et continue à effectuer des sauvegardes clé-valeur. Sur un appareil équipé d'Android 6.0 (niveau d'API 23) ou version ultérieure, votre application effectue une sauvegarde automatique au lieu de la sauvegarde clé-valeur.
Protection de la vie privée des utilisateurs
Chez Google, nous sommes pleinement conscients de la confiance que les utilisateurs nous accordent et de notre responsabilité quant à la protection de leur vie privée. Google transmet de manière sécurisée les données de sauvegarde vers et depuis les serveurs Google afin d'offrir des fonctionnalités de sauvegarde et de restauration. Google traite ces données comme des informations personnelles, conformément à ses Règles de confidentialité.
Par ailleurs, les utilisateurs peuvent désactiver la fonctionnalité de sauvegarde des données via les paramètres de sauvegarde du système Android. Lorsqu'un utilisateur désactive la sauvegarde, Android Backup Service supprime toutes les données de sauvegarde enregistrées. Un utilisateur peut réactiver la sauvegarde sur l'appareil, mais Android Backup Service ne restaurera aucune donnée précédemment supprimée.