Android Backup Service を使用して Key-Value ペアをバックアップする

Android Backup Service によって、Android アプリ内の Key-Value データについて、クラウド ストレージでのバックアップと復元が可能になります。Key-Value バックアップの実行時に、アプリのバックアップ データはデバイスのバックアップ トランスポートに渡されます。デバイスがデフォルトの Google Backup Transport を使用している場合、データは Android Backup Service に渡されてアーカイブされます。

データは、アプリのユーザーごとに 5 MB に制限されます。バックアップ データは無料で保存できます。

Android のバックアップ オプションの概要と、バックアップおよび復元する必要があるデータに関するガイダンスについては、データのバックアップの概要をご覧ください。

Key-Value バックアップを実装する

アプリデータをバックアップするには、バックアップ エージェントを実装する必要があります。バックアップ エージェントは、バックアップと復元の両方でバックアップ マネージャーによって呼び出されます。

バックアップ エージェントを実装するには、以下を行う必要があります。

  1. マニフェスト ファイルで android:backupAgent 属性を使用してバックアップ エージェントを宣言する。

  2. 次のいずれかの方法でバックアップ エージェントを定義する。

    • BackupAgent の拡張

      BackupAgent クラスは、アプリがバックアップ マネージャーとの通信に使用する中央インターフェースを提供します。このクラスを直接拡張する場合、onBackup()onRestore() をオーバーライドしてデータのバックアップと復元のオペレーションを処理する必要があります。

    • BackupAgentHelper の拡張

      BackupAgentHelper クラスは、BackupAgent クラスを中心とした便利なラッパーを提供し、記述する必要があるコードの量を最小限に抑えます。BackupAgentHelper では、onBackup()onRestore() を実装する必要がないように、特定の種類のデータを自動的にバックアップおよび復元する 1 つ以上のヘルパー オブジェクトを使用する必要があります。アプリのバックアップを完全に制御する必要がない場合は、BackupAgentHelper を使用してアプリのバックアップを処理することをおすすめします。

      現在、Android は SharedPreferences内部ストレージからファイル一式をバックアップして復元するバックアップ ヘルパーを提供しています。

マニフェストでバックアップ エージェントを宣言する

バックアップ エージェントのクラス名を決定したら、マニフェストで <application> タグ内の android:backupAgent 属性を使用して宣言します。

次に例を示します。

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

古いデバイスをサポートする場合は、API キー <meta-data> を Android マニフェスト ファイルに追加することをおすすめします。Android Backup Service ではサービスキーが不要になりましたが、一部の古いデバイスでは、まだバックアップ時にキーが確認されることがあります。android:namecom.google.android.backup.api_key に設定し、android:valueunused に設定します。

android:restoreAnyVersion 属性は、バックアップ データを生成したバージョンと比較した現在のアプリのバージョンにかかわらず、アプリデータを復元するかどうかを示すブール値を取得します。デフォルト値は false です。詳細については、復元データのバージョンを確認するをご覧ください。

BackupAgentHelper を拡張する

ファイル一式を(SharedPreferences または内部ストレージから)バックアップする場合、BackupAgentHelper を使用してバックアップ エージェントをビルドする必要があります。BackupAgentHelper を使用してバックアップ エージェントをビルドする場合、onBackup()onRestore() を実装する必要がないため、BackupAgent を拡張するよりもはるかに少ないコードで済みます。

BackupAgentHelper を実装する場合、バックアップ ヘルパーを 1 つ以上使用する必要があります。バックアップ ヘルパーは、BackupAgentHelper が特定の種類のデータのバックアップおよび復元処理を実行するために呼び出す特殊なコンポーネントです。Android フレームワークでは現在、以下の 2 つのヘルパーが提供されています。

BackupAgentHelper に複数のヘルパーを含めることができますが、各データタイプに必要なヘルパーは 1 つのみです。つまり、複数の SharedPreferences ファイルがある場合、必要となるのは SharedPreferencesBackupHelper 1 つだけです。

