[android] Android 백업 / 복원 : 내부 데이터베이스를 백업하는 방법은 무엇입니까?

BackupAgentHelper제공된 FileBackupHelper기본 데이터베이스를 백업하고 복원하기 위해 제공된 사용 을 구현했습니다 . 일반적으로 함께 사용 ContentProviders하고에 상주 하는 데이터베이스 입니다 /data/data/yourpackage/databases/.

이것은 일반적인 경우라고 생각할 것입니다. 그러나 문서는 http://developer.android.com/guide/topics/data/backup.html 에서 무엇을 해야할지 명확하지 않습니다 . 이러한 일반적인 데이터베이스에 대한 특별한 것은 없습니다 BackupHelper. 따라서를 사용하고FileBackupHelper/databases/“의 .db 파일을 가리키고, 내에서 모든 db 작업 (예 :)에 대한 잠금을 도입 db.insert했으며 ContentProviders, 설치 후에는 존재하지 않기 때문에 /databases/이전에 ” “디렉토리를 만들려고 onRestore()했습니다.

SharedPreferences과거에 다른 앱에서 성공적으로 유사한 솔루션을 구현했습니다 . 그러나 에뮬레이터 2.2에서 새 구현을 테스트 할 때 LocalTransport로그에서 백업이 수행 되고 복원이 수행되고 onRestore()호출 되는 것을 볼 수 있습니다. 그러나 db 파일 자체는 생성되지 않습니다.

이것은 모두 설치 후와 앱을 처음 실행하기 전, 복원이 수행 된 후입니다. 그 외에도 내 테스트 전략은 http://developer.android.com/guide/topics/data/backup.html#Testing 기반이었습니다 .

또한 내가 직접 관리하는 sqlite 데이터베이스에 대해 이야기하지 않으며 SDcard, 자체 서버 또는 다른 곳에 백업하는 것에 대해서도 이야기하지 않습니다.

사용자 지정 사용을 권장하는 데이터베이스에 대한 문서에서 언급을 BackupAgent보았지만 관련이없는 것 같습니다.

그러나 다음과 같은 경우 BackupAgent를 직접 확장 할 수 있습니다. * 데이터베이스의 데이터를 백업합니다. 사용자가 애플리케이션을 다시 설치할 때 복원하려는 SQLite 데이터베이스가있는 경우 백업 작업 중에 적절한 데이터를 읽는 사용자 지정 BackupAgent를 빌드 한 다음 복원 작업 중에 테이블을 만들고 데이터를 삽입해야합니다.

좀 명확하게 부탁드립니다.

SQL 수준까지 직접 수행해야하는 경우 다음 항목이 걱정됩니다.

  • 데이터베이스 및 트랜잭션을 엽니 다. 내 앱의 워크 플로 외부의 단일 클래스에서 닫는 방법을 모릅니다.

  • 백업이 진행 중이고 데이터베이스가 잠겨 있음을 사용자에게 알리는 방법. 시간이 오래 걸릴 수 있으므로 진행률 표시 줄을 표시해야 할 수 있습니다.

  • 복원시 동일한 작업을 수행하는 방법. 내가 이해했듯이 복원은 사용자가 이미 앱을 사용하고 데이터베이스에 데이터를 입력하기 시작했을 때 발생할 수 있습니다. 따라서 백업 된 데이터를 제자리에 복원한다고 가정 할 수 없습니다 (빈 데이터 나 오래된 데이터 삭제). 당신은 어떻게 든 그것에 가입해야 할 것입니다. ID 때문에 사소하지 않은 데이터베이스는 불가능합니다.

  • 복원이 완료된 후 사용자가 도달 할 수없는 지점에서 멈추지 않고 앱을 새로 고치는 방법.

  • 백업 또는 복원시 데이터베이스가 이미 업그레이드되었는지 확인할 수 있습니까? 그렇지 않으면 예상 스키마가 일치하지 않을 수 있습니다.



답변

더 깨끗한 접근 방식은 사용자 정의를 만드는 것입니다 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));
}


답변

내 질문을 다시 방문한ConnectBot이 어떻게 작동 하는지 살펴본 후 작동하도록 할 수있었습니다 . Kenny와 Jeffrey에게 감사합니다!

실제로 다음을 추가하는 것만 큼 쉽습니다.

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

귀하의 BackupAgentHelper.

내가 놓친 점은 ” ../databases/” 와 함께 상대 경로를 사용해야한다는 사실입니다 .

그래도 이것이 완벽한 해결책은 아닙니다. FileBackupHelper예를 들어 언급 할 문서 : ” FileBackupHelper는 큰 바이너리 파일이 아닌 작은 구성 파일에만 사용해야합니다. “, 후자는 SQLite 데이터베이스의 경우입니다.

