Zaimplementowałem BackupAgentHelper
użycie dostarczonego FileBackupHelper
do tworzenia kopii zapasowych i przywracania natywnej bazy danych, którą mam. Jest to baza danych, z której zwykle korzystasz ContentProviders
i która znajduje się w /data/data/yourpackage/databases/
.
Można by pomyśleć, że to powszechny przypadek. Jednak dokumentacja nie jest jasna, co należy zrobić: http://developer.android.com/guide/topics/data/backup.html . Nie ma BackupHelper
specjalnie dla tych typowych baz danych. Dlatego użyłemFileBackupHelper
, wskazałem na mój plik .db w " /databases/
", wprowadziłem blokady wokół wszelkich operacji db (takich jak db.insert
) w moim ContentProviders
, a nawet próbowałem utworzyć /databases/
katalog " " wcześniej, onRestore()
ponieważ nie istnieje po instalacji.
W przeszłości wdrożyłem podobne rozwiązanie dla SharedPreferences
pomyślnie w innej aplikacji. Jednak kiedy testuję moją nową implementację w emulatorze-2.2, widzę tworzenie kopii zapasowej LocalTransport
z dzienników, a także wykonywanie (i onRestore()
wywoływanie) przywracania . Jednak sam plik db nigdy nie jest tworzony.
Zwróć uwagę, że dzieje się to po instalacji, a przed pierwszym uruchomieniem aplikacji, po wykonaniu przywracania. Poza tym moja strategia testowa opierała się na http://developer.android.com/guide/topics/data/backup.html#Testing .
Proszę również zauważyć, że nie mówię o jakiejś bazie danych sqlite, którą sam zarządzam, ani o tworzeniu kopii zapasowych na karcie SD, na własnym serwerze lub w innym miejscu.
Widziałem wzmiankę w dokumentach o bazach danych zalecających używanie niestandardowego BackupAgent
ale nie wydaje się to być powiązane:
Jednak możesz bezpośrednio rozszerzyć BackupAgent, jeśli potrzebujesz: * Utworzyć kopię zapasową danych w bazie danych. Jeśli masz bazę danych SQLite, którą chcesz przywrócić, gdy użytkownik ponownie zainstaluje Twoją aplikację, musisz utworzyć niestandardowy BackupAgent, który odczytuje odpowiednie dane podczas operacji tworzenia kopii zapasowej, a następnie utwórz tabelę i wstaw dane podczas operacji przywracania.
Proszę o wyjaśnienie.
Jeśli naprawdę muszę to zrobić samodzielnie do poziomu SQL, martwię się następującymi tematami:
Otwarte bazy danych i transakcje. Nie mam pojęcia, jak zamknąć je z takiej pojedynczej klasy poza przepływem pracy mojej aplikacji.
Jak powiadomić użytkownika, że trwa tworzenie kopii zapasowej, a baza danych jest zablokowana. Może to zająć dużo czasu, więc może być konieczne wyświetlenie paska postępu.
Jak zrobić to samo przy przywracaniu. Jak rozumiem, przywrócenie może nastąpić właśnie wtedy, gdy użytkownik zaczął już korzystać z aplikacji (i wprowadzać dane do bazy danych). Nie możesz więc zakładać, że po prostu przywrócisz kopię zapasową danych w miejscu (usuwając puste lub stare dane). Będziesz musiał jakoś się do niego przyłączyć, co dla każdej nietrywialnej bazy danych jest niemożliwe ze względu na identyfikator.
Jak odświeżyć aplikację po zakończeniu przywracania bez utknięcia użytkownika w jakimś - teraz - nieosiągalnym punkcie.
Czy mogę mieć pewność, że baza danych została już zaktualizowana podczas tworzenia kopii zapasowej lub przywracania? W przeciwnym razie oczekiwany schemat może nie być zgodny.
Odpowiedzi:
Czystszym podejściem byłoby stworzenie niestandardowego
BackupHelper
:public class DbBackupHelper extends FileBackupHelper { public DbBackupHelper(Context ctx, String dbName) { super(ctx, ctx.getDatabasePath(dbName).getAbsolutePath()); } }
a następnie dodaj do
BackupAgentHelper
:public void onCreate() { addHelper(DATABASE, new DbBackupHelper(this, DB.FILE)); }
źródło
Po ponownym przejrzeniu mojego pytania udało mi się uruchomić je po sprawdzeniu, jak robi to ConnectBot . Dzięki Kenny i Jeffrey!
W rzeczywistości jest to tak proste, jak dodanie:
FileBackupHelper hosts = new FileBackupHelper(this, "../databases/" + HostDatabase.DB_NAME); addHelper(HostDatabase.DB_NAME, hosts);
do twojego
BackupAgentHelper
.Brakowało mi tego, że musiałbyś użyć ścieżki względnej z „
../databases/
”.Nie jest to jednak idealne rozwiązanie. Dokumenty,
FileBackupHelper
na przykład, wspominają: „FileBackupHelper
powinny być używane tylko z małymi plikami konfiguracyjnymi, a nie dużymi plikami binarnymi ”, przy czym to drugie dotyczy baz danych SQLite.Chciałbym uzyskać więcej sugestii, wglądu w to, czego się od nas oczekuje (jakie jest właściwe rozwiązanie) i porady, jak to może się zepsuć.
źródło
db.open()
za pomocą instrukcji, czydb.close()
tylkoinsert
, czy co.Oto jeszcze czystszy sposób tworzenia kopii zapasowych baz danych w postaci plików. Brak zakodowanych ścieżek.
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(); } }
Uwaga: zastępuje getFilesDir dzięki czemu FileBackupHelper działa w katalogu baz danych, a nie w katalogu plików.
Kolejna wskazówka: możesz również użyć databaseList, aby pobrać wszystkie swoje bazy danych i nazwy kanałów z tej listy (bez ścieżki nadrzędnej) do FileBackupHelper. Wtedy wszystkie bazy danych aplikacji zostaną zapisane w kopii zapasowej.
źródło
Użycie
FileBackupHelper
do tworzenia kopii zapasowych / przywracania sqlite db rodzi poważne pytania:1. Co się stanie, jeśli aplikacja użyje kursora pobranego z,
ContentProvider.query()
a agent kopii zapasowej spróbuje zastąpić cały plik?2. Link jest dobrym przykładem doskonałego (niska entrofia;) testowania. Odinstaluj aplikację, zainstaluj ją ponownie i kopia zapasowa zostanie przywrócona. Jednak życie może być brutalne. Spójrz na link . Wyobraźmy sobie scenariusz, w którym użytkownik kupuje nowe urządzenie. Ponieważ nie ma własnego zestawu, agent kopii zapasowych używa zestawu innego urządzenia. Aplikacja zostanie zainstalowana, a program backupHelper pobierze stary plik ze schematem wersji bazy danych niższym niż bieżący.
SQLiteOpenHelper
wywołaniaonDowngrade
z domyślną implementacją:public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) { throw new SQLiteException("Can't downgrade database from version " + oldVersion + " to " + newVersion); }
Bez względu na to, co robi użytkownik, nie może korzystać z Twojej aplikacji na nowym urządzeniu.
Sugerowałbym użycie
ContentResolver
do pobierania danych -> serializuj (bez_id
s) do tworzenia kopii zapasowych i deserializacji -> wstaw dane do przywrócenia.Uwaga: pobieranie / wstawianie danych odbywa się za pośrednictwem ContentResolver, co pozwala uniknąć problemów z walutami. Serializacja jest wykonywana w backupAgent. Jeśli wykonujesz własne mapowanie obiektów kursora <->, serializowanie elementu może być tak proste, jak implementacja
Serializable
ztransient
polem _id w klasie reprezentującej Twoją jednostkę.Użyłbym również wstawiania zbiorczego, tj.
ContentProviderOperation
Przykład iCursorLoader.setUpdateThrottle
tak, że aplikacja nie jest zablokowany z ponownym Loader na zmianę danych w trakcie procesu przywracania kopii zapasowych.Jeśli zdarzy się, że jesteś w sytuacji obniżenia wersji, możesz przerwać przywracanie danych lub przywrócić i zaktualizować ContentResolver z polami dotyczącymi obniżonej wersji.
Zgadzam się, że temat nie jest łatwy, nie jest dobrze wyjaśniony w dokumentach, a niektóre pytania nadal pozostają takie jak zbiorczy rozmiar danych itp.
Mam nadzieję że to pomoże.
źródło
Począwszy od systemu Android M, dla aplikacji dostępny jest teraz interfejs API pełnego tworzenia kopii zapasowych / przywracania danych. Ten nowy interfejs API zawiera w manifeście aplikacji specyfikację opartą na języku XML, która umożliwia programistom opisanie plików, których kopie zapasowe mają być tworzone w sposób bezpośredni semantyczny: „wykonaj kopię zapasową bazy danych o nazwie„ mydata.db ””. Ten nowy interfejs API jest znacznie łatwiejszy w użyciu dla programistów - nie musisz śledzić różnic ani jawnie żądać przebiegu kopii zapasowej, a opis XML plików do utworzenia kopii zapasowej oznacza, że często nie musisz pisać żadnego kodu w ogóle.
(You can wziąć udział nawet w pełnym wymiarze kopii zapasowej danych / przywrócenia działania, aby uzyskać oddzwanianie, gdy przywróci się dzieje, na przykład. Jest elastyczny w ten sposób).
Zobacz sekcję Konfigurowanie automatycznej kopii zapasowej dla aplikacji na developer.android.com, aby zapoznać się z opisem korzystania z nowego interfejsu API.
źródło
Jedną z opcji będzie zbudowanie go w logice aplikacji powyżej bazy danych. Myślę, że to rzeczywiście krzyczy na taki poziom. Nie jestem pewien, czy już to robisz, ale większość ludzi (pomimo podejścia kursora menedżera treści w Androidzie) wprowadzi pewne mapowanie ORM - niestandardowe lub niektóre podejście orm-lite. A w tym przypadku wolałbym:
Więc w tym przypadku zamiast robić to na poziomie db, zrób to na poziomie aplikacji.
źródło