BackupAgentHelper に追加するヘルパーごとに、onCreate() メソッドで次の処理を行う必要があります。

  1. 目的のヘルパークラスのインスタンスをインスタンス化する。クラス コンストラクタに、バックアップするファイルを指定する必要があります。
  2. addHelper() を呼び出して BackupAgentHelper にヘルパーを追加する。

以下のセクションでは、使用可能な各ヘルパーを使用してバックアップ エージェントを作成する方法について説明します。

SharedPreferences をバックアップする

SharedPreferencesBackupHelper をインスタンス化するときは、1 つ以上の SharedPreferences ファイルの名前を含める必要があります。

たとえば、user_preferences という名の SharedPreferences ファイルをバックアップする場合、BackupAgentHelper を使用する完全なバックアップ エージェントは次のようになります。

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 には SharedPreferences ファイルのバックアップと復元に必要なコードがすべて含まれています。

バックアップ マネージャーが onBackup()onRestore() を呼び出すと、BackupAgentHelper がバックアップ ヘルパーを呼び出して指定したファイルのバックアップと復元を行います。

他のファイルをバックアップする

FileBackupHelper をインスタンス化するときは、openFileOutput() がファイルを書き込む場所と同じ getFilesDir() で指定されているとおり、アプリの内部ストレージに保存されている 1 つ以上のファイルの名前を含める必要があります。

たとえば、scoresstats という名前の 2 つのファイルをバックアップする場合、BackupAgentHelper を使用するバックアップ エージェントは次のようになります。

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 には、アプリの内部ストレージに保存されているファイルをバックアップおよび復元するために必要なすべてのコードが含まれています。

しかし、内部ストレージ上のファイルの読み書きはスレッドセーフではありません。バックアップ エージェントがアクティビティと同時にファイルを読み書きしないようにするには、読み取りや書き込みを実行するたびに同期ステートメントを使用する必要があります。たとえば、ファイルを読み書きするアクティビティの場合、同期ステートメントの組み込みロックとして使用するオブジェクトが必要です。

Kotlin

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

Java

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

次に、ファイルを読み書きするたびに、このロックを使用して同期ステートメントを作成します。たとえば、ゲームの最新スコアをファイルに書き込むための同期ステートメントは次のとおりです。

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

同じロックを使用して読み取りステートメントを同期する必要があります。

次に、BackupAgentHelperonBackup()onRestore() をオーバーライドし、同じ組み込みロックを使用してバックアップと復元の処理を同期します。たとえば、上の MyFileBackupAgent の例には次のメソッドが必要です。

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

BackupAgent を拡張する

ほとんどのアプリでは、BackupAgent クラスを直接拡張する必要はありませんが、代わりに BackupAgentHelper を拡張して、ファイルを自動的にバックアップおよび復元する組み込みヘルパークラスを利用する必要があります。ただし、次のようにすることで、BackupAgent を直接拡張できます。

  • データ形式をバージョニングする。たとえば、アプリデータの書式を変更する必要がある場合、バックアップ エージェントをビルドして復元処理時にアプリのバージョンをクロスチェックし、デバイスのバージョンがバックアップ データと異なる場合に互換性を保つうえで必要な作業を行います。詳細については、復元データのバージョンを確認するをご覧ください。
  • バックアップするデータの部分を指定する。ファイル全体をバックアップする代わりに、バックアップするデータの部分と各部分のデバイスへの復元方法を指定できます。これは、ファイル一式ではなく、一意のエンティティとしてデータを読み書きするため、さまざまなバージョンの管理にも役立ちます。
  • データベースのデータをバックアップする。ユーザーがアプリを再インストールしたときに復元する SQLite データベースがある場合、バックアップ処理中に適切なデータを読み取るカスタム BackupAgent をビルドし、復元処理中にテーブルを作成してデータを挿入する必要があります。