나는 더 많은 제안, 우리에게 기대되는 것에 대한 통찰력 (적절한 해결책은 무엇인가), 그리고 이것이 어떻게 깨질 수 있는지에 대한 조언을 받고 싶습니다.


답변

다음은 데이터베이스를 파일로 백업하는 더 깨끗한 방법입니다. 하드 코딩 된 경로가 없습니다.

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가 파일 dir이 아닌 데이터베이스 dir에서 작동하도록 getFilesDir 을 대체 합니다.

또 다른 힌트 : databaseList 를 사용 하여이 목록 (부모 경로 없음)에서 모든 DB 및 피드 이름을 FileBackupHelper로 가져올 수도 있습니다 . 그러면 모든 앱의 DB가 백업에 저장됩니다.


답변

FileBackupHelpersqlite db를 백업 / 복원하는 데 사용 하면 몇 가지 심각한 질문이 제기됩니다.
1. 앱이 검색된 커서를 사용 ContentProvider.query()하고 백업 에이전트가 전체 파일을 덮어 쓰려고하면 어떻게됩니까?
2. 링크 는 완벽한 (낮은 엔트로피) 테스트의 좋은 예입니다. 앱을 제거하고 다시 설치하면 백업이 복원됩니다. 그러나 인생은 잔인 할 수 있습니다. 한 번 봐 가지고 링크를 . 사용자가 새 장치를 구입하는 시나리오를 상상해 봅시다. 자체 세트가 없기 때문에 백업 에이전트는 다른 장치의 세트를 사용합니다. 앱이 설치되고 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에서 수행됩니다. 직접 커서 <-> 객체 매핑을 수행하는 경우 항목을 직렬화하는 것은 엔티티를 나타내는 클래스의 _id 필드를 사용하여 구현 Serializable하는 것처럼 간단 할 수 있습니다 transient.

또한, 즉 삽입 대부분을 사용하십시오 ContentProviderOperation 예를 하고 CursorLoader.setUpdateThrottle그래서 응용 프로그램 복원 프로세스를 백업하는 동안 데이터 변경에 로더를 다시 시작과 붙어 아니라고.

다운 그레이드 상황이 발생하면 데이터 복원을 중단하거나 다운 그레이드 된 버전과 관련된 필드를 사용하여 ContentResolver를 복원 및 업데이트하도록 선택할 수 있습니다.

주제가 쉽지 않고 문서에 잘 설명되어 있지 않으며 일부 질문은 여전히 ​​대량 데이터 크기 등으로 남아 있다는 데 동의합니다.

도움이 되었기를 바랍니다.


답변

Android M부터는 앱에서 사용할 수있는 전체 데이터 백업 / 복원 API가 있습니다. 이 새로운 API에는 개발자가 직접 의미 론적 방식으로 백업 할 파일을 설명 할 수있는 XML 기반 사양이 앱 매니페스트에 포함되어 있습니다. ‘ “mydata.db”라는 데이터베이스 백업’. 이 새로운 API는 개발자가 훨씬 쉽게 사용할 수 있습니다. diff를 추적하거나 명시 적으로 백업 패스를 요청할 필요가 없으며 백업 할 파일에 대한 XML 설명은 종종 코드를 작성할 필요가 없음을 의미합니다. 조금도.

(당신은 할 수 도 전체 데이터 백업에 참여 /를 예를 들어, 어떻게 복원 할 때 콜백을 얻기 위해 복원 작업. 그것은 그런 식으로 유연.)

새 API 사용 방법에 대한 설명은 developer.android.com의 앱용 자동 백업 구성 섹션을 참조하세요 .


답변

한 가지 옵션은 데이터베이스 위의 애플리케이션 로직에서 빌드하는 것입니다. 실제로 그런 수준의 비명을 지르는 것 같습니다. 이미하고 있는지 확실하지 않지만 대부분의 사람들 (Android 콘텐츠 관리자 커서 접근 방식에도 불구하고)은 사용자 지정 또는 일부 orm-lite 접근 방식 중 일부 ORM 매핑을 도입 할 것입니다. 그리고이 경우에 제가하고 싶은 것은 :

  1. 응용 프로그램이 이미 시작된 상태에서 새 데이터가 추가 / 제거 된 백그라운드에서 응용 프로그램 / 데이터가 추가 될 때 응용 프로그램이 제대로 작동하는지 확인합니다.
  2. Java-> protobuf 또는 단순히 Java 직렬화 매핑을 만들고 자신의 BackupHelper를 작성하여 스트림에서 데이터를 읽고 간단히 데이터베이스에 추가합니다 ….

따라서이 경우에는 db 수준에서 수행하는 대신 응용 프로그램 수준에서 수행합니다.


답변