Schlüssel/Wert-Paare mit dem Android Backup Service sichern

Der Android Backup Service bietet die Möglichkeit zum Sichern und Wiederherstellen von Schlüssel/Wert-Paar-Daten in Ihrer Android-App über den Cloud-Speicher. Während eines Sicherungsvorgangs für Schlüssel/Wert-Paare werden die Sicherungsdaten der App an das Sicherungstransport des Geräts übergeben. Wenn das Gerät den standardmäßigen Google-Sicherungstransport verwendet, werden die Daten zur Archivierung an den Android Backup Service übergeben.

Die Datenmenge ist auf 5 MB pro Nutzer Ihrer App beschränkt. Für das Speichern von Sicherungsdaten fallen keine Kosten an.

Eine Übersicht über die Sicherungsoptionen von Android und eine Anleitung dazu, welche Daten gesichert und wiederhergestellt werden sollten, finden Sie in der Übersicht zur Datensicherung.

Schlüssel/Wert-Paar-Sicherung implementieren

Zum Sichern Ihrer Anwendungsdaten müssen Sie einen Ersatz-Agent implementieren. Der Backup-Agent wird vom Sicherungsmanager sowohl während der Sicherung als auch während der Wiederherstellung aufgerufen.

So implementieren Sie einen Ersatz-Agent:

  1. Deklarieren Sie Ihren Ersatz-Agent in der Manifestdatei mit dem Attribut android:backupAgent.

  2. Definieren Sie einen Ersatz-Agent, indem Sie einen der folgenden Schritte ausführen:

    • BackupAgent wird verlängert

      Die Klasse BackupAgent stellt die zentrale Schnittstelle bereit, über die Ihre Anwendung mit dem Sicherungsmanager kommuniziert. Wenn Sie diese Klasse direkt erweitern, müssen Sie onBackup() und onRestore() überschreiben, um die Sicherungs- und Wiederherstellungsvorgänge für Ihre Daten zu verarbeiten.

    • BackupAgentHelper wird verlängert

      Die Klasse BackupAgentHelper bietet einen praktischen Wrapper um die Klasse BackupAgent und minimiert so die Menge an Code, den Sie schreiben müssen. In Ihrer BackupAgentHelper müssen Sie ein oder mehrere Hilfsobjekte verwenden, die bestimmte Datentypen automatisch sichern und wiederherstellen, sodass Sie onBackup() und onRestore() nicht implementieren müssen. Wenn Sie keine vollständige Kontrolle über die Sicherungen Ihrer Anwendung benötigen, empfehlen wir die Verwendung von BackupAgentHelper für die Sicherungen Ihrer Anwendung.

      Android bietet derzeit Sicherungshelfer, mit denen vollständige Dateien aus SharedPreferences und dem internen Speicher gesichert und wiederhergestellt werden können.

Ersatz-Agent im Manifest deklarieren

Wenn Sie den Klassennamen für den Back-up-Agent ausgewählt haben, deklarieren Sie ihn in Ihrem Manifest mit dem Attribut android:backupAgent im Tag <application>.

Beispiele:

<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>

Zur Unterstützung älterer Geräte empfehlen wir, den API-Schlüssel <meta-data> in die Android-Manifestdatei aufzunehmen. Für den Android Backup Service ist kein Dienstschlüssel mehr erforderlich. Einige ältere Geräte suchen jedoch möglicherweise während der Sicherung nach einem Schlüssel. Setzen Sie android:name auf com.google.android.backup.api_key und android:value auf unused.

Das Attribut android:restoreAnyVersion verwendet einen booleschen Wert, um anzugeben, ob Sie die Anwendungsdaten unabhängig von der aktuellen App-Version und der Version, aus der die Sicherungsdaten erstellt wurden, wiederherstellen möchten. Der Standardwert ist false. Weitere Informationen finden Sie unter Version der Datenwiederherstellung prüfen.

BackupAgentHelper erweitern

Erstellen Sie den Sicherungs-Agent mit BackupAgentHelper, wenn Sie vollständige Dateien entweder aus SharedPreferences oder dem internen Speicher sichern möchten. Für das Erstellen des Back-up-Agents mit BackupAgentHelper ist weit weniger Code erforderlich als zum Erweitern von BackupAgent, da onBackup() und onRestore() nicht implementiert werden müssen.