上記のいずれかのタスクを実行する必要がなく、SharedPreferences または内部ストレージからファイル一式をバックアップしたい場合は、BackupAgentHelper を拡張するをご覧ください。

必須メソッド

BackupAgent の作成時に、以下のコールバック メソッドを実装する必要があります。

onBackup()
バックアップをリクエストすると、バックアップ マネージャーがこのメソッドを呼び出します。このメソッドでは、デバイスからアプリデータを読み取り、バックアップするデータをバックアップ マネージャーに渡します。詳しくは、バックアップを実行するをご覧ください。
onRestore()

バックアップ マネージャーは、復元処理時にこのメソッドを呼び出します。このメソッドにより、バックアップ データが配信され、アプリで以前の状態を復元するために使用できます。詳しくは、復元を実行するをご覧ください。

ユーザーがアプリを再インストールすると、システムはこのメソッドを呼び出してバックアップ データを復元しますが、アプリで復元をリクエストすることもできます。

バックアップを実行する

バックアップ リクエストによって onBackup() メソッドがすぐに呼び出されるわけではありません。バックアップ マネージャーは適切な時間だけ待機し、最後にバックアップが実行された時点以降にバックアップをリクエストしたすべてのアプリについてバックアップを実行します。この時点でクラウド ストレージに保存できるように、バックアップ マネージャーにアプリデータを渡す必要があります。

バックアップ マネージャーのみがバックアップ エージェントの onBackup() メソッドを呼び出すことができます。アプリデータが変更され、バックアップを実行するたびに、dataChanged() を呼び出してバックアップ処理をリクエストする必要があります。詳細については、バックアップをリクエストするをご覧ください。

ヒント: アプリの開発中に、bmgr ツールを使用して、バックアップ マネージャーからすぐにバックアップ処理を開始できます。

バックアップ マネージャーは onBackup() メソッドを呼び出す際、次の 3 つのパラメータを渡します。

oldState
アプリが提供する最後のバックアップ状態を指す、オープンな、読み取り専用の ParcelFileDescriptor。これは、クラウド ストレージからのバックアップ データではなく、newState によって、または onRestore() で定義されているように、onBackup() が前回呼び出されたときにバックアップされたデータのローカル表現です。onRestore() については次のセクションで説明します。onBackup() はクラウド ストレージ内の既存のバックアップ データを読み取ることができないため、このローカル表現を使用して、前回のバックアップ以降にデータが変更されたかどうかを判別します。
data
BackupDataOutput オブジェクト。バックアップ マネージャーにバックアップ データを配信するために使用します。
newState
data に配信したデータの表現を書き込む必要があるファイルを指している、オープンで、読み書き可能な ParcelFileDescriptor。表現は、ファイルの最終変更タイムスタンプと同じくらいシンプルにできます。このオブジェクトは、次回バックアップ マネージャーが onBackup() メソッドを呼び出すときに oldState として返されます。バックアップ データを newState に書き込まない場合、oldState はバックアップ マネージャーが次回 onBackup() を呼び出すときに空のファイルを指します。

