Tworzenie kopii zapasowych par klucz-wartość przy użyciu Android Backup Service

Usługa kopii zapasowej Androida umożliwia tworzenie kopii zapasowych danych w chmurze i ich przywracanie w aplikacji na Androida. Podczas operacji tworzenia kopii zapasowej danych klucz-wartość dane kopii zapasowej aplikacji są przekazywane do mechanizmu przesyłania kopii zapasowej urządzenia. Jeśli urządzenie korzysta z domyślny transport kopii zapasowych Google, wówczas dane będą przekazywane do kopii zapasowej Androida. Usługa archiwizacji.

Każdy użytkownik aplikacji może mieć maksymalnie 5 MB danych. Przechowywanie danych jest bezpłatne tworzyć kopię zapasową danych.

Przegląd opcji tworzenia kopii zapasowych w Androidzie i wskazówki dotyczące danych tworzyć i przywracać kopie zapasowe, zapoznaj się z sekcją Kopia zapasowa danych .

Wdrożenie kopii zapasowej par klucz-wartość

Aby utworzyć kopię zapasową danych aplikacji, musisz wdrożyć agenta kopii zapasowej. Twój agent kopii zapasowej jest wywoływany przez menedżera kopii zapasowych zarówno podczas tworzenia kopii zapasowej, jak i przywracania.

Aby wdrożyć agenta zapasowego, musisz:

  1. Zadeklaruj agenta kopii zapasowej w pliku manifestu za pomocą atrybutu android:backupAgent.

  2. Zdefiniuj agenta zapasowego, wykonując jedną z tych czynności:

    • Przedłużam: BackupAgent

      Zajęcia BackupAgent to główny interfejs, za pomocą którego aplikacja komunikuje się z Menedżer kopii zapasowych. Jeśli rozszerzysz tę klasę bezpośrednio, musisz zastąpić metody onBackup() i onRestore(), aby obsługiwać operacje tworzenia kopii zapasowych i przywracania danych.

    • Przedłużam: BackupAgentHelper

      BackupAgentHelper zapewnia wygodny kod wokół klasy BackupAgent, do zminimalizowania ilości kodu do napisania. W BackupAgentHelper, musisz użyć co najmniej 1 obiektu pomocniczego, który automatycznie tworzyć i przywracać kopie zapasowe określonych typów danych, dzięki czemu unikniesz Trzeba zaimplementować onBackup() i onRestore(). Jeśli nie potrzebujesz pełnej kontroli nad kopiami zapasowymi aplikacji, zalecamy używanie BackupAgentHelper do zarządzania kopiami zapasowymi aplikacji.

      Android udostępnia obecnie pomocnicze narzędzia do tworzenia kopii zapasowych, które umożliwiają tworzenie kopii zapasowych i przywracanie kompletnych plików z SharedPreferences oraz wewnętrznego miejsca na dane.

Zadeklarowanie agenta zapasowego w pliku manifestu

Gdy zdecydujesz się na nazwę klasy dla agenta zapasowego, zadeklaruj ją w manifeście za pomocą atrybutu android:backupAgent w tagu <application>.

Na przykład:

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

W celu obsługi starszych urządzeń zalecamy dodanie klucza interfejsu API <meta-data> do pliku manifestu Androida. Usługa Android Backup Service nie wymaga już klucza usługi, ale niektóre starsze urządzenia mogą nadal sprawdzać klucz podczas tworzenia kopii zapasowej. Ustaw android:name na com.google.android.backup.api_key, a android:value do unused.

android:restoreAnyVersion przyjmuje wartość logiczną wskazującą, czy chcesz przywrócić aplikację niezależnie od bieżącej wersji aplikacji w porównaniu z wersją przy tworzeniu kopii zapasowej. Wartością domyślną jest false. Więcej informacji znajdziesz w artykule Sprawdzanie wersji danych do przywrócenia.

Rozszerzenie BackupAgentHelper

