Android2.2以降で使えるようになった、Key-Value形式のバックアップについての詳細と実装方法を解説していきます。
※ この記事では、実装については「BackupAgentHelper」だけ取り上げます。
バックアップファイルの保存場所
Android Backup Serviceを通じてクラウドに保存されます。
Android6.0から導入されたauto backupと違い、Google Driveでは無いです。
上限は5MBまで。
auto backupは25MBまでなので、容量は少ないですが、その分、デフォルトではauto backupはwifi環境に繋がっていないと実行されませんが、key-value形式のバックアップはモバイル通信環境でも実行されます。
実装方法
「BackupAgent」を拡張する方法と、「BackupAgentHelper」を拡張する方法の2種類が用意されています。
「BackupAgentHelper」はBackupAgentのラッパークラスで
- Shared preferencesファイル
- getFilesDir()でアクセスできるアプリ内ファイル
のバックアップにだけ絞り、処理が既に実装されているので、最小限の記述だけでバックアップ機能を導入できます。
この記事ではBackupAgentHelperについてだけ取り上げます。
1. Android Backup Service keyの取得と登録
まずは、以下のページにアクセスして「Android Backup Service Key」を取得します。
https://developer.android.com/google/backup/signup.html
規約に同意して、パッケージ名を入れ、登録ボタンをクリック
登録完了画面下にAndroid Backup Serviceキーが記述された「meta-dataタグ」が表示されるのでコピー
AndroidManifest.xmlの、application直下に追加します。
<application ....... > <meta-data android:name="com.google.android.backup.api_key" android:value="xxxxxxxxxxxxxxxxxxxxx" /> ......
BackupAgentHelperの拡張クラスを登録
BackupAgentHelperの拡張クラスを作って、AndroidManifest.xmlに登録します。
MizutamaBackupAgentHelperとしました。
<application android:backupAgent="util.MizutamaBackupAgentHelper" ....... >
BackupAgentHelperの拡張クラスの実装
BackupAgentHelperの拡張クラスのonCreate内に、バックアップするファイルを記述していきます。
import android.app.backup.BackupAgentHelper import android.app.backup.FileBackupHelper import android.app.backup.SharedPreferencesBackupHelper class MizutamaBackupAgentHelper: BackupAgentHelper() { val PREFS_BACKUP_KEY = "prefs" val FILES_BACKUP_KEY = "files" override fun onCreate() { // SharedPreferencesのバックアップ登録 SharedPreferencesBackupHelper(this, "hogePref1", "hogePref2", "hogePref3").also { addHelper(PREFS_BACKUP_KEY, it) } // アプリ内ファイルのバックアップ登録 FileBackupHelper(this, "hoge.jpg", "hoge.xml", "hoge.mp3").also { addHelper(FILES_BACKUP_KEY, it) } } }
- 「SharedPreferencesBackupHelper」にはバックアップしたいSharedPreferencesのキー名
- 「FileBackupHelper」にはバックアップしたいファイル名
を記述して、addHelperします。
PREFS_BACKUP_KEY、FILES_BACKUP_KEYはBackupAgentHelperが内部処理で使うキーなので適当でいいです。
SharedPreferencesはスレッドセーフなので、SharedPreferencesだけをバックアップ対象とする場合はこれでOKですが、内部ストレージのファイルもバックアップ対象とする場合は、スレッドセーフではないので、バックアップとリストアの処理が同時に発生しないことを保証するために、バックアップとリストア処理をoverrideして同期処理を記述します。
class MizutamaBackupAgentHelper: BackupAgentHelper() { .... companion object { val dataLock = Any() } override fun onCreate() { .... } override fun onBackup( oldState: ParcelFileDescriptor?, data: BackupDataOutput?, newState: ParcelFileDescriptor? ) { synchronized(MizutamaBackupAgentHelper.dataLock) { super.onBackup(oldState, data, newState) } } override fun onRestore( data: BackupDataInput?, appVersionCode: Int, newState: ParcelFileDescriptor? ) { synchronized(MizutamaBackupAgentHelper.dataLock) { super.onRestore(data, appVersionCode, newState) } } }
これで実装は完了です。
バックアップの実行
バックアップをしたいタイミングでBackupManagerのdataChanged()を呼び出すと、バックアップのリクエストがBackup Managerのキューに追加されます。
val backupManager = BackupManager(this) // Context backupManager.dataChanged()
dataChangedが呼ばれてすぐバックアップが実行されるわけでは「ありません」
システムが「適切なタイミングで実行」し、BackupAgentHelper#onBackupが呼び出されます。
つまりコントロールできません….
リストアの実行
基本的にはリストアの呼び出しは必要ありません。
アプリがインストールされたタイミングで、バックアップデータがある場合は自動的にリストアされ、BackupAgentHelper#onRestoreが呼び出されます。
それでもマニュアルで実行したい場合は、BackupManagerのrequestRestore(RestoreObserver)を呼び出します。
val backupManager = BackupManager(this) // Context backupManager.requestRestore(object: RestoreObserver() { override fun restoreStarting(numPackages: Int) { super.restoreStarting(numPackages) // リストア処理が開始 } override fun onUpdate(nowBeingRestored: Int, currentPackage: String?) { super.onUpdate(nowBeingRestored, currentPackage) // パッケージのデータがリストア中 } override fun restoreFinished(error: Int) { super.restoreFinished(error) // リストア処理が完了 } })
リストアデータバージョン
バックアップデータがクラウトに保存される時、自動的にアプリのバージョン番号(AndroidManifestのversionCode)も一緒に保存されます。
そして、バックアップデータのバージョンが、アプリのバージョンより高い場合、リストア処理が中止されます。
AndroidManifest.xmlで「restoreAnyVersion=true」を指定することでバージョンにかかわらずリストア処理を実行させる事ができます。
<application android:restoreAnyVersion="true" ...... >
BackupAgentHelper#onRestoreの第二引数で、バックアップデータに紐付けられているバージョン番号が送られてくるので、restoreAnyVersion=true、として常にリストア処理が実行されるようにし、onRestore内で、ユーザーにアプリの最新バージョンを促すようアラートを出すのがベターだと思います。
参考資料
Android developers
「Data backup overview」
https://developer.android.com/guide/topics/data/backup
「Back up key-value pairs with Android Backup Service」
https://developer.android.com/guide/topics/data/keyvaluebackup.html
Googleのサンプルソース
BackupRestore
https://android.googlesource.com/platform/development/+/0b3758ea4e53f9bfd0b112eaa4a7dd7b7f4040f5/samples/BackupRestore
twitterでも開発情報をつぶやいていますので、フォローお願いします。