これらのパラメータを使用して、onBackup() メソッドを実装して以下を行います。

  1. oldState と現在のデータを比較して、最後のバックアップ以降にデータが変更されたかどうかを確認します。oldState でデータを読み取る方法は、newState に最初に書き込んだ方法によって異なります(ステップ 3 を参照)。ファイルの状態を記録する最も簡単な方法は、最後に変更されたタイムスタンプを使用することです。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
    }
    

    何も変更されておらず、バックアップする必要がない場合は、ステップ 3 に進みます。

  2. oldState と比較してデータが変更されている場合、現在のデータを data に書き込んで、クラウド ストレージにバックアップします。

    BackupDataOutput にデータの各チャンクをエンティティとして書き込む必要があります。エンティティは、一意のキー文字列で識別されるフラット化されたバイナリデータ レコードです。したがって、バックアップするデータセットは、概念的には Key-Value ペアのセットになります。

    エンティティをバックアップ データセットに追加するには:

    1. writeEntityHeader() を呼び出して、書き込むデータの一意の文字列キーとデータサイズを渡します。

    2. writeEntityData() を呼び出して、データを含むバイトバッファとバッファから書き込むバイト数(writeEntityHeader() に渡されるサイズと一致する必要があります)を渡します。

    次のコードは、一部のデータをバイト ストリームに変換し、1 つのエンティティに書き込む例を示します。

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

    これは、バックアップするデータごとに実行します。データをエンティティに分割する方法は自由に決めることができます。エンティティを 1 つだけ使用することもできます。

  3. バックアップ(ステップ 2)を実行するかどうかにかかわらず、現在のデータの表現を newState ParcelFileDescriptor に書き込みます。バックアップ マネージャーは、このオブジェクトを、現在バックアップされているデータの表現としてローカルで保持します。次に onBackup() を呼び出すときに、これを oldState として返します。これにより、別のバックアップが必要かどうかを判断できます(ステップ 1 で処理)。現在のデータ状態をこのファイルに書き込まない場合、次のコールバック中に oldState は空になります。

    次の例では、ファイルの最終変更タイムスタンプを使用して、現在のデータの表現を newState ファイルに保存します。

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

復元を実行する

アプリデータを復元するときは、バックアップ マネージャーがバックアップ エージェントの onRestore() メソッドを呼び出します。このメソッドを呼び出すと、バックアップ マネージャーによりバックアップ データが提供され、デバイスに復元できます。

バックアップ マネージャーのみが onRestore() を呼び出すことができます。この呼び出しは、アプリがインストールされ、既存のバックアップ データが検出されたときに自動的に行われます。

バックアップ マネージャーは onRestore() メソッドを呼び出す際、次の 3 つのパラメータを渡します。

data
BackupDataInput オブジェクト。バックアップ データの読み取りに使用できます。
appVersionCode
アプリの android:versionCode マニフェスト属性の値を表す整数。バックアップ時と同じデータになります。これを使用して、現在のアプリのバージョンをクロスチェックし、データ形式に互換性があるかどうかを判断できます。異なるバージョンの復元データを処理する方法については、復元データのバージョンを確認するをご覧ください。
newState
オープンされている、読み書き可能な ParcelFileDescriptordata で提供された最終的なバックアップ状態を書き込む必要のあるファイルを指します。このオブジェクトは、次回 onBackup() が呼び出されたときに oldState として返されます。また、onBackup() コールバックで同じ newState オブジェクトを書き込む必要があります。書き込むことで、onBackup() に指定された oldState オブジェクトは、デバイスの復元後に初めて onBackup() が呼び出された場合でも有効になります。

onRestore() の実装で、data に対して readNextHeader() を呼び出して、データセット内のすべてのエンティティを反復処理する必要があります。検出されたエンティティごとに、以下を行います。

  1. getKey() を使用してエンティティ キーを取得する。
  2. エンティティ キーを、BackupAgent クラス内で静的な最終文字列として宣言する必要がある既知の Key-Value リストと比較します。キーが既知のキー文字列のいずれかと一致したら、ステートメントを入力してエンティティ データを抽出し、デバイスに保存します。

    1. getDataSize() を使用してエンティティ データのサイズを取得し、そのサイズのバイト配列を作成します。
    2. readEntityData() を呼び出して、データの格納先となるバイト配列を渡し、開始オフセットと読み取るサイズを指定します。
    3. バイト配列がいっぱいになりました。自由にデータを読み取って、デバイスに書き込みます。
  3. データを読み取り、デバイスに書き戻した後、onBackup() のときと同じように、データの状態を newState パラメータに書き込みます。

前のセクションの例でバックアップされたデータを復元する方法を以下に示します。

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

