「BackupAgentHelper」クラスを継承したバックアップエージェントは、ほとんどonCreate()だけで実装が済んでしまいましたが、「BackupAgent」クラスを継承するバックアップエージェントは少し複雑です。ただし、細かな制御も可能になります。
実装が少し長いので、onCreate()から見ていきましょう。
@Override
public void onCreate() {
mDataFile = new File(getFilesDir(), FILENAME);
}
ファイルディレクトリに保存されているデータファイルを作成します。後で、このファイルから内容を読み出してバックアップデータを生成します。
次はonBackup()です。
@Override
public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
ParcelFileDescriptor newState) throws IOException {
Log.d(TAG, "Backup...");
synchronized (LOCK) { // 【1】
RandomAccessFile file = new RandomAccessFile(mDataFile, "r");
mCount = file.readInt(); // 【2】
file.close();
}
boolean doBackup = (oldState == null); // 【3】
if (!doBackup) {
doBackup = compareStateFile(oldState); // 【4】
}
if (doBackup) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(baos);
dos.writeInt(mCount);
byte[] buffer = baos.toByteArray();
int len = buffer.length;
data.writeEntityHeader(APP_DATA_KEY, len); // 【5】
data.writeEntityData(buffer, len);
}
writeStateFile(newState); // 【6】
}
ファイル内容を読み出す場合も、【1】のようにsynchronizedで排他するのは同様です。
【2】では、ファイルからデータを取り出しています。FileBackupHelperを用いる実装と異なるのはバックアップ対象データを扱う柔軟さですが、実装が複雑になる部分でもあります。
【3】では、バックアップを取る必要があるかどうかを判断しています。oldStateがnullならバックアップが必要、または【4】でバックアップ実装またはデータ内容が異なる場合はバックアップが必要、と判断しています。
バックアップが必要な場合、【5】でバックアップデータを書き出しています。【6】では、現在の状態をnewStateに書き出します。
最後にonRestore()です。
@Override
public void onRestore(BackupDataInput data, int appVersionCode, // 【1】
ParcelFileDescriptor newState) throws IOException {
Log.d(TAG, "Restore...");
while (data.readNextHeader()) { // 【2】
String key = data.getKey();
int dataSize = data.getDataSize();
if (APP_DATA_KEY.equals(key)) { // 【3】
byte[] dataBuf = new byte[dataSize];
data.readEntityData(dataBuf, 0, dataSize);
ByteArrayInputStream bais = new ByteArrayInputStream(dataBuf);
DataInputStream din = new DataInputStream(bais);
mCount = din.readInt(); // 【4】
MainActivity.sendBroadcast(this, mCount); // 【5】
synchronized (LOCK) { // 【6】
RandomAccessFile file = new RandomAccessFile(mDataFile,
"rw");
file.setLength(0L);
file.writeInt(mCount);
file.close();
}
} else {
data.skipEntityData(); // 【7】
}
}
writeStateFile(newState); // 【8】
}
今回の実装では使いませんでしたが、【1】のようなappVersionCodeでアプリのバージョンコードが渡されてきます。バージョンコードはバックアップの際には自動的に収集されます。
今回はバックアップエージェントにバージョンを独自に持ちましたが、アプリのバージョンコードで実装の違いを管理するのも1つの方法です。
バックアップされたデータには複数の領域が存在する可能性があるため、データを読み出す際には、【2】のようにwhileループですべてのヘッダを読み出して目的のデータを探し出す必要があります。
目的のデータかどうかは【3】のように確認します。データ形式が分かっているので【4】のようにデータを直接読み出します。
【5】では、Intentをブロードキャストしてデータを画面に即時反映しています。繰り返しになりますが、ファイルの読み書きは【6】のような排他が必要です。
目的のデータではなかった場合、【7】のようにスキップします。これは通常は起こりませんが、古いアプリが新しいバックアップデータを読んでしまうようなケースに限られるでしょう。
【8】のように現在の状態をnewStateに書き出してレストアは終了です。
アプリのデータバックアップ、いかがだったでしょうか。買い替えなどで新しい端末にアプリを再インストールしたとき、以前の状態が復元されると、ユーザーの利便性が増し、「おおっ」とか「ちゃんとしてるな」という印象を得られるのではないでしょうか。
最後になりますが、バックアップで注意すべき点を個条書きにして今回は終わりにします。
安藤幸央のランダウン(62):スマホアプリ開発に超絶便利なBaaSとは、MEAPとは
Java 7ランタイムもサポート:Google App Engine、「Cloud Endpoints」でモバイルバックエンド環境を提供Copyright © ITmedia, Inc. All Rights Reserved.