Android

Key-Value形式のバックアップ機能詳細

Android2.2以降で使えるようになった、Key-Value形式のバックアップについての詳細と実装方法を解説していきます。

※ この記事では、実装については「BackupAgentHelper」だけ取り上げます。

Android自動バックアップ(Auto Backup for Apps)詳細Android 6.0から、特別な実装なしで自動バックアップを実現する機能「Auto Backup for Apps」が追加されました。...

 

バックアップファイルの保存場所

Android Backup Serviceを通じてクラウドに保存されます。
Android6.0から導入されたauto backupと違い、Google Driveでは無いです。

上限は5MBまで。

auto backupは25MBまでなので、容量は少ないですが、その分、デフォルトではauto backupはwifi環境に繋がっていないと実行されませんが、key-value形式のバックアップはモバイル通信環境でも実行されます

 

実装方法

「BackupAgent」を拡張する方法と、「BackupAgentHelper」を拡張する方法の2種類が用意されています。

「BackupAgentHelper」はBackupAgentのラッパークラスで

  1. Shared preferencesファイル
  2. 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_KEYFILES_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自動バックアップ(Auto Backup for Apps)詳細Android 6.0から、特別な実装なしで自動バックアップを実現する機能「Auto Backup for Apps」が追加されました。...

 

参考資料

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でも開発情報をつぶやいていますので、フォローお願いします。