この例では、onRestore() に渡される appVersionCode パラメータは使用されません。ただし、ユーザーのアプリのバージョンが実際にダウングレードしたとき(ユーザーがアプリのバージョンを 1.5 から 1.0 に移行した場合など)に、バックアップを実行するように選択した場合に使用できます。詳細については、次のセクションをご覧ください。

復元データのバージョンを確認する

バックアップ マネージャーは、データをクラウド ストレージに保存する際、マニフェスト ファイルの android:versionCode 属性で定義されているとおり、自動的にアプリのバージョンを含めます。バックアップ マネージャーは、バックアップ エージェントを呼び出してデータを復元する前に、インストールされているアプリの android:versionCode を確認し、復元データセットに記録されている値と比較します。復元データセットに記録されたバージョンがデバイス上のアプリのバージョンよりも新しい場合、ユーザーはアプリをダウングレードしています。この場合、バックアップ マネージャーはアプリの復元処理を中止し、onRestore() メソッドを呼び出しません。これは、復元セットが古いバージョンにとって意味がないと見なされるためです。

android:restoreAnyVersion 属性を使用すると、この動作をオーバーライドできます。この属性を true に設定して、復元セット バージョンにかかわらずアプリを復元することを示します。デフォルト値は false です。これを true に設定した場合、バックアップ マネージャーは android:versionCode を無視し、すべての場合で onRestore() メソッドを呼び出します。そうすることにより、onRestore() メソッドでバージョンの違いを手動で確認し、バージョンが一致しない場合にデータの互換性を保つために必要な手順を踏むことができます。

復元処理時にさまざまなバージョンを処理できるように、onRestore() メソッドは、復元データセットに含まれているバージョン コードを appVersionCode パラメータとして渡します。これで、PackageInfo.versionCode フィールドを使用して、現在のアプリのバージョン コードに対してクエリを行うことができます。次に例を示します。

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

次に、PackageInfo から取得した versiononRestore() に渡された appVersionCode と比較します。

バックアップをリクエストする

dataChanged() を呼び出して、いつでもバックアップ処理をリクエストできます。このメソッドは、バックアップ エージェントを使用してデータをバックアップすることをバックアップ マネージャーに通知します。その後、ある時点で、バックアップ エージェントの onBackup() メソッドが呼び出されます。通常、データが変更されるたびに(バックアップするアプリの設定を変更した場合など)、バックアップをリクエストする必要があります。バックアップ マネージャーがエージェントからのバックアップをリクエストする前に dataChanged() を数回呼び出した場合も、エージェントは onBackup() の呼び出しを 1 回だけ受け取ります。

復元をリクエストする

アプリの通常使用中は、復元処理をリクエストする必要はありません。バックアップ データが自動的にチェックされ、アプリのインストール時に復元が実行されます。

自動バックアップに移行する

アプリをフルデータ バックアップに移行するには、マニフェスト ファイルの <application> 要素で android:fullBackupOnlytrue に設定します。Android 5.1(API レベル 22)以前のデバイスでは、マニフェストのこの値は無視され、Key-Value のバックアップが続行されます。デバイスで Android 6.0(API レベル 23)以降を実行している場合、Key-Value バックアップの代わりに自動バックアップがアプリで実行されます。

ユーザーのプライバシー

Google は、ユーザーの皆様からお寄せいただいている信頼と、ユーザーの皆様のプライバシーを保護する責任を深く認識しています。Google はバックアップと復元の機能を提供するために Google サーバーとの間でバックアップ データを安全に送信します。Google のプライバシー ポリシーに沿って、上記データは個人情報として扱われます。

また、Android システムのバックアップ設定でデータのバックアップ機能を無効にすることもできます。ユーザーがバックアップを無効にすると、Android Backup Service は保存したバックアップ データをすべて削除します。デバイスのバックアップは再び有効にできますが、Android Backup Service が以前に削除したデータは復元されません。