Android Backup Service cung cấp tính năng sao lưu và khôi phục bộ nhớ trên đám mây cho dữ liệu khoá-giá trị trong ứng dụng Android của bạn. Trong hoạt động sao lưu khoá-giá trị, dữ liệu sao lưu của ứng dụng sẽ được chuyển đến công cụ sao lưu của thiết bị. Nếu thiết bị đang sử dụng công cụ sao lưu mặc định của Google, thì dữ liệu sẽ được chuyển vào Android Backup Service để lưu trữ.
Giới hạn dữ liệu là 5 MB cho mỗi người dùng ứng dụng của bạn. Bạn sẽ không phải trả phí cho việc lưu trữ dữ liệu sao lưu.
Để biết tổng quan về các tuỳ chọn sao lưu của Android và hướng dẫn về dữ liệu bạn cần sao lưu và khôi phục, hãy xem Tổng quan về sao lưu dữ liệu.
Triển khai sao lưu khoá-giá trị
Để sao lưu dữ liệu ứng dụng, bạn cần triển khai một tác nhân sao lưu. Tác nhân sao lưu của bạn sẽ được Trình quản lý sao lưu gọi ra trong cả quá trình sao lưu và khôi phục.
Để triển khai tác nhân sao lưu, bạn phải:
Khai báo tác nhân sao lưu trong tệp kê khai với thuộc tính
android:backupAgent
.Xác định một tác nhân sao lưu bằng cách thực hiện một trong những thao tác sau:
-
Lớp
BackupAgent
cung cấp giao diện trung tâm mà ứng dụng của bạn sử dụng để giao tiếp với Trình quản lý sao lưu. Nếu trực tiếp mở rộng lớp này, bạn phải ghi đèonBackup()
vàonRestore()
để xử lý bản sao lưu và khôi phục hoạt động cho dữ liệu của bạn. -
Lớp
BackupAgentHelper
cung cấp một trình bao bọc tiện lợi xung quanh lớpBackupAgent
, nhằm giảm thiểu số lượng mã bạn cần viết. TrongBackupAgentHelper
, bạn phải sử dụng một hoặc nhiều đối tượng trợ giúp. Các đối tượng này tự động sao lưu và khôi phục một số loại dữ liệu nhất định để bạn không cần triển khaionBackup()
vàonRestore()
. Bạn nên sử dụngBackupAgentHelper
để xử lý các bản sao lưu của ứng dụng, trừ phi cần có toàn quyền kiểm soát các bản sao lưu của ứng dụng.Android hiện cung cấp các trình trợ giúp sao lưu để sao lưu và khôi phục các tệp hoàn chỉnh từ
SharedPreferences
và bộ nhớ trong.
-
Khai báo tác nhân sao lưu trong tệp kê khai
Sau khi bạn quyết định tên lớp cho tác nhân sao lưu, hãy khai báo tên đó trong
tệp kê khai sử dụng thuộc tính android:backupAgent
trong thẻ
<application>
.
Ví dụ:
<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>
Để hỗ trợ các thiết bị cũ, bạn nên thêm khoá API <meta-data>
vào tệp kê khai Android của mình. Dịch vụ sao lưu của Android không còn yêu cầu
khoá dịch vụ nữa, nhưng một số thiết bị cũ vẫn có thể kiểm tra khoá khi
sao lưu. Hãy đặt android:name
thành com.google.android.backup.api_key
và
android:value
thành unused
.
Thuộc tính
android:restoreAnyVersion
nhận giá trị boolean cho biết bạn có muốn khôi phục dữ liệu
ứng dụng hay không bất kể phiên bản ứng dụng hiện tại so với phiên bản
đã tạo dữ liệu sao lưu. Giá trị mặc định là false
. Hãy xem Kiểm tra phiên bản
khôi phục dữ liệu để biết thêm thông tin.
Mở rộng BackupAgentHelper
Bạn nên tạo tác nhân sao lưu sử dụng BackupAgentHelper
nếu muốn
sao lưu các tệp hoàn chỉnh từ SharedPreferences
hoặc bộ nhớ trong.
Việc tạo tác nhân sao lưu bằng BackupAgentHelper
yêu cầu ít mã hơn so với
việc mở rộng BackupAgent
vì bạn không phải triển khai onBackup()
và
onRestore()
.
Việc triển khai BackupAgentHelper
phải sử dụng một hoặc nhiều trình trợ giúp sao lưu.
Trình trợ giúp sao lưu là một thành phần đặc biệt giúp BackupAgentHelper
triệu tập để
thực hiện các hoạt động sao lưu và khôi phục cho một loại dữ liệu cụ thể. Khung
Android hiện cung cấp 2 trình trợ giúp:
SharedPreferencesBackupHelper
để sao lưu tệpSharedPreferences
.FileBackupHelper
để sao lưu các tệp từ bộ nhớ trong.
Bạn có thể đưa nhiều trình trợ giúp vào BackupAgentHelper
, nhưng chỉ cần một
trình trợ giúp cho mỗi loại dữ liệu. Nghĩa là nếu có nhiều
SharedPreferences
tệp, thì bạn chỉ cần một
SharedPreferencesBackupHelper
.
Đối với mỗi trình trợ giúp mà bạn muốn thêm vào BackupAgentHelper
, hãy làm
theo các bước sau trong phương thức
onCreate()
:
- Tạo bản sao lớp trình trợ giúp mong muốn. Trong hàm khởi tạo lớp, bạn phải chỉ định (các) tệp muốn sao lưu.
- Gọi
addHelper()
để thêm trình trợ giúp vàoBackupAgentHelper
.
Các phần sau đây mô tả cách tạo tác nhân sao lưu sử dụng từng trình trợ giúp có sẵn.
Sao lưu SharedPreferences
Khi tạo SharedPreferencesBackupHelper
, bạn phải bao gồm
tên của một hoặc nhiều tệp SharedPreferences
.
Ví dụ: để sao lưu tệp SharedPreferences
có tên user_preferences
,
một tác nhân sao lưu hoàn chỉnh sử dụng BackupAgentHelper
sẽ có dạng như sau:
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
bao gồm tất cả mã cần thiết để sao lưu và
khôi phục tệp SharedPreferences
.
Khi Trình quản lý sao lưu gọi onBackup()
và onRestore()
,
BackupAgentHelper
sẽ gọi ra trình trợ giúp sao lưu của bạn để sao lưu và khôi phục
các tệp đã chỉ định.
Sao lưu các tệp khác
Khi tạo một FileBackupHelper
, bạn phải bao gồm tên của một hoặc
nhiều tệp được lưu vào bộ nhớ trong của ứng dụng, như được chỉ định bởi
getFilesDir()
ở cùng vị trí mà
openFileOutput()
ghi tệp.
Ví dụ: để sao lưu hai tệp có tên scores
và stats
, một tác nhân sao lưu
sử dụng BackupAgentHelper
sẽ có dạng như sau:
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
bao gồm tất cả mã cần thiết để sao lưu và khôi phục
các tệp được lưu vào bộ nhớ trong của ứng dụng.
Tuy nhiên, việc đọc và ghi vào tệp trên bộ nhớ trong là không an toàn theo luồng. Để đảm bảo tác nhân sao lưu của bạn không đọc hoặc ghi các tệp cùng một lúc với các hoạt động, bạn phải sử dụng các câu lệnh được đồng bộ hoá mỗi khi thực hiện đọc hoặc ghi. Ví dụ: trong bất kỳ hoạt động nào mà bạn đọc và ghi tệp, bạn cần có đối tượng để sử dụng làm khoá nội tại cho các câu lệnh được đồng bộ hoá:
Kotlin
// Object for intrinsic lock companion object { val sDataLock = Any() }
Java
// Object for intrinsic lock static final Object sDataLock = new Object();
Sau đó, tạo một câu lệnh được đồng bộ hoá bằng kiểu khoá này mỗi khi bạn đọc hoặc ghi các tệp. Ví dụ: đây là câu lệnh được đồng bộ hoá để ghi điểm số mới nhất trong một trò chơi vào một tệp:
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"); }
Bạn nên đồng bộ hoá các câu lệnh đã đọc bằng cùng một khoá.
Sau đó, trong BackupAgentHelper
, bạn phải ghi đè onBackup()
và
onRestore()
để đồng bộ hoá các hoạt động sao lưu và khôi phục với cùng một khoá nội tại. Ví dụ: điểm dữ liệu MyFileBackupAgent
ở trên cần
các phương thức sau:
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); } }
Mở rộng BackupAgent
Hầu hết các ứng dụng không cần phải mở rộng trực tiếp lớp BackupAgent
, mà thay vào đó nên
mở rộng BackupAgentHelper
để tận dụng các lớp trình trợ giúp tích hợp sẵn sẽ tự động sao lưu và khôi phục các tệp của bạn.
Tuy nhiên, bạn có thể trực tiếp mở rộng BackupAgent
để thực hiện những việc sau:
- Định phiên bản định dạng dữ liệu của bạn. Chẳng hạn, nếu dự đoán nhu cầu sửa đổi định dạng mà bạn ghi dữ liệu ứng dụng, bạn có thể tạo tác nhân sao lưu để kiểm tra chéo phiên bản ứng dụng trong suốt hoạt động khôi phục và thực hiện bất kỳ công việc có khả năng tương thích nếu phiên bản trên thiết bị khác với phiên bản của dữ liệu sao lưu. Để biết thêm thông tin, hãy xem Kiểm tra phiên bản dữ liệu khôi phục.
- Chỉ định các phần dữ liệu cần sao lưu. Thay vì sao lưu toàn bộ tệp, bạn có thể chỉ định các phần dữ liệu để sao lưu và cách mỗi phần sau đó được khôi phục vào thiết bị. Điều này cũng có thể giúp bạn quản lý nhiều phiên bản vì bạn đọc và ghi dữ liệu của mình dưới dạng các thực thể duy nhất, thay vì hoàn thiện các tệp.
- Sao lưu dữ liệu trong cơ sở dữ liệu. Nếu bạn có cơ sở dữ liệu SQLite muốn
khôi phục khi người dùng cài đặt lại ứng dụng của bạn, hãy tạo
BackupAgent
tuỳ chỉnh để đọc dữ liệu thích hợp trong hoạt động sao lưu, sau đó tạo bảng và chèn dữ liệu trong hoạt động khôi phục.
Nếu bạn không cần thực hiện bất kỳ thao tác nào ở trên và muốn sao lưu các tệp hoàn chỉnh từ SharedPreferences
hoặc bộ nhớ trong, hãy xem Mở rộng
BackupAgentHelper
.
Phương thức bắt buộc
Khi tạo BackupAgent
, bạn phải triển khai các phương pháp gọi lại sau:
onBackup()
- Trình quản lý sao lưu gọi phương thức này sau khi bạn yêu cầu sao lưu. Trong phương thức này, bạn sẽ đọc dữ liệu ứng dụng của mình từ thiết bị và chuyển dữ liệu bạn muốn sao lưu vào Trình quản lý sao lưu, như mô tả trong phần Sao lưu.
onRestore()
Trình quản lý sao lưu gọi phương thức này trong hoạt động khôi phục. Phương thức này cung cấp dữ liệu sao lưu của bạn. Ứng dụng của bạn có thể sử dụng dữ liệu này để khôi phục trạng thái trước đây, như mô tả trong phần Khôi phục.
Hệ thống gọi phương thức này để khôi phục mọi dữ liệu sao lưu khi người dùng cài đặt lại ứng dụng, nhưng ứng dụng của bạn cũng có thể yêu cầu khôi phục.
Sao lưu
Yêu cầu sao lưu không dẫn đến một lệnh gọi ngay đến phương thức
onBackup()
. Thay vào đó, Trình quản lý sao lưu sẽ chờ một thời gian thích hợp, sau đó
sao lưu dữ liệu cho tất cả các ứng dụng đã yêu cầu sao lưu kể từ
lần sao lưu gần đây nhất. Đây là thời điểm bạn phải cung cấp dữ liệu ứng dụng của mình
cho Trình quản lý sao lưu để có thể lưu dữ liệu vào bộ nhớ trên đám mây.
Chỉ Trình quản lý sao lưu mới có thể gọi phương thức onBackup()
của tác nhân sao lưu. Mỗi
khi dữ liệu ứng dụng của bạn thay đổi và bạn muốn sao lưu, bạn phải
yêu cầu hoạt động sao lưu bằng cách gọi
dataChanged()
.
Hãy xem phần Yêu cầu sao lưu để biết thêm thông tin.
Mẹo: Trong khi phát triển ứng dụng, bạn có thể bắt đầu hoạt động sao lưu
ngay lập tức từ Trình quản lý sao lưu bằng công cụ
bmgr
.
Khi gọi phương thức onBackup()
, Trình quản lý sao lưu sẽ chuyển
3 thông số:
oldState
-
ParcelFileDescriptor
mở, ở chế độ chỉ đọc, trỏ đến trạng thái sao lưu gần nhất do ứng dụng cung cấp. Đây không phải là dữ liệu sao lưu từ bộ nhớ trên đám mây, mà là biểu diễn cục bộ về dữ liệu được sao lưu vào lần trướconBackup()
được gọi, như đượcnewState
xác định hoặc từonRestore()
.onRestore()
được đề cập trong phần tiếp theo. DoonBackup()
không cho phép bạn đọc dữ liệu sao lưu hiện có trong bộ nhớ trên đám mây, nên bạn có thể sử dụng biểu diễn cục bộ này để xác định xem dữ liệu của mình đã thay đổi kể từ lần sao lưu gần đây nhất hay chưa. data
- Đối tượng
BackupDataOutput
mà bạn sử dụng để gửi dữ liệu sao lưu đến Trình quản lý sao lưu. newState
ParcelFileDescriptor
mở, ở chế độ đọc/ghi, trỏ đến tệp mà bạn phải ghi nội dung biểu diễn cho dữ liệu mà bạn đã cung cấp chodata
. Nội dung biểu diễn có thể đơn giản như dấu thời gian sửa đổi gần đây nhất của tệp. Đối tượng này được trả về dưới dạngoldState
vào lần tiếp theo mà Trình quản lý sao lưu gọi phương thứconBackup()
. Nếu bạn không ghi dữ liệu sao lưu của mình vàonewState
, thìoldState
sẽ trỏ đến một tệp trống vào lần tiếp theo Trình quản lý sao lưu gọionBackup()
.
Bằng cách sử dụng các thông số này, hãy triển khai phương thức onBackup()
của bạn để thực hiện những việc sau:
Kiểm tra xem dữ liệu của bạn đã thay đổi kể từ lần sao lưu gần đây nhất hay chưa bằng cách so sánh
oldState
với dữ liệu hiện tại. Cách đọc dữ liệu trongoldState
tuỳ thuộc vào cách ban đầu bạn ghi dữ liệu vàonewState
(xem bước 3). Cách dễ nhất để ghi lại trạng thái của tệp là sử dụng dấu thời gian được sửa đổi lần cuối. Ví dụ: dưới đây là cách bạn có thể đọc và so sánh dấu thời gian từ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 }
Nếu không có gì thay đổi và bạn không cần sao lưu, hãy chuyển sang bước 3.
Nếu dữ liệu của bạn đã thay đổi, so với
oldState
, hãy ghi dữ liệu hiện tại vàodata
để sao lưu dữ liệu vào bộ nhớ trên đám mây.Bạn phải viết từng phần dữ liệu dưới dạng thực thể trong
BackupDataOutput
. Thực thể là một bản ghi dữ liệu nhị phân phẳng được xác định bởi một chuỗi khoá duy nhất. Do đó, về mặt nhận thức, tập dữ liệu mà bạn sao lưu là một tập hợp các cặp khoá-giá trị.Để thêm một thực thể vào tập dữ liệu sao lưu, bạn phải:
Gọi
writeEntityHeader()
, chuyển khoá chuỗi duy nhất cho dữ liệu bạn sắp ghi và kích thước dữ liệu.Gọi
writeEntityData()
, chuyển vùng đệm byte chứa dữ liệu của bạn và số byte cần ghi từ vùng đệm và cần khớp với kích thước được chuyển đếnwriteEntityHeader()
.
Ví dụ: mã sau đây làm phẳng một số dữ liệu thành luồng byte và ghi dữ liệu đó vào một thực thể duy nhấ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);
Thực hiện thao tác này cho từng phần dữ liệu mà bạn muốn sao lưu. Việc bạn phân chia dữ liệu thành các thực thể như thế nào là do bạn quyết định. Thậm chí, bạn chỉ có thể sử dụng một thực thể.
Dù bạn có thực hiện sao lưu hay không (ở bước 2), hãy ghi nội dung biểu diễn dữ liệu hiện tại vào
newState
ParcelFileDescriptor
. Trình quản lý sao lưu sẽ giữ lại đối tượng này trong cục bộ dưới dạng nội dung biểu diễn dữ liệu hiện đã được sao lưu. Trình này sẽ chuyển lại cho bạn dưới dạngoldState
vào lần tới gọionBackup()
để bạn có thể xác định xem liệu một bản sao lưu khác có cần thiết hay không, như đã xử lý ở bước 1. Nếu bạn không ghi trạng thái dữ liệu hiện tại vào tệp này, thìoldState
sẽ trống trong lần gọi lại tiếp theo.Ví dụ sau đây sẽ lưu nội dung biểu diễn dữ liệu hiện tại vào
newState
sử dụng dấu thời gian sửa đổi gần đây nhất của tệp: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);
Khôi phục
Khi đến thời điểm khôi phục dữ liệu ứng dụng, Trình quản lý sao lưu sẽ gọi
phương thức onRestore()
của tác nhân sao lưu. Khi gọi phương thức này, Trình quản lý sao lưu
sẽ phân phối dữ liệu sao lưu của bạn để bạn có thể khôi phục dữ liệu đó trên thiết bị.
Chỉ Trình quản lý sao lưu mới có thể gọi onRestore()
. Quá trình này tự động diễn ra
khi hệ thống cài đặt ứng dụng của bạn và tìm dữ liệu sao lưu hiện có.
Khi gọi phương thức onRestore()
, Trình quản lý sao lưu sẽ chuyển 3
thông số:
data
- Đối tượng
BackupDataInput
, cho phép bạn đọc dữ liệu sao lưu. appVersionCode
- Một số nguyên thể hiện giá trị của thuộc tính tệp kê khai
android:versionCode
của ứng dụng, giống như khi bạn sao lưu dữ liệu này. Bạn có thể sử dụng số này để kiểm tra chéo phiên bản ứng dụng hiện tại và xác định xem định dạng dữ liệu có tương thích hay không. Để biết thêm thông tin về cách sử dụng phiên bản này để xử lý nhiều phiên bản dữ liệu khôi phục, hãy xem Kiểm tra phiên bản khôi phục dữ liệu. newState
ParcelFileDescriptor
mở, ở chế độ đọc/ghi, trỏ đến tệp mà bạn phải ghi trạng thái sao lưu cuối cùng được cung cấp kèm theodata
. Đối tượng này được trả về dưới dạngoldState
vào lần tiếp theo gọionBackup()
. Xin lưu ý rằng bạn cũng phải ghi cùng đối tượngnewState
trong lệnh gọi lạionBackup()
và thực hiện việc này ở đây đảm bảo rằng đối tượngoldState
được cấp choonBackup()
là hợp lệ ngay từ lần đầu tiên gọionBackup()
sau khi thiết bị được khôi phục.
Khi triển khai onRestore()
, bạn nên gọi
readNextHeader()
trên data
để lặp lại tất cả các thực thể trong tập dữ liệu. Đối với mỗi thực thể
tìm thấy, hãy làm như sau:
- Nhận khoá thực thể bằng
getKey()
. So sánh khoá thực thể với danh sách các khoá đã biết mà bạn nên khai báo là chuỗi cuối cùng tĩnh bên trong lớp
BackupAgent
. Khi khoá khớp với một trong các chuỗi khoá đã biết, hãy nhập câu lệnh để trích xuất dữ liệu thực thể và lưu vào thiết bị:- Tải kích thước dữ liệu thực thể bằng
getDataSize()
và tạo một mảng byte có kích thước đó. - Gọi
readEntityData()
, chuyển nó vào mảng byte (đây là nơi dữ liệu sẽ được chuyển đi) và chỉ định đích bù trừ bắt đầu và kích thước cần đọc. - Mảng byte của bạn hiện đã đầy. Đọc dữ liệu và ghi vào thiết bị theo cách bạn muốn.
- Tải kích thước dữ liệu thực thể bằng
Sau khi bạn đọc và ghi dữ liệu trở lại thiết bị, hãy ghi trạng thái của dữ liệu vào thông số
newState
giống như khi bạn thực hiện trongonBackup()
.
Ví dụ: dưới đây là cách bạn có thể khôi phục dữ liệu đã được sao lưu theo điểm dữ liệu ở phần trước:
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); }
Trong ví dụ này, thông số appVersionCode
được chuyển đến onRestore()
không
được sử dụng. Tuy nhiên, bạn có thể muốn sử dụng thông số này nếu bạn đã chọn thực hiện sao lưu
khi phiên bản ứng dụng của người dùng đã thực sự di chuyển lùi (ví dụ:
người dùng chuyển từ phiên bản 1.5 của ứng dụng sang 1.0). Để biết thêm thông tin, hãy xem
phần tiếp theo.
Kiểm tra phiên bản dữ liệu khôi phục
Khi Trình quản lý sao lưu lưu dữ liệu vào bộ nhớ trên đám mây, nó sẽ tự động
bao gồm phiên bản ứng dụng của bạn như được xác định bởi thuộc tính android:versionCode
của tệp kê khai. Trước khi Trình quản lý sao lưu gọi tác nhân sao lưu
để khôi phục dữ liệu, trình này sẽ xem android:versionCode
của
ứng dụng đã cài đặt và so sánh với giá trị đã ghi trong tập dữ liệu khôi phục. Nếu
phiên bản được ghi lại trong tập dữ liệu khôi phục mới hơn phiên bản ứng dụng trên
thiết bị, thì người dùng đã hạ cấp ứng dụng của họ. Trong trường hợp này, Trình quản lý sao lưu
sẽ huỷ hoạt động khôi phục cho ứng dụng của bạn và không gọi phương thức
onRestore()
, vì tập dữ liệu khôi phục được xem là không có ý nghĩa đối với
phiên bản cũ.
Bạn có thể ghi đè hành vi này bằng thuộc tính android:restoreAnyVersion
.
Đặt thuộc tính này thành true
để cho biết bạn muốn khôi phục ứng dụng
bất kể phiên bản của tập dữ liệu khôi phục. Giá trị mặc định là false
. Nếu bạn
đặt giá trị này thành true
thì Trình quản lý sao lưu sẽ bỏ qua
android:versionCode
và gọi phương thức onRestore()
trong mọi trường hợp. Khi làm
vậy, bạn có thể kiểm tra sự khác biệt về phiên bản theo cách thủ công trong phương thức onRestore()
và thực hiện mọi bước cần thiết để làm cho dữ liệu tương thích nếu các phiên bản
không khớp.
Để giúp bạn xử lý nhiều phiên bản trong hoạt động khôi phục, phương thức
onRestore()
sẽ chuyển cho bạn mã phiên bản có trong tập
dữ liệu khôi phục dưới dạng thông số appVersionCode
. Sau đó, bạn có thể truy vấn mã phiên bản
của ứng dụng hiện tại bằng trường
PackageInfo.versionCode
. Ví dụ:
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; }
Sau đó, so sánh version
thu được từ
PackageInfo
với
appVersionCode
được chuyển vào onRestore()
.
Yêu cầu sao lưu
Bạn có thể yêu cầu một hoạt động sao lưu bất cứ lúc nào bằng cách gọi dataChanged()
. Phương thức này
thông báo cho Trình quản lý sao lưu rằng bạn muốn sao lưu dữ liệu sử dụng
tác nhân sao lưu của mình. Sau đó, Trình quản lý sao lưu sẽ gọi phương thức
onBackup()
của tác nhân sao lưu trong tương lai. Thông thường, bạn nên yêu cầu
sao lưu mỗi khi dữ liệu thay đổi (chẳng hạn như khi người dùng thay đổi lựa chọn ưu tiên về ứng dụng
mà bạn muốn sao lưu). Nếu bạn gọi dataChanged()
nhiều lần
trước khi Trình quản lý sao lưu yêu cầu một bản sao lưu từ tác nhân của bạn, thì tác nhân
sẽ chỉ nhận một lệnh gọi đến onBackup()
.
Yêu cầu khôi phục
Trong thời gian hoạt động bình thường của ứng dụng, bạn không cần yêu cầu hoạt động khôi phục. Hệ thống tự động kiểm tra dữ liệu sao lưu và thực hiện khôi phục khi ứng dụng của bạn được cài đặt.
Chuyển sang tính năng Tự động sao lưu
Bạn có thể chuyển đổi ứng dụng của mình sang bản sao lưu toàn bộ dữ liệu bằng cách đặt
android:fullBackupOnly
thành true
trong phần tử <application>
của tệp kê khai. Khi chạy
trên thiết bị chạy Android 5.1 (API cấp 22) trở xuống, ứng dụng sẽ bỏ qua
giá trị này trong tệp kê khai và tiếp tục thực hiện sao lưu khoá-giá trị. Khi chạy
trên thiết bị chạy Android 6.0 (API cấp 23) trở lên,
ứng dụng sẽ thực hiện sao lưu Tự động sao lưu thay vì sao lưu khoá-giá trị.
Quyền riêng tư của người dùng
Tại Google, chúng tôi thấu hiểu sự tin tưởng của người dùng đối với chúng tôi và trách nhiệm của chúng tôi trong việc bảo vệ quyền riêng tư của người dùng. Google truyền dữ liệu sao lưu tới và từ các máy chủ của Google một cách an toàn để cung cấp các tính năng sao lưu và khôi phục. Google xử lý dữ liệu này như thông tin cá nhân theo Chính sách quyền riêng tư của Google.
Ngoài ra, người dùng có thể tắt chức năng sao lưu dữ liệu thông qua phần cài đặt sao lưu của hệ thống Android. Khi người dùng tắt tính năng sao lưu, Android Backup Service sẽ xoá tất cả dữ liệu sao lưu đã lưu. Người dùng có thể bật lại tính năng sao lưu trên thiết bị, nhưng Android Backup Service sẽ không khôi phục bất kỳ dữ liệu nào đã bị xoá trước đó.