Ihre Implementierung von BackupAgentHelper muss mindestens eine Sicherungshilfe verwenden. Ein Back-up-Hilfsprogramm ist eine spezialisierte Komponente, die von BackupAgentHelper zur Durchführung von Sicherungs- und Wiederherstellungsvorgängen für einen bestimmten Datentyp aufgerufen wird. Das Android-Framework bietet derzeit zwei verschiedene Hilfsprogramme:

Sie können mehrere Helper in BackupAgentHelper aufnehmen, aber für jeden Datentyp wird nur ein Helper benötigt. Wenn Sie also mehrere SharedPreferences-Dateien haben, benötigen Sie nur eine SharedPreferencesBackupHelper.

Führen Sie für jeden Helper, den Sie zu BackupAgentHelper hinzufügen möchten, während der Methode onCreate() die folgenden Schritte aus:

  1. Instanziieren Sie eine Instanz der gewünschten Hilfsklasse. Im Klassenkonstruktor müssen Sie die Datei(en) angeben, die Sie sichern möchten.
  2. Rufen Sie addHelper() auf, um das Hilfsprogramm zu BackupAgentHelper hinzuzufügen.

In den folgenden Abschnitten wird beschrieben, wie Sie mit jedem der verfügbaren Hilfsprogramme einen Sicherungs-Agent erstellen.

Gemeinsame Einstellungen sichern

Wenn Sie eine SharedPreferencesBackupHelper instanziieren, müssen Sie den Namen einer oder mehrerer SharedPreferences-Dateien angeben.

Beispiel: Zur Sicherung einer SharedPreferences-Datei namens user_preferences sieht ein vollständiger Back-up-Agent mit BackupAgentHelper so aus:

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);
    }
}

Das SharedPreferencesBackupHelper enthält den gesamten Code, der zum Sichern und Wiederherstellen einer SharedPreferences-Datei erforderlich ist.

Wenn der Sicherungsmanager onBackup() und onRestore() aufruft, ruft BackupAgentHelper Ihre Sicherungshelfer auf, um die angegebenen Dateien zu sichern und wiederherzustellen.

Andere Dateien sichern

Wenn Sie eine FileBackupHelper instanziieren, müssen Sie den Namen einer oder mehrerer Dateien angeben, die im internen Speicher der Anwendung gespeichert sind, wie in getFilesDir() angegeben. Dies ist der Ort, an dem openFileOutput() Dateien schreibt.

Wenn Sie beispielsweise zwei Dateien mit den Namen scores und stats sichern möchten, sieht ein Back-up-Agent mit BackupAgentHelper so aus:

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);
    }
}

Der FileBackupHelper enthält den gesamten Code, der zum Sichern und Wiederherstellen von Dateien erforderlich ist, die im internen Speicher der Anwendung gespeichert sind.

Das Lesen und Schreiben in Dateien im internen Speicher ist jedoch nicht Thread-sicher. Damit der Backup-Agent Ihre Dateien nicht gleichzeitig mit Ihren Aktivitäten liest oder schreibt, müssen Sie jedes Mal, wenn Sie einen Lese- oder Schreibvorgang ausführen, synchronisierte Anweisungen verwenden. Beispielsweise benötigen Sie in jeder Aktivität, bei der Sie die Datei lesen und schreiben, ein Objekt, das als systeminterne Sperre für die synchronisierten Anweisungen verwendet wird:

Kotlin

// Object for intrinsic lock
companion object {
    val sDataLock = Any()
}

Java

// Object for intrinsic lock
static final Object sDataLock = new Object();

Erstellen Sie dann jedes Mal, wenn Sie die Dateien lesen oder schreiben, eine synchronisierte Anweisung mit dieser Sperre. Hier ist beispielsweise eine synchronisierte Anweisung zum Schreiben des letzten Punktestands in einem Spiel in eine Datei:

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");
}

Sie sollten Ihre Leseanweisungen mit derselben Sperre synchronisieren.

Anschließend müssen Sie in Ihrer BackupAgentHelper onBackup() und onRestore() überschreiben, um die Sicherungs- und Wiederherstellungsvorgänge mit derselben systemeigenen Sperre zu synchronisieren. Für das obige Beispiel MyFileBackupAgent sind beispielsweise die folgenden Methoden erforderlich:

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);
    }
}

BackupAgent erweitern