Aby to zrobić, utwórz agenta kopii zapasowej za pomocą BackupAgentHelper tworzyć kopie zapasowe pełnych plików z usługi SharedPreferences lub pamięci wewnętrznej. Utworzenie agenta kopii zapasowej za pomocą BackupAgentHelper wymaga znacznie mniej kodu niż rozszerzenie BackupAgent, ponieważ nie musisz implementować onBackup() ani onRestore().

Implementacja BackupAgentHelper musi korzystać z co najmniej 1 asystenta zapasowego. Asystent kopii zapasowej to wyspecjalizowany komponent, do którego przywołuje się w usłudze BackupAgentHelper wykonywania operacji tworzenia i przywracania kopii zapasowych określonego typu danych. Platforma Android udostępnia obecnie 2 różne aplikacje pomocnicze:

W elementach BackupAgentHelper możesz uwzględnić wiele elementów pomocniczych, ale dla każdego typu danych potrzebny jest tylko 1 element pomocniczy. Oznacza to, że jeśli masz kilka SharedPreferences plików, potrzebujesz tylko jednego SharedPreferencesBackupHelper

W przypadku każdego pomocnika, którego chcesz dodać do BackupAgentHelper, musisz wykonać te czynności w ramach metody onCreate():

  1. Utwórz instancję odpowiedniej klasy pomocniczej. W konstruktorze klasy musisz określić pliki, których kopię zapasową chcesz utworzyć.
  2. Zadzwoń do nas addHelper() aby dodać pomocnik do urządzenia BackupAgentHelper.

W poniższych sekcjach opisano, jak utworzyć agenta zapasowego za pomocą każdego z z dostępnymi pomocnikami.

Tworzenie kopii zapasowej SharedPreferences

Podczas tworzenia instancji SharedPreferencesBackupHelper musisz dołączyć parametr nazwy co najmniej jednego pliku SharedPreferences.

Aby na przykład utworzyć kopię zapasową pliku SharedPreferences o nazwie user_preferences, pełny agent kopii zapasowej przy użyciu BackupAgentHelper wygląda tak:

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

SharedPreferencesBackupHelper zawiera cały kod niezbędny do utworzenia kopii zapasowej przywrócić plik SharedPreferences.

Gdy Menedżer kopii zapasowej wywoła onBackup() i onRestore(), BackupAgentHelper wezwa pomocników, aby utworzyć i przywrócić kopię zapasową danych określonych plików.

Tworzenie kopii zapasowych innych plików

Podczas tworzenia instancji FileBackupHelper musisz podać nazwę jednego lub w pamięci wewnętrznej aplikacji, zgodnie z ustawieniami getFilesDir() czyli w tym samym miejscu, openFileOutput() zapisuje pliki.

Na przykład, aby utworzyć kopię zapasową 2 plików o nazwach scores i stats, agent kopii zapasowej korzystający z BackupAgentHelper będzie wyglądać tak:

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

FileBackupHelper zawiera cały kod niezbędny do utworzenia kopii zapasowej i przywrócenia danych zapisane w pamięci wewnętrznej aplikacji.

Odczytywanie i zapisywanie plików w pamięci wewnętrznej nie jest bezpieczne w wąskim kontekście wątku. Aby mieć pewność, że agent kopii zapasowej nie odczyta ani nie zapisze plików jednocześnie z działaniami, należy używać zsynchronizowanych instrukcji za każdym razem, gdy wykonujesz odczyt lub zapis. Na przykład w każdej czynności, w której odczytujesz i zapisujesz plik, musisz użyć obiektu jako blokady wewnętrznej dla instrukcji zsynchronizowanych:

Kotlin

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

Java

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

Następnie utwórz zsynchronizowaną instrukcję z tą blokadą przy każdym odczytywaniu lub zapisie pliki. Oto na przykład zsynchronizowane instrukcja zapisywania najnowszego wyniku w grze do pliku:

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

Instrukcje odczytu należy synchronizować przy użyciu tej samej blokady.

Następnie w BackupAgentHelper musisz zastąpić zasady onBackup() i onRestore(), aby zsynchronizować operacje tworzenia i przywracania kopii zapasowych z tym samym blokady. Opisane powyżej pole MyFileBackupAgent wymaga: następujące metody:

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

Przedłużenie agenta kopii zapasowej

