web-dev-qa-db-ja.com

Android backup / restore:内部データベースをバックアップするには?

提供されているBackupAgentHelperを使用してFileBackupHelperを実装し、所有しているネイティブデータベースをバックアップおよび復元しました。これは、通常ContentProvidersとともに使用するデータベースであり、_/data/data/yourpackage/databases/_にあります。

これは一般的なケースだと思うでしょう。しかし、ドキュメントは何をすべきか明確ではありません: http://developer.Android.com/guide/topics/data/backup.htmlこれらの典型的なデータベース専用のBackupHelperはありません。したがって、私はFileBackupHelperを使用し、「_/databases/_」で.dbファイルをポイントし、db操作(_db.insert_など)にロックを導入しました)ContentProvidersで、インストール後に存在しないため、onRestore()の前に "_/databases/_"ディレクトリを作成しようとしました。

過去に別のアプリでSharedPreferencesに対して同様のソリューションを正常に実装しました。ただし、エミュレータ2.2で新しい実装をテストすると、ログからLocalTransportにバックアップが実行され、復元が実行されます(およびonRestore()が呼び出されます)。 まだ、dbファイル自体は作成されません。

これはすべてインストール後、復元が実行された後、アプリの最初の起動前であることに注意してください。それとは別に、私のテスト戦略は http://developer.Android.com/guide/topics/data/backup.html#Testing に基づいていました。

また、私が自分で管理するいくつかのsqliteデータベースについても、SDカード、自分のサーバー、または他の場所へのバックアップについても話していません。

私は、カスタムBackupAgentを使用することを勧めるデータベースに関するドキュメントで言及を見ましたが、それは関連していないようです:

ただし、必要な場合はBackupAgentを直接拡張することをお勧めします。*データベース内のデータをバックアップします。ユーザーがアプリケーションを再インストールしたときに復元するSQLiteデータベースがある場合、バックアップ操作中に適切なデータを読み取るカスタムBackupAgentを構築し、復元操作中にテーブルを作成してデータを挿入する必要があります。

明確にしてください

SQLレベルまで自分で本当に行う必要がある場合は、次のトピックが心配です。

  • データベースとトランザクションを開きます。私のアプリのワークフローの外で、そのようなシングルトンクラスからそれらを閉じる方法がわかりません。

  • バックアップが進行中で、データベースがロックされていることをユーザーに通知する方法。時間がかかる場合があるため、進行状況バーを表示する必要がある場合があります。

  • 復元時に同じことを行う方法。私が理解しているように、復元は、ユーザーが既にアプリの使用を開始(およびデータをデータベースに入力)したときに発生する可能性があります。そのため、バックアップされたデータを所定の場所に復元する(空のデータまたは古いデータを削除する)だけを想定することはできません。あなたは何らかの形でそれに参加する必要がありますが、それはどんな些細なデータベースでもIDのために不可能です。

  • ユーザーがどこかで立ち往生することなく、復元が完了した後にアプリを更新する方法-今-到達不能ポイント。

  • バックアップまたは復元時にデータベースがすでにアップグレードされていることを確認できますか?そうしないと、予想されるスキーマが一致しない場合があります。

86
pjv

より明確なアプローチは、カスタムBackupHelperを作成することです。

public class DbBackupHelper extends FileBackupHelper {

    public DbBackupHelper(Context ctx, String dbName) {
        super(ctx, ctx.getDatabasePath(dbName).getAbsolutePath());
    }
}

BackupAgentHelperに追加します:

public void onCreate() {
    addHelper(DATABASE, new DbBackupHelper(this, DB.FILE));
}
21
yanchenko

私の質問を再考した後、私は ConnectBotがそれを行う方法 を見た後、それを動作させることができました。ケニーとジェフリーに感謝します!

実際に追加するのと同じくらい簡単です:

FileBackupHelper hosts = new FileBackupHelper(this,
    "../databases/" + HostDatabase.DB_NAME);
addHelper(HostDatabase.DB_NAME, hosts);

BackupAgentHelperに。

私が欠けていた点は、「../databases/」で相対パスを使用する必要があるという事実でした。

それでも、これは決して完璧な解決策ではありません。 FileBackupHelperのドキュメントでは、たとえば「FileBackupHelperは、大きな構成ファイルではなく、小さな構成ファイルでのみ使用する必要があります。」、後者はSQLiteデータベースの場合です。

私は、より多くの提案、私たちに期待されることに対する洞察(適切な解決策は何か)、およびこれがどのように壊れるかについてのアドバイスをもらいたいと思います。

34
pjv