Die meisten Anwendungen sollten die BackupAgent-Klasse nicht direkt erweitern müssen. Stattdessen sollten Sie BackupAgentHelper erweitern, um die integrierten Hilfsklassen zu nutzen, die Ihre Dateien automatisch sichern und wiederherstellen. Sie können BackupAgent jedoch direkt erweitern, um Folgendes zu tun:

  • Versionieren Sie Ihr Datenformat. Wenn Sie beispielsweise davon ausgehen, dass Sie das Format, in dem Sie Ihre App-Daten schreiben, ändern müssen, können Sie einen Backup-Agent erstellen, um Ihre App-Version während eines Wiederherstellungsvorgangs zu überprüfen und alle erforderlichen Kompatibilitätsarbeiten durchzuführen, falls sich die Version auf dem Gerät von der der Sicherungsdaten unterscheidet. Weitere Informationen finden Sie unter Version der Datenwiederherstellung prüfen.
  • Teile der Daten angeben, die gesichert werden sollen Anstatt eine ganze Datei zu sichern, können Sie angeben, welche Teile der Daten gesichert werden sollen und wie jeder Teil dann auf dem Gerät wiederhergestellt wird. Dies kann auch bei der Verwaltung verschiedener Versionen hilfreich sein, da Sie Ihre Daten als eindeutige Entitäten und nicht als vollständige Dateien lesen und schreiben.
  • Daten in einer Datenbank sichern: Wenn Sie eine SQLite-Datenbank haben, die bei der Neuinstallation der Anwendung durch den Nutzer wiederhergestellt werden soll, müssen Sie eine benutzerdefinierte BackupAgent erstellen, die während eines Sicherungsvorgangs die entsprechenden Daten liest. Erstellen Sie dann Ihre Tabelle und fügen Sie die Daten während eines Wiederherstellungsvorgangs ein.

Wenn Sie keine der oben genannten Aufgaben ausführen müssen und vollständige Dateien aus SharedPreferences oder dem internen Speicher sichern möchten, lesen Sie BackupAgentHelper erweitern.

Erforderliche Methoden

Wenn Sie ein BackupAgent erstellen, müssen Sie die folgenden Callback-Methoden implementieren:

onBackup()
Der Sicherungsmanager ruft diese Methode auf, nachdem Sie eine Sicherung angefordert haben. Bei dieser Methode lesen Sie Ihre App-Daten vom Gerät aus und übergeben die Daten, die Sie sichern möchten, an den Sicherungsmanager, wie unter Daten sichern beschrieben.
onRestore()

Der Sicherungsmanager ruft diese Methode während eines Wiederherstellungsvorgangs auf. Diese Methode stellt die Sicherungsdaten bereit, mit denen die Anwendung ihren ursprünglichen Zustand wiederherstellen kann, wie unter Wiederherstellung durchführen beschrieben.

Das System ruft diese Methode auf, um Sicherungsdaten wiederherzustellen, wenn der Nutzer Ihre App neu installiert. Ihre App kann aber auch eine Wiederherstellung anfordern.

Sicherung durchführen

Eine Sicherungsanfrage führt nicht sofort zu einem sofortigen Aufruf der Methode onBackup(). Stattdessen wartet Backup Manager eine angemessene Zeit und führt dann eine Sicherung für alle Anwendungen aus, die seit der letzten Sicherung eine Sicherung angefordert haben. An diesem Punkt müssen Sie Ihre Anwendungsdaten für den Sicherungsmanager bereitstellen, damit sie in Cloud Storage gespeichert werden können.

Nur der Sicherungsmanager kann die Methode onBackup() des Sicherungs-Agents aufrufen. Jedes Mal, wenn sich Ihre Anwendungsdaten ändern und Sie eine Sicherung durchführen möchten, müssen Sie durch Aufrufen von dataChanged() einen Sicherungsvorgang anfordern. Weitere Informationen finden Sie unter Sicherung anfordern.

Tipp: Während der Entwicklung Ihrer Anwendung können Sie mit dem bmgr-Tool über den Sicherungsmanager einen sofortigen Sicherungsvorgang starten.

Wenn der Sicherungsmanager die Methode onBackup() aufruft, werden drei Parameter übergeben:

oldState
Ein offener, schreibgeschützter ParcelFileDescriptor, der auf den letzten Sicherungsstatus Ihrer Anwendung verweist. Dies sind keine Sicherungsdaten aus dem Cloud-Speicher, sondern eine lokale Darstellung der Daten, die beim letzten Aufruf von onBackup() gesichert wurden, wie durch newState oder onRestore() definiert. onRestore() wird im nächsten Abschnitt behandelt. Da Sie mit onBackup() vorhandene Sicherungsdaten im Cloud-Speicher nicht lesen können, können Sie mit dieser lokalen Darstellung feststellen, ob sich die Daten seit der letzten Sicherung geändert haben.
data
Ein BackupDataOutput-Objekt, mit dem Sie Ihre Sicherungsdaten an den Sicherungsmanager senden.
newState
Ein offener Lese-/Schreib-ParcelFileDescriptor, der auf eine Datei verweist, in der Sie eine Darstellung der Daten schreiben müssen, die Sie an data übermittelt haben. Eine Darstellung kann so einfach wie der Zeitstempel der letzten Änderung Ihrer Datei sein. Dieses Objekt wird als oldState zurückgegeben, wenn der Sicherungsmanager das nächste Mal die Methode onBackup() aufruft. Wenn Sie Ihre Sicherungsdaten nicht in newState schreiben, verweist oldState beim nächsten Aufruf von onBackup() durch Backup Manager auf eine leere Datei.

Implementieren Sie mithilfe dieser Parameter die Methode onBackup(), um Folgendes zu ermöglichen:

  1. Prüfen Sie, ob sich Ihre Daten seit der letzten Sicherung geändert haben, indem Sie oldState mit Ihren aktuellen Daten vergleichen. Wie Sie Daten in oldState lesen, hängt davon ab, wie Sie sie ursprünglich in newState geschrieben haben (siehe Schritt 3). Der Status einer Datei lässt sich am einfachsten anhand des Zeitstempels der letzten Änderung erfassen. So können Sie beispielsweise einen Zeitstempel von oldState lesen und vergleichen:

    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
    }
    

    Wenn sich nichts geändert hat und Sie keine Daten sichern müssen, fahren Sie mit Schritt 3 fort.

  2. Wenn sich Ihre Daten im Vergleich zu oldState geändert haben, schreiben Sie die aktuellen Daten in data, um sie im Cloud-Speicher zu sichern.

    Sie müssen jeden Datenblock als Entität im BackupDataOutput schreiben. Eine Entität ist ein vereinfachter binärer Datensatz, der durch einen eindeutigen Schlüsselstring identifiziert wird. Daher ist das Dataset, das Sie sichern, konzeptionell ein Satz von Schlüssel/Wert-Paaren.

    So fügen Sie Ihrem Sicherungsdatensatz eine Entität hinzu:

    1. Rufen Sie writeEntityHeader() auf und übergeben Sie einen eindeutigen Stringschlüssel für die Daten, die Sie schreiben möchten, sowie die Datengröße.

    2. Rufen Sie writeEntityData() auf und übergeben Sie einen Bytezwischenspeicher, der Ihre Daten und die Anzahl der aus dem Zwischenspeicher zu schreibenden Byte enthält. Diese sollte der an writeEntityHeader() übergebenen Größe entsprechen.

    Der folgende Code vereinfacht beispielsweise einige Daten in einem Bytestream und schreibt sie in eine einzelne Entität:

    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);
    

    Führen Sie diesen Vorgang für alle Daten aus, die Sie sichern möchten. Wie Sie Ihre Daten in Entitäten aufteilen, bleibt Ihnen überlassen. Sie können sogar nur eine Entität verwenden.

  3. Schreiben Sie eine Darstellung der aktuellen Daten in den newState-ParcelFileDescriptor, unabhängig davon, ob Sie eine Sicherung (in Schritt 2) durchführen oder nicht. Der Backup Manager bewahrt dieses Objekt lokal als Darstellung der aktuell gesicherten Daten auf. Beim nächsten Aufruf von onBackup() wird dies als oldState an Sie zurückgegeben. So können Sie feststellen, ob eine weitere Sicherung erforderlich ist, wie in Schritt 1 beschrieben. Wenn Sie den aktuellen Datenstatus nicht in diese Datei schreiben, ist oldState beim nächsten Callback leer.

    Im folgenden Beispiel wird eine Darstellung der aktuellen Daten mithilfe des Zeitstempels der letzten Änderung der Datei in newState gespeichert:

    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);
    

Wiederherstellung ausführen