Większość aplikacji nie musi rozszerzać bezpośrednio klasy BackupAgent, ale powinna rozszerzaćBackupAgentHelper, aby korzystać z wbudowanych klas pomocniczych, które automatycznie tworzą kopie zapasowe plików i je przywracają. Możesz jednak bezpośrednio rozszerzyć dostęp do BackupAgent, aby:

  • Wersja formatu danych. Jeśli na przykład spodziewasz się, że konieczne będzie zmienić format, w którym zapisywane są dane aplikacji, można utworzyć kopię zapasową, do sprawdzenia wersji aplikacji podczas przywracania wszelkie niezbędne zgodności działają, jeśli wersja na urządzeniu jest inna niż w kopii zapasowej. Więcej informacji znajdziesz w artykule Sprawdzanie wersji danych do przywracania.
  • Określ części danych, które mają być zapisywane w kopii zapasowej. Zamiast tworzyć kopię zapasową całego pliku, możesz określić, które części danych mają zostać skopiowane i jak mają zostać przywrócone na urządzenie. Pomaga to też w zarządzaniu różnymi wersjami, ponieważ dane odczytuje się i zapisze jako unikalne elementy, a nie jako kompletne pliki.
  • Utwórz kopię zapasową danych w bazie danych. Jeśli masz bazę danych SQLite, którą chcesz przywrócić, gdy użytkownik ponownie zainstaluje aplikację, musisz utworzyć niestandardową funkcję BackupAgent, która odczytuje odpowiednie dane podczas operacji tworzenia kopii zapasowej, a następnie utworzy tabelę i wstawi dane podczas operacji przywracania.

Jeśli nie musisz wykonywać żadnej z powyższych czynności i chcesz utworzyć kopię zapasową pełne pliki z usługi SharedPreferences lub pamięci wewnętrznej, patrz Przedłużanie BackupAgentHelper.

Wymagane metody

Podczas tworzenia BackupAgent musisz zaimplementować te metody wywołania zwrotnego:

onBackup()
Menedżer kopii zapasowych wywołuje tę metodę po przesłaniu prośby Kopia zapasowa. Ta metoda odczytuje dane aplikacji z urządzenia i przekazać do Menedżera kopii zapasowych dane, których kopie zapasowe chcesz utworzyć. opisane w artykule Tworzenie kopii zapasowej.
onRestore()

Menedżer kopii zapasowych wywołuje tę metodę podczas operacji przywracania. Ta metoda udostępnia kopię zapasową danych, dzięki której aplikacja może przywrócić jak opisano w sekcji Przywracanie danych.

System wywołuje tę metodę, aby przywrócić dane kopii zapasowej, gdy użytkownik ponownie instaluje Twoją aplikację, ale aplikacja może też poprosić o przywróć.

Utwórz kopię zapasową

Żądanie kopii zapasowej nie powoduje natychmiastowego wywołania metody onBackup(). Zamiast tego Menedżer kopii zapasowych czeka odpowiedni czas, a potem tworzy kopie zapasowe wszystkich aplikacji, które poprosiły o kopię zapasową od czasu ostatniej kopii zapasowej. W tym momencie musisz przekazać dane aplikacji do Menedżera kopii zapasowych, aby można je było zapisać w magazynie w chmurze.

Tylko menedżer kopii zapasowych może wywołać metodę onBackup() agenta kopii zapasowej. Za każdym razem, gdy dane aplikacji ulegną zmianie i chcesz utworzyć kopię zapasową, musisz poprosić o wykonanie operacji tworzenia kopii zapasowej, wywołując funkcję dataChanged(). Więcej informacji znajdziesz w artykule Wymaganie utworzenia kopii zapasowej.

Wskazówka: podczas tworzenia aplikacji możesz uruchomić natychmiastowe tworzenie kopii zapasowej w Menedżerze kopii zapasowych za pomocą narzędzia bmgr.

Gdy Menedżer kopii zapasowych wywołuje metodę onBackup(), przekazuje 3 parametry:

oldState
Otwarta, tylko do odczytu ParcelFileDescriptor wskazuje do ostatniego stanu kopii zapasowej udostępnionego przez aplikację. To nie są dane kopii zapasowej z chmury, ale jest lokalną reprezentacją danych, których kopia zapasowa została utworzona czas ostatniego wywołania funkcji onBackup(), zgodnie z definicją zawartą w funkcji newState lub z onRestore() onRestore() omawiamy w następnej sekcji. Ponieważ onBackup() nie pozwala na odczytywanie istniejących danych kopii zapasowej w chmurze, możesz użyć tej lokalnej reprezentacji, aby określić, czy Twoje dane zmieniły się od czasu ostatniej kopii zapasowej.
data
Obiekt BackupDataOutput, który służy do przesyłania danych kopii zapasowej do Menedżera kopii zapasowych.
newState
Otwarty, do odczytu i zapisu ParcelFileDescriptor wskazujący plik, w którym musi zapisać dane dostarczone przez Ciebie do data. O może być tak prosta jak sygnatura czasowa ostatniej modyfikacji pliku. Ten obiekt jest zwracany jako oldState podczas następnego wywołania przez menedżera kopii zapasowej metody onBackup(). Jeśli nie zapiszesz danych kopii zapasowej w usłudze newState, a przy następnym wywołaniu Menedżera kopii zapasowej oldState wskaże pusty plik onBackup()

Za pomocą tych parametrów zaimplementuj metodę onBackup(), aby:

  1. Sprawdź, czy Twoje dane zmieniły się od czasu ostatniej kopii zapasowej, porównując dane z kopii zapasowej oldState z bieżącymi danymi. Sposób odczytywania danych w usłudze oldState zależy od sposób, w jaki zostało ono wysłane do użytkownika newState (patrz krok 3). Najprostszym sposobem rejestrowania stanu pliku jest użycie sygnatury czasowej ostatniej modyfikacji. Przykład: Aby odczytać i porównać sygnaturę czasową z domeny oldState:

    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
    }

    Jeśli nic się nie zmieniło i nie musisz tworzyć kopii zapasowej, przejdź do kroku 3.

  2. Jeśli Twoje dane uległy zmianie w porównaniu z oldState, zapisz bieżące dane w data, aby utworzyć ich kopię zapasową w chmurze.

    Każdy fragment danych musisz zapisać jako encję w BackupDataOutput. An encja to spłaszczony rekord binarny identyfikowany przez unikalny klucz ciągu znaków. Dlatego zestaw danych, którego kopię zapasową tworzysz, to teoretycznie zbiór par klucz-wartość.

    Aby dodać encję do zbioru danych kopii zapasowej, musisz:

    1. Zadzwoń do nas writeEntityHeader(), przekazując w niej unikalny klucz ciągu znaków dla danych, które zamierzasz zapisać, oraz rozmiaru danych.

    2. Wywołaj funkcję writeEntityData(), przekazując bufor bajtów zawierający dane i liczbę bajtów do zapisania z bufora, która powinna być zgodna z rozmiarem przekazanym do funkcji writeEntityHeader().

    Na przykład w poniższym kodzie niektóre dane są spłaszczone do postaci strumienia bajtów, zapisuje go w pojedynczej jednostce:

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

    Wykonaj te czynności w przypadku każdego elementu danych, którego kopię zapasową chcesz utworzyć. Sposób dzielenia zależy od Ciebie. Możesz użyć nawet tylko 1 elementu.

  3. Niezależnie od tego, czy wykonasz kopię zapasową (w kroku 2), zapisz reprezentacje bieżących danych w pliku newState ParcelFileDescriptor. Menedżer kopii zapasowych przechowuje ten obiekt lokalnie jako reprezentację danych, które są obecnie kopiowane. Gdy następnym razem wywoła się funkcja oldState, przekaże Ci ją jako oldState, aby umożliwić Ci określenie, czy potrzebna jest kolejna kopia zapasowa (w tym celu wykonasz krok 1). Jeśli nie zapiszesz bieżącego stanu danych do tego pliku, pole oldState będzie puste podczas następnego wywołania zwrotnego.

    W poniższym przykładzie zapisywane są bieżące dane w pliku newState przy użyciu sygnatury czasowej ostatniej zmiany pliku:

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

