Android Backup Service umożliwia tworzenie i przywracanie kopii zapasowych danych par klucz-wartość w chmurze w Twojej aplikacji na Androida. Podczas operacji tworzenia kopii zapasowej par klucz-wartość dane kopii zapasowej aplikacji są przekazywane do transportu kopii zapasowych na urządzeniu. Jeśli urządzenie korzysta z domyślnego transportu zapasowego Google, dane są przekazywane do Android Backup Service w celu archiwizacji.
Maksymalny rozmiar danych to 5 MB na użytkownika aplikacji. Przechowywanie kopii zapasowych danych jest bezpłatne.
Omówienie opcji tworzenia kopii zapasowych na Androidzie oraz wskazówki dotyczące tego, które dane należy tworzyć i przywracać, znajdziesz w omówieniu kopii zapasowych danych.
Wdrażanie kopii zapasowych par klucz-wartość
Aby utworzyć kopię zapasową danych aplikacji, musisz wdrożyć agenta zapasowego. Agent kopii zapasowych jest wywoływany przez Menedżera kopii zapasowych zarówno podczas tworzenia, jak i przywracania kopii zapasowych.
Aby wdrożyć agenta zapasowego, musisz:
Zadeklaruj agenta zapasowego w pliku manifestu za pomocą atrybutu
android:backupAgent
.Zdefiniuj agenta zapasowego, wykonując jedną z tych czynności:
-
Klasa
BackupAgent
zapewnia główny interfejs, którego aplikacja używa do komunikacji z Menedżerem kopii zapasowych. Jeśli rozszerzysz tę klasę bezpośrednio, musisz zastąpićonBackup()
ionRestore()
, aby obsługiwać operacje tworzenia i przywracania kopii zapasowych danych. -
Klasa
BackupAgentHelper
zapewnia wygodne opakowanie klasyBackupAgent
, która zmniejsza ilość kodu do napisania. WBackupAgentHelper
musisz używać co najmniej 1 obiektu pomocniczego, który automatycznie tworzy i przywraca kopie zapasowe określonych typów danych. Dzięki temu nie musisz implementowaćonBackup()
ionRestore()
. Jeśli nie potrzebujesz pełnej kontroli nad kopiami zapasowymi aplikacji, do ich obsługi zalecamy korzystanie zBackupAgentHelper
.Android udostępnia obecnie narzędzia pomocnicze do tworzenia kopii zapasowych, które tworzą i przywracają pełne pliki z
SharedPreferences
i pamięci wewnętrznej.
-
Zadeklaruj agenta zapasowego w pliku manifestu
Po wybraniu nazwy klasy agenta zapasowego zadeklaruj ją w pliku manifestu 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>
Aby obsługiwać starsze urządzenia, zalecamy dodanie klucza interfejsu API <meta-data>
do pliku manifestu Androida. Android Backup Service nie wymaga już klucza usługi, ale niektóre starsze urządzenia mogą nadal go sprawdzać podczas tworzenia kopii zapasowej. Ustaw android:name
na com.google.android.backup.api_key
, a android:value
na unused
.
Atrybut android:restoreAnyVersion
przyjmuje wartość logiczną wskazującą, czy chcesz przywrócić dane aplikacji niezależnie od jej obecnej wersji w porównaniu z wersją, która wygenerowała kopię zapasową danych. Wartością domyślną jest false
. Więcej informacji znajdziesz w sekcji Sprawdzanie przywracania danych.
Rozszerzenie usługi BackupAgentHelper
Jeśli chcesz tworzyć kopie zapasowe kompletnych plików z SharedPreferences
lub pamięci wewnętrznej, utwórz agenta kopii zapasowej za pomocą usługi BackupAgentHelper
.
Do utworzenia agenta zapasowego za pomocą BackupAgentHelper
wymaga znacznie mniej kodu niż rozszerzenie BackupAgent
, ponieważ nie musisz implementować onBackup()
ani onRestore()
.
Twoja implementacja BackupAgentHelper
musi korzystać z co najmniej 1 zapasowego elementu pomocniczego.
Asystent kopii zapasowych to wyspecjalizowany komponent, który BackupAgentHelper
przywołuje w celu wykonywania operacji tworzenia i przywracania kopii zapasowej określonego typu danych. Platforma Androida udostępnia obecnie 2 różne pomoce:
SharedPreferencesBackupHelper
, aby utworzyć kopię zapasowąSharedPreferences
plików.FileBackupHelper
, aby utworzyć kopię zapasową plików z pamięci wewnętrznej.
W elemencie BackupAgentHelper
możesz uwzględnić wielu pomocników, ale dla każdego typu danych potrzeba tylko 1 takiego elementu. Oznacza to, że jeśli masz wiele plików SharedPreferences
, potrzebujesz tylko jednego z nich SharedPreferencesBackupHelper
.
W przypadku każdego pomocnika, który chcesz dodać do usługi BackupAgentHelper
, musisz wykonać te czynności podczas korzystania z metody onCreate()
:
- Utwórz instancję wybranej klasy pomocniczej. W konstruktorze klas musisz określić pliki, których kopie zapasowe chcesz utworzyć.
- Zadzwoń pod numer
addHelper()
, aby dodać pomocniczą do:BackupAgentHelper
.
Sekcje poniżej opisują, jak utworzyć agenta kopii zapasowej za pomocą każdej z dostępnych opcji pomocniczych.
Utwórz kopię zapasową SharedPreferences
Gdy tworzysz instancję SharedPreferencesBackupHelper
, musisz dodać nazwę co najmniej jednego pliku SharedPreferences
.
Aby na przykład utworzyć kopię zapasową pliku SharedPreferences
o nazwie user_preferences
, agent tworzenia kopii zapasowej korzystający z 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); } }
Pole SharedPreferencesBackupHelper
zawiera cały kod potrzebny do utworzenia kopii zapasowej i przywrócenia pliku SharedPreferences
.
Gdy Menedżer kopii zapasowych wywołuje onBackup()
i onRestore()
, BackupAgentHelper
wywołuje pomocnicze kopie zapasowe, aby utworzyć kopię zapasową i przywrócić określone pliki.
Tworzenie kopii zapasowych innych plików
Gdy tworzysz instancję FileBackupHelper
, musisz podać nazwę co najmniej jednego pliku zapisanego w pamięci wewnętrznej aplikacji wskazaną w polu getFilesDir()
, czyli tej samej lokalizacji, w której openFileOutput()
zapisuje pliki.
Aby na przykład utworzyć kopię zapasową 2 plików o nazwie scores
i stats
, agent kopii zapasowej korzystający z BackupAgentHelper
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); } }
Plik FileBackupHelper
zawiera cały kod niezbędny do tworzenia i przywracania kopii zapasowych plików zapisanych w pamięci wewnętrznej aplikacji.
Jednak odczyt i zapis plików w pamięci wewnętrznej nie jest bezpieczny dla wątków. Aby agent kopii zapasowej nie odczytywał ani nie zapisywał plików w tym samym czasie co działania, używaj zsynchronizowanych instrukcji podczas każdego odczytu lub zapisu. Na przykład w każdym działaniu, w którym czytasz i zapisujesz plik, potrzebujesz obiektu, który będzie służyć jako wewnętrzna blokada dla zsynchronizowanych instrukcji:
Kotlin
// Object for intrinsic lock companion object { val sDataLock = Any() }
Java
// Object for intrinsic lock static final Object sDataLock = new Object();
Następnie twórz instrukcję zsynchronizowaną z tą blokadą za każdym razem, gdy będziesz odczytywać lub zapisywać pliki. Na przykład takie zsynchronizowane polecenie zapisu w pliku najnowszego wyniku w grze:
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"); }
Wyciągi czytania powinny być synchronizowane z tą samą kłódką.
Następnie w BackupAgentHelper
musisz zastąpić zasady onBackup()
i onRestore()
, aby synchronizować operacje tworzenia i przywracania kopii zapasowej z tą samą blokadą wewnętrzną. Na przykład podany wyżej przykład MyFileBackupAgent
wymaga tych metod:
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); } }
Rozszerz agenta kopii zapasowej
Większość aplikacji nie powinna wymagać bezpośredniego rozszerzania klasy BackupAgent
. Zamiast tego rozszerz BackupAgentHelper
, aby korzystać z wbudowanych klas pomocniczych, które automatycznie tworzą kopie zapasowe plików i przywracają je.
Możesz jednak rozszerzyć adres BackupAgent
bezpośrednio o te funkcje:
- Wersja formatu danych: Jeśli na przykład przewidujesz konieczność zmiany formatu zapisu danych aplikacji, możesz utworzyć agenta zapasowego, który będzie sprawdzał wersję aplikacji podczas operacji przywracania i wykonał niezbędne działania w zakresie zgodności, jeśli wersja na urządzeniu różni się od wersji na kopii zapasowej. Więcej informacji znajdziesz w sekcji Sprawdzanie wersji danych do przywracania.
- Określanie części danych, których kopię zapasową chcesz utworzyć Zamiast tworzyć kopię zapasową całego pliku, możesz określić, które z danych plików mają być zapisywane w kopii zapasowej oraz w jaki sposób mają być one przywracane na urządzeniu. Może to też ułatwić zarządzanie różnymi wersjami, ponieważ dane są odczytywane i zapisywane jako niepowtarzalne elementy, a nie pełne pliki.
- Tworzenie kopii zapasowej danych w bazie danych. Jeśli masz bazę danych SQLite, którą chcesz przywrócić, gdy użytkownik ponownie zainstaluje Twoją aplikację, musisz utworzyć niestandardową instancję
BackupAgent
, która będzie odczytywać odpowiednie dane podczas operacji tworzenia kopii zapasowej, a potem utworzyć tabelę i wstawić do niej dane podczas operacji przywracania.
Jeśli nie musisz wykonywać żadnej z powyższych czynności, a chcesz tworzyć kopie zapasowe wszystkich plików z usługi SharedPreferences
lub pamięci wewnętrznej, zapoznaj się z artykułem Przedłużanie dostępu BackupAgentHelper
.
Wymagane metody
Podczas tworzenia BackupAgent
musisz zaimplementować te metody wywołania zwrotnego:
onBackup()
- Menedżer kopii zapasowych wywołuje tę metodę, gdy prosisz o utworzenie kopii zapasowej. Ta metoda polega na odczytywaniu danych aplikacji z urządzenia i przekazywaniu tych, których kopie zapasowe chcesz utworzyć w Menedżerze kopii zapasowych, zgodnie z opisem w sekcji Wykonywanie kopii zapasowej.
onRestore()
Menedżer kopii zapasowych wywołuje tę metodę podczas operacji przywracania. Ta metoda dostarcza dane kopii zapasowej, których aplikacja może użyć do przywrócenia jej poprzedniego stanu zgodnie z opisem w sekcji Przywracanie.
System wywołuje tę metodę, aby przywrócić dane kopii zapasowej, gdy użytkownik ponownie zainstaluje aplikację. Aplikacja może też poprosić o przywrócenie danych.
Utwórz kopię zapasową
Żądanie utworzenia kopii zapasowej nie powoduje natychmiastowego wywołania metody onBackup()
. Zamiast tego czeka na odpowiedni czas, a potem tworzy kopię zapasową wszystkich aplikacji, które zażądały utworzenia kopii zapasowej od czasu utworzenia ostatniej kopii zapasowej. Na tym etapie musisz przekazać dane aplikacji do Menedżera kopii zapasowych,
aby można je było zapisać w chmurze.
Tylko Menedżer kopii zapasowych może wywoływać metodę onBackup()
agenta zapasowego. Za każdym razem, gdy dane aplikacji ulegną zmianie i chcesz utworzyć kopię zapasową, musisz wysłać żądanie wykonania operacji tworzenia kopii zapasowej, wywołując metodę dataChanged()
.
Więcej informacji znajdziesz w artykule Przesyłanie prośby o kopię zapasową.
Wskazówka: podczas tworzenia aplikacji możesz zainicjować natychmiastową operację tworzenia 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, dostępna tylko do odczytu wartość
ParcelFileDescriptor
wskazująca stan ostatniej kopii zapasowej przez Twoją aplikację. Nie jest to dane kopii zapasowej z pamięci w chmurze, ale lokalna reprezentacja danych, które zostały zapisane przy ostatnim wywołaniu usługionBackup()
, zgodnie z definicją wnewState
lubonRestore()
.onRestore()
został omówiony w następnej sekcji. Ponieważ właściwośćonBackup()
nie pozwala na odczytywanie danych z kopii zapasowej w chmurze, możesz użyć tej lokalnej reprezentacji, aby określić, czy dane zmieniły się od czasu utworzenia ostatniej kopii zapasowej. data
- Obiekt
BackupDataOutput
, który służy do przesyłania danych kopii zapasowej do Menedżera kopii zapasowych. newState
- Otwarty plik
ParcelFileDescriptor
do odczytu i zapisu, który wskazuje plik, w którym musisz umieścić reprezentację danych przesłanych do usługidata
. Może to być sygnatura czasowa ostatniej modyfikacji pliku. Ten obiekt jest zwracany jakooldState
, gdy następnym razem Menedżer kopii zapasowych wywoła Twoją metodęonBackup()
. Jeśli nie zapiszesz danych kopii zapasowej w usłudzenewState
, następnym razem, gdy Menedżer kopii zapasowych wywołaonBackup()
,oldState
wskaże pusty plik.
Za pomocą tych parametrów zaimplementuj metodę onBackup()
, aby:
Sprawdź, czy dane zmieniły się od czasu utworzenia ostatniej kopii zapasowej, porównując
oldState
z danymi bieżącymi. Sposób odczytu danych w usłudzeoldState
zależy od tego, jak zostały pierwotnie zapisane w usłudzenewState
(patrz krok 3). Najłatwiejszym sposobem rejestrowania stanu pliku jest podanie sygnatury czasowej ostatniej modyfikacji. Na przykład w ten sposób możesz odczytać i porównać sygnaturę czasową z tabelioldState
: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.
Jeśli Twoje dane uległy zmianie w porównaniu z
oldState
, zapisz bieżące dane w usłudzedata
, aby utworzyć ich kopię zapasową w chmurze.Każdy fragment danych musisz zapisać jako encję w komponencie
BackupDataOutput
. Element to spłaszczony rekord danych binarnych identyfikowany przez unikalny ciąg klucza. Oznacza to, że kopia zapasowa tworzonego przez Ciebie zbioru danych jest koncepcyjnie zbiorem par klucz-wartość.Aby dodać encję do zbioru danych kopii zapasowej:
Wywołaj metodę
writeEntityHeader()
, która przekazuje unikalny klucz ciągu znaków odpowiadający danym, które zamierzasz zapisać, oraz rozmiarowi danych.Wywołaj metodę
writeEntityData()
, aby przekazać bufor zawierający Twoje dane oraz liczbę bajtów do zapisania z bufora. Powinna ona odpowiadać rozmiarowi przekazanemu dowriteEntityHeader()
.
Na przykład ten kod dzieli część danych do strumienia bajtów i zapisuje je w jedną encję:
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 tę czynność dla każdego fragmentu danych, którego kopię zapasową chcesz utworzyć. Sposób podziału danych na encje zależy tylko od Ciebie. Możesz nawet użyć tylko jednego elementu.
Niezależnie od tego, czy utworzysz kopię zapasową (w kroku 2), zapisz bieżące dane w interfejsie
newState
ParcelFileDescriptor
. Menedżer kopii zapasowych przechowuje ten obiekt lokalnie jako reprezentację danych, które mają obecnie kopię zapasową. Przekazuje go z powrotem do Ciebie jakooldState
przy następnym wywołaniu funkcjionBackup()
, dzięki czemu możesz określić, czy potrzebna jest nowa kopia zapasowa, jak opisano w kroku 1. Jeśli nie zapiszesz w tym pliku bieżącego stanu danych, poleoldState
będzie puste podczas następnego wywołania zwrotnego.Ten przykład umożliwia zapisanie reprezentacji bieżących danych w elemencie
newState
za pomocą sygnatury czasowej ostatniej modyfikacji 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 przywrócenia danych aplikacji, Menedżer kopii zapasowych wywoła metodę onRestore()
agenta zapasowego. Wywołując tę metodę, Menedżer kopii zapasowej
dostarcza dane z kopii zapasowej, którą można przywrócić na urządzenie.
Tylko Menedżer kopii zapasowych może wywołać metodę onRestore()
, co następuje automatycznie, gdy system zainstaluje aplikację i znajduje dane kopii zapasowej.
Gdy Menedżer kopii zapasowych wywołuje metodę onRestore()
, przekazuje 3 parametry:
data
- Obiekt
BackupDataInput
, który umożliwia odczyt danych kopii zapasowej. appVersionCode
- Liczba całkowita określająca wartość atrybutu manifestu
android:versionCode
w takiej postaci, w jakiej miała miejsce kopia zapasowa tych danych. Dzięki temu możesz sprawdzić obecną wersję aplikacji i ustalić, czy format danych jest zgodny. Więcej informacji o korzystaniu z tej funkcji do obsługi różnych wersji przywracania danych znajdziesz w sekcji Sprawdzanie przywracania danych. newState
- Otwarta, do odczytu i zapisu
ParcelFileDescriptor
wskazująca plik, w którym musisz wpisać ostateczny stan kopii zapasowej dostarczony zdata
. Ten obiekt jest zwracany jakooldState
przy następnym wywołaniu obiektuonBackup()
. Pamiętaj, że w wywołaniu zwrotnymonBackup()
musisz też zapisać ten sam obiektnewState
. Dzięki temu będziesz mieć pewność, że obiektoldState
przekazany doonBackup()
będzie prawidłowy nawet przy pierwszym wywołaniuonBackup()
po przywróceniu urządzenia.
W swojej implementacji onRestore()
musisz wywołać metodę readNextHeader()
w data
, aby iterować wszystkie encje w zbiorze danych. W przypadku każdego znalezionego elementu wykonaj te czynności:
- Pobierz klucz encji za pomocą
getKey()
. Porównaj klucz encji z listą znanych wartości klucza, które należy zadeklarować jako statyczne ciągi końcowe w klasie
BackupAgent
. Gdy klucz pasuje do jednego ze znanych ciągów kluczy, wpisz instrukcję, aby wyodrębnić dane encji i zapisać je na urządzeniu:- Pobierz rozmiar danych encji za pomocą
getDataSize()
i utwórz tablicę bajtów o tym rozmiarze. - Wywołaj metodę
readEntityData()
i przekaż do niej tablicę bajtów, na którą zostaną przeniesione dane, i określ przesunięcie rozpoczęcia oraz rozmiar do odczytania. - Tablica bajtów jest teraz pełna. Możesz odczytywać dane i zapisywać je w dowolnym momencie na urządzeniu.
- Pobierz rozmiar danych encji za pomocą
Po odczytaniu i zapisaniu danych na urządzeniu zapisz w parametrze
newState
stan swoich danych tak samo jak w przypadku użycia funkcjionBackup()
.
Możesz np. przywrócić dane z kopii zapasowej utworzonej zgodnie z przykładem z 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 onRestore()
nie jest używany. Może Ci się jednak przydać, jeśli masz włączone tworzenie kopii zapasowej, gdy wersja aplikacji cofnięta została przez użytkownika (np. gdy użytkownik przeszedł z wersji 1.5 aplikacji do wersji 1.0). Więcej informacji znajdziesz w następnej sekcji.
Sprawdzanie wersji przywracania danych
Gdy Menedżer kopii zapasowych zapisuje dane w chmurze, automatycznie dołącza wersję aplikacji zgodnie z ustawieniem atrybutu android:versionCode
w pliku manifestu. Zanim Menedżer kopii zapasowych wywoła agenta kopii zapasowej w celu przywrócenia danych, sprawdza android:versionCode
zainstalowanej aplikacji i porównuje ją z wartością zarejestrowaną w zbiorze danych przywracania. Jeśli wersja zarejestrowana w zbiorze danych przywracania jest nowsza niż wersja aplikacji na urządzeniu, oznacza to, że użytkownik zmienił wersję aplikacji na starszą. W takim przypadku Menedżer kopii zapasowych przerwie operację przywracania aplikacji i nie wywoła metody onRestore()
, ponieważ zestaw przywracania jest uznawany za nieznaczący dla 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ę niezależnie od wersji zestawu przywracania. Wartością domyślną jest false
. Jeśli ustawisz tę wartość na true
, Menedżer kopii zapasowych zignoruje android:versionCode
i we wszystkich przypadkach będzie wywoływać metodę onRestore()
. W takim przypadku możesz ręcznie sprawdzić różnicę w wersji metody onRestore()
i podjąć odpowiednie kroki w celu zapewnienia zgodności danych, jeśli wersje się nie zgadzają.
Aby ułatwić obsługę różnych wersji podczas operacji przywracania, metoda onRestore()
przekazuje kod wersji dołączony do zbioru danych przywracania jako parametr appVersionCode
. Następnie możesz wysłać zapytanie o kod wersji bieżącej aplikacji, korzystając z 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 version
z zewnątrz PackageInfo
z danymi appVersionCode
przekazanymi do onRestore()
.
Poproś o kopię zapasową
W każdej chwili możesz poprosić o utworzenie kopii zapasowej, wywołując dataChanged()
. Ta metoda powiadamia Menedżera kopii zapasowych, że chcesz utworzyć kopię zapasową danych przy użyciu agenta kopii zapasowej. Menedżer kopii zapasowych wywoła wtedy metodę onBackup()
agenta kopii zapasowej w przyszłości. Zwykle prośbę o utworzenie kopii zapasowej należy wysłać po każdej zmianie danych (np. gdy użytkownik zmieni ustawienie aplikacji, której kopię zapasową chcesz utworzyć). Jeśli wywołasz dataChanged()
kilka razy, zanim Menedżer kopii zapasowych zażąda utworzenia kopii zapasowej od agenta, agent nadal otrzyma tylko 1 wywołanie do onBackup()
.
Poproś o przywrócenie
W trakcie normalnego życia aplikacji nie musisz prosić o operację przywracania. System automatycznie sprawdza dostępność danych kopii zapasowej i przywraca ją po zainstalowaniu aplikacji.
Migracja do Automatycznej kopii zapasowej
Aby przejść na tworzenie pełnych kopii zapasowych danych, ustaw parametr android:fullBackupOnly
na true
w elemencie <application>
w pliku manifestu. Gdy aplikacja działa na urządzeniu z Androidem 5.1 (poziom interfejsu API 22) lub niższym, ignoruje tę wartość w pliku manifestu i nadal wykonuje kopie zapasowe par klucz-wartość. Jeśli aplikacja działa na urządzeniu z Androidem 6.0 (poziom interfejsu API 23) lub nowszym, zamiast tworzyć kopie zapasowe par klucz-wartość, używa ona Automatycznej kopii zapasowej.
Prywatność użytkowników
W Google doskonale zdajemy sobie sprawę z zaufania, jakim obdarzają nas użytkownicy, i z tego, że mamy obowiązek chronić ich prywatność. Google bezpiecznie przesyła dane kopii zapasowej na serwery Google i z nich, aby zapewnić działanie funkcji tworzenia i przywracania kopii zapasowych. Google traktuje te dane jako dane osobowe zgodnie ze swoją Polityką prywatności.
Oprócz tego użytkownicy mogą wyłączyć tę funkcję w ustawieniach kopii zapasowej systemu Android. Gdy użytkownik wyłączy kopię zapasową, Android Backup Service usunie wszystkie zapisane dane kopii zapasowej. Użytkownik może ponownie włączyć tworzenie kopii zapasowej na urządzeniu, ale Android Backup Service nie przywróci żadnych wcześniej usuniętych danych.