Wenn Ihre Anwendungsdaten wiederhergestellt werden müssen, ruft der Sicherungsmanager die Methode onRestore() des Backup-Agents auf. Wenn diese Methode aufgerufen wird, sendet der Sicherungsmanager Ihre Sicherungsdaten, damit Sie sie auf dem Gerät wiederherstellen können.

Nur der Sicherungsmanager kann onRestore() aufrufen. Dies geschieht automatisch, wenn das System Ihre App installiert und vorhandene Sicherungsdaten findet.

Wenn der Sicherungsmanager die Methode onRestore() aufruft, werden drei Parameter übergeben:

data
Ein BackupDataInput-Objekt, mit dem Sie Ihre Sicherungsdaten lesen können.
appVersionCode
Eine Ganzzahl, die den Wert des Manifestattributs android:versionCode Ihrer App zum Zeitpunkt der Sicherung dieser Daten darstellt. Damit können Sie die aktuelle Anwendungsversion prüfen und feststellen, ob das Datenformat kompatibel ist. Weitere Informationen dazu, wie Sie damit verschiedene Versionen von Wiederherstellungsdaten verarbeiten können, finden Sie unter Datenversion für die Wiederherstellung prüfen.
newState
Ein offener Lese-/Schreib-ParcelFileDescriptor, der auf eine Datei verweist, in die Sie den endgültigen Sicherungsstatus schreiben müssen, der mit data bereitgestellt wurde. Für dieses Objekt wird beim nächsten Aufruf von onBackup() der Status oldState zurückgegeben. Sie müssen dasselbe newState-Objekt in den onBackup()-Callback schreiben. Dadurch wird sichergestellt, dass das oldState-Objekt, das onBackup() übergeben wird, auch beim ersten Aufruf von onBackup() nach der Wiederherstellung des Geräts gültig ist.

Bei der Implementierung von onRestore() sollten Sie readNextHeader() für data aufrufen, um alle Entitäten im Dataset zu durchlaufen. Führen Sie für jede gefundene Entität folgende Schritte aus:

  1. Rufen Sie den Entitätsschlüssel mit getKey() ab.
  2. Vergleichen Sie den Entitätsschlüssel mit einer Liste bekannter Schlüssel/Wert-Paare, die Sie in der Klasse BackupAgent als statische finale Strings deklariert hätten sollten. Wenn der Schlüssel mit einem Ihrer bekannten Schlüsselstrings übereinstimmt, geben Sie eine Anweisung ein, um die Entitätsdaten zu extrahieren und auf dem Gerät zu speichern:

    1. Rufen Sie die Größe der Entitätsdaten mit getDataSize() ab und erstellen Sie ein Byte-Array dieser Größe.
    2. Rufen Sie readEntityData() auf, übergeben Sie das Byte-Array, in dem die Daten gespeichert werden, und geben Sie den Startversatz und die zu lesende Größe an.
    3. Ihr Byte-Array ist jetzt voll. Sie können die Daten nach Belieben lesen und auf das Gerät schreiben.
  3. Nachdem Sie Ihre Daten wieder auf das Gerät gelesen und geschrieben haben, schreiben Sie den Status Ihrer Daten in den newState-Parameter, wie Sie es während onBackup() tun.

So können Sie beispielsweise die Daten wiederherstellen, die mit dem Beispiel im vorherigen Abschnitt gesichert wurden:

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);
}

In diesem Beispiel wird der an onRestore() übergebene Parameter appVersionCode nicht verwendet. Sie können ihn jedoch verwenden, wenn Sie eine Sicherung durchführen möchten, wenn die Nutzerversion der App tatsächlich rückwärts verschoben wurde (z. B. wenn der Nutzer von Version 1.5 Ihrer Anwendung zu Version 1.0 gewechselt ist). Weitere Informationen finden Sie im nächsten Abschnitt.

Version der Datenwiederherstellung prüfen

Wenn der Sicherungsmanager Ihre Daten in Cloud Storage speichert, ist darin automatisch die Version Ihrer Anwendung enthalten, wie im Attribut android:versionCode der Manifestdatei definiert. Bevor der Sicherungsmanager den Sicherungs-Agent zum Wiederherstellen Ihrer Daten aufruft, prüft er die android:versionCode der installierten Anwendung und vergleicht sie mit dem Wert, der im Wiederherstellungs-Dataset aufgezeichnet wurde. Wenn die im Wiederherstellungsdatensatz aufgezeichnete Version neuer ist als die App-Version auf dem Gerät, hat der Nutzer ein Downgrade der App durchgeführt. In diesem Fall bricht der Sicherungsmanager die Wiederherstellung für die App ab und ruft die Methode onRestore() nicht auf, da der Wiederherstellungssatz für eine ältere Version als bedeutungslos angesehen wird.