Przywracanie

Gdy nadejdzie czas przywracania danych aplikacji, Menedżer kopii zapasowej wywołuje metodę onRestore() skryptu kopii zapasowej. Po wywołaniu tej metody Menedżer kopii zapasowych dostarcza kopię zapasową danych, by móc ją przywrócić na urządzeniu.

Tylko menedżer kopii zapasowych może wywołać funkcję onRestore() (co odbywa się automatycznie). gdy system instaluje aplikację i znajduje dane w kopii zapasowej.

Gdy Menedżer kopii zapasowych wywołuje metodę onRestore(), przekazuje 3 parametry:

data
Obiekt BackupDataInput, który umożliwia odczytywanie danych kopii zapasowej.
appVersionCode
Liczba całkowita określająca wartość android:versionCode w pliku manifestu. Możesz go użyć do sprawdzenia bieżącej wersji aplikacji i określenia, czy format danych jest zgodny. Więcej informacji o używaniu tej funkcji do obsługi różnych wersji danych do przywracania znajdziesz w artykule Sprawdzanie wersji danych do przywracania.
newState
Otwarty plik ParcelFileDescriptor z dostępem do odczytu i zapisu, który wskazuje plik, do którego musisz zapisać końcowy stan kopii zapasowej podany w pliku data. Ten obiekt jest zwracany jako oldState przy następnym wywołaniu funkcji onBackup(). Przypomnij sobie, musisz również napisać ten sam obiekt newState w funkcji onBackup() wywołanie zwrotne – można tu też sprawdzić, czy obiekt oldState przypisany do argumentu Metoda onBackup() jest prawidłowa nawet wtedy, gdy funkcja onBackup() zostanie wywołana po raz pierwszy po urządzenie zostało przywrócone.

W implementacji funkcji onRestore() należy wywołać funkcję readNextHeader() w funkcji data, aby przejść przez wszystkie elementy w zbiorze danych. Dla każdej jednostki wykonaj te czynności:

  1. Pobierz klucz encji za pomocą getKey()
  2. Porównaj klucz elementu z listą znanych wartości kluczy, które powinny być zadeklarowane jako statyczne końcowe ciągi znaków w klasie BackupAgent. Gdy klucz pasuje do jednego ze znanych ciągów kluczowych, wprowadź wyrażenie do instrukcji, aby je wyodrębnić dane jednostki i zapisz je na urządzeniu:

    1. Pobierz rozmiar danych encji za pomocą: getDataSize() i utworzyć tablicę bajtów o tym rozmiarze.
    2. Wywołaj funkcję readEntityData() i przekaż jej tablicę bajtów, do której mają trafić dane, oraz określ przesunięcie początkowe i rozmiar do odczytu.
    3. Tablica bajtów jest teraz pełna. Odczytywanie danych i zapisywanie ich na urządzeniu w dowolny sposób.
  3. Po odczytaniu i zapisaniu danych z powrotem na urządzeniu zapisz stan do parametru newState tak samo jak podczas onBackup()

Oto jak możesz przywrócić dane z kopii zapasowej zgodnie z przykładem w w poprzedniej sekcji:

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

W tym przykładzie parametr appVersionCode przekazany do strony onRestore() to nie . Może być on jednak przydatny, jeśli zdecydujesz się wykonać kopię zapasową gdy wersja aplikacji została przeniesiona do poprzedniej wersji (np. użytkownik zmienił wersję aplikacji z 1.5 na 1.0). Więcej informacji znajdziesz w następnej sekcji.

Sprawdzanie wersji przywracania danych

Gdy Menedżer kopii zapasowej zapisuje dane w chmurze, automatycznie uwzględnia wersję aplikacji zdefiniowaną przez atrybut android:versionCode w pliku manifestu. Zanim Menedżer kopii zapasowych wywoła kopię zapasową aby przywrócić dane, sprawdza android:versionCode zainstalowanej aplikacji i porównuje ją z wartością zarejestrowaną w zbiorze danych przywracania. Jeśli wersja zapisana w zbiorze danych do przywracania jest nowsza niż wersja aplikacji na urządzeniu, oznacza to, że użytkownik usunął aplikację. W takim przypadku Menedżer kopii zapasowych przerwie operację przywracania aplikacji i nie wywoła metody onRestore(), ponieważ zbiór danych do przywracania jest uważany za bezużyteczny w przypadku starszej wersji.