データベースをファイルとしてバックアップするさらにクリーンな方法を次に示します。ハードコーディングされたパスはありません。

class MyBackupAgent extends BackupAgentHelper{
   private static final String DB_NAME = "my_db";

   @Override
   public void onCreate(){
      FileBackupHelper dbs = new FileBackupHelper(this, DB_NAME);
      addHelper("dbs", dbs);
   }

   @Override
   public File getFilesDir(){
      File path = getDatabasePath(DB_NAME);
      return path.getParentFile();
   }
}

注:FileBackupHelperがfiles dirではなくデータベースdirで動作するように、 getFilesDir をオーバーライドします。

別のヒント: databaseList を使用して、このリストからすべてのDBおよびフィード名を(親パスなしで)FileBackupHelperに取得することもできます。その後、すべてのアプリのDBがバックアップに保存されます。

22
Pointer Null

FileBackupHelperを使用してsqlite dbをバックアップ/復元すると、いくつかの深刻な問題が発生します。
1。アプリがContentProvider.query()から取得したカーソルを使用し、バックアップエージェントがファイル全体を上書きしようとするとどうなりますか?
2。 link は、完璧な(低エントロピー;)テストの良い例です。アプリをアンインストールし、再度インストールすると、バックアップが復元されます。しかし、人生は残酷になる可能性があります。 link をご覧ください。ユーザーが新しいデバイスを購入するシナリオを想像してみましょう。独自のセットがないため、バックアップエージェントは他のデバイスのセットを使用します。アプリがインストールされ、backupHelperが現在よりも低いdbバージョンスキーマで古いファイルを取得します。 SQLiteOpenHelperは、デフォルトの実装でonDowngradeを呼び出します。

public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    throw new SQLiteException("Can't downgrade database from version " +
            oldVersion + " to " + newVersion);
}

ユーザーが何をしても、新しいデバイスでアプリを使用することはできません。

ContentResolverを使用してデータを取得することをお勧めします->シリアル化(_ids)バックアップおよびデシリアライズ->復元用のデータの挿入。

注:データの取得/挿入はContentResolverを介して行われるため、通貨の問題を回避できます。シリアル化は、backupAgentで実行されます。独自のcursor <-> object mappingを行うと、エンティティを表すクラスでSerializable field _idを使用してtransientを実装するだけで、アイテムをシリアル化できます。

一括挿入も使用します。つまり、ContentProviderOperation およびCursorLoader.setUpdateThrottleバックアップ復元プロセス中にデータが変更されたときにローダーが再起動することでアプリが停止しないようにします。

ダウングレードが発生した場合は、データの復元を中止するか、ダウングレードされたバージョンに関連するフィールドでContentResolverを復元して更新するかを選択できます。

主題は簡単ではなく、ドキュメントで十分に説明されておらず、バルクデータサイズなどのようないくつかの質問がまだ残っていることに同意します。

お役に立てれば。

7
Piotr Bazan

Android Mの時点で、アプリで利用可能なフルデータバックアップ/復元APIがあります。この新しいAPIには、アプリマニフェストにXMLベースの仕様が含まれています。 「mydata.db」と呼ばれるデータベースをバックアップします。この新しいAPIは、開発者が使用するのがはるかに簡単です。差分を追跡したり、バックアップパスを明示的に要求したりする必要はありません。バックアップするファイルのXML記述は、多くの場合、コードをまったく記述する必要がないことを意味します。

(あなたはcan復元が発生したときにコールバックを取得するためにフルデータのバックアップ/復元操作に関与することができます。そのように柔軟です。 )

新しいAPIの使用方法については、developer.Android.comの アプリの自動バックアップの設定 セクションをご覧ください。

3
ctate

1つのオプションは、データベースの上のアプリケーションロジックでビルドすることです。それは私が思うにそのようなレベル1のために実際に叫びます。すでにそれを行っているかどうかはわかりませんが、ほとんどの人は(Androidコンテンツマネージャーカーソルアプローチ)にもかかわらず、カスタムまたは何らかのorm-liteアプローチのいずれかのORMマッピングを導入します。この場合:

  1. アプリケーションが既に起動している間にアプリ/データがバックグラウンドで追加/削除され、新しいデータが追加/削除されたときにアプリケーションが正常に動作することを確認する
  2. java-> protobufまたは単にJavaシリアル化マッピングを作成し、独自のBackupHelperを作成してストリームからデータを読み取り、データベースに追加するだけです...

したがって、この場合、データベースレベルで実行するのではなく、アプリケーションレベルで実行します。

0
Jarek Potiuk