Sie können dieses Verhalten mit dem Attribut android:restoreAnyVersion überschreiben. Setzen Sie dieses Attribut auf true, um anzugeben, dass Sie die Anwendung unabhängig von der Version des Wiederherstellungssatzes wiederherstellen möchten. Der Standardwert ist false. Wenn Sie hier true festlegen, ignoriert der Sicherungsmanager android:versionCode und ruft in allen Fällen die Methode onRestore() auf. Dabei können Sie den Versionsunterschied in der Methode onRestore() manuell prüfen und alle erforderlichen Schritte unternehmen, um die Daten kompatibel zu machen, wenn die Versionen nicht übereinstimmen.

Damit Sie während eines Wiederherstellungsvorgangs verschiedene Versionen verarbeiten können, übergibt die Methode onRestore() den im Wiederherstellungsdatensatz enthaltenen Versionscode als appVersionCode-Parameter. Anschließend können Sie den Versionscode der aktuellen App mit dem Feld PackageInfo.versionCode abfragen. Beispiele:

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;
}

Vergleichen Sie dann die von PackageInfo erhaltene version mit dem an onRestore() übergebenen appVersionCode.

Sicherung anfordern

Sie können jederzeit einen Sicherungsvorgang anfordern, indem Sie dataChanged() aufrufen. Bei dieser Methode wird der Sicherungsmanager darüber informiert, dass Sie Ihre Daten mit dem Sicherungs-Agent sichern möchten. Der Sicherungsmanager ruft dann die Methode onBackup() des Back-up-Agents zu einem späteren Zeitpunkt auf. In der Regel sollten Sie jedes Mal eine Sicherung anfordern, wenn sich Ihre Daten ändern (z. B. wenn der Nutzer eine App-Präferenz ändert, die Sie sichern möchten). Wenn Sie dataChanged() mehrmals aufrufen, bevor der Sicherungsmanager eine Sicherung von Ihrem Agent anfordert, erhält der Agent immer noch nur einen Aufruf an onBackup().

Wiederherstellung anfordern

Während der normalen Lebensdauer Ihrer Anwendung sollten Sie keinen Wiederherstellungsvorgang anfordern müssen. Das System prüft automatisch, ob Sicherungsdaten vorhanden sind, und führt eine Wiederherstellung durch, wenn die Anwendung installiert wird.

Zur automatischen Sicherung migrieren

Du kannst deine App auf vollständige Datensicherungen umstellen. Setze dazu android:fullBackupOnly im <application>-Element der Manifestdatei auf true. Wenn die App auf einem Gerät mit Android 5.1 (API-Level 22) oder niedriger ausgeführt wird, ignoriert die App diesen Wert im Manifest und führt weiterhin Sicherungen von Schlüssel/Wert-Paaren durch. Wenn Ihre App auf einem Gerät mit Android 6.0 (API-Level 23) oder höher ausgeführt wird, erfolgt anstelle der Sicherung von Schlüssel/Wert-Paaren eine automatische Sicherung.

Datenschutz

Wir bei Google sind uns des Vertrauens bewusst, das Nutzer in uns setzen, und sind uns unserer Verantwortung für den Schutz der Privatsphäre unserer Nutzer bewusst. Google überträgt Sicherungsdaten auf sichere Weise an und von Google-Servern, um Sicherungs- und Wiederherstellungsfunktionen bereitzustellen. Diese Daten werden gemäß der Datenschutzerklärung von Google als personenbezogene Daten behandelt.

Darüber hinaus können Nutzer die Datensicherungsfunktion über die Sicherungseinstellungen des Android-Systems deaktivieren. Wenn ein Nutzer die Sicherung deaktiviert, löscht Android Backup Service alle gespeicherten Sicherungsdaten. Ein Nutzer kann die Sicherung auf dem Gerät wieder aktivieren, aber der Android Backup Service stellt keine zuvor gelöschten Daten wieder her.