Możesz zastąpić to działanie za pomocą atrybutu android:restoreAnyVersion. Ustaw ten atrybut na true, aby wskazać, że chcesz przywrócić aplikację bez względu na wersję zestawu przywracania. Wartością domyślną jest false. Jeśli ustaw tę wartość na true, Menedżer kopii zapasowych zignoruje android:versionCode i we wszystkich przypadkach wywołaj metodę onRestore(). W tym celu możesz ręcznie sprawdzić różnice w wersji metody onRestore() i podjąć odpowiednie kroki, aby zapewnić zgodność danych, jeśli wersje się różnią.

Aby ułatwić obsługę różnych wersji podczas operacji przywracania, metoda onRestore() przekazuje kod wersji zawarty w zbiorze danych do przywracania jako parametr appVersionCode. Następnie możesz zapytać o kod wersji bieżącej aplikacji za pomocą pola PackageInfo.versionCode. Na przykład:

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

Następnie porównaj wartość version uzyskaną z PackageInfo z wartością appVersionCode przekazaną do funkcji onRestore().

Poproś o kopię zapasową

W każdej chwili możesz poprosić o utworzenie kopii zapasowej, dzwoniąc pod numer dataChanged(). Ta metoda powiadamia Menedżera kopii zapasowych, że chcesz utworzyć kopię zapasową danych za pomocą swojego agenta kopii zapasowej. Menedżer kopii zapasowych wywołuje metodę agenta kopii zapasowej onBackup() w przyszłości. Zazwyczaj należy żądać tworzenia kopii zapasowej za każdym razem, gdy dane ulegają zmianie (np. gdy użytkownik zmienia ustawienia aplikacji, których kopię zapasową chcesz utworzyć). Jeśli przed tym, jak Menedżer kopii zapasowych poprosi o kopię zapasową, zadzwonisz do dataChanged() kilka razy, Twój pracownik obsługi klienta nadal otrzyma tylko 1 wywołanie do onBackup().

Prośba o przywrócenie

Nie musisz prosić o przywrócenie aplikacji podczas normalnego działania aplikacji. . System automatycznie sprawdza, czy istnieją dane kopii zapasowej, i przywraca je po zainstalowaniu aplikacji.

Migracja do Automatycznej kopii zapasowej

Możesz przełączyć aplikację na kopie zapasowe pełnych danych, ustawiając wartość android:fullBackupOnly na true w elemencie <application> w pliku manifestu. Jeśli aplikacja działa na urządzeniu z Androidem 5.1 (poziom interfejsu API 22) lub starszym, ignoruje tę wartość w pliku manifestu i nadal wykonuje kopie zapasowe kluczy i wartości. Gdy aplikacja działa na urządzeniu z Androidem 6.0 (poziom interfejsu API 23) lub nowszym, wykonuje automatyczne kopie zapasowe zamiast kopii zapasowych kluczy i wartości.

Prywatność użytkowników

W Google doskonale zdajemy sobie sprawę z zaufania, jakim obdarzają nas użytkownicy, odpowiedzialnością za ochronę użytkowników prywatności. Google bezpiecznie przesyła dane kopii zapasowej do serwerów Google i z nich w celu udostępniania funkcji tworzenia kopii zapasowych i przywracania danych. Google traktuje te dane jako dane osobowe zgodnie z Polityka prywatności.

Użytkownicy mogą też wyłączyć funkcję tworzenia kopii zapasowych w ustawieniach kopii zapasowej systemu Android. Gdy użytkownik wyłączy kopię zapasową, usługa Android Backup Service usunie wszystkie zapisane dane kopii zapasowej. Użytkownik może ponownie włączyć kopię zapasową na urządzeniu, ale usługa kopii zapasowej Androida nie przywróci wcześniej usuniętych danych.