Android sauvegarde/restauration: comment sauvegarder une base de données interne?
j'ai implémenté un BackupAgentHelper
en utilisant le FileBackupHelper
fourni pour sauvegarder et restaurer la base de données native que j'ai. Il s'agit de la base de données que vous utilisez généralement avec ContentProviders
et qui réside dans /data/data/yourpackage/databases/
.
on pourrait penser que c'est un cas courant. Cependant les docs ne sont pas clairs sur ce qu'il faut faire: http://developer.android.com/guide/topics/data/backup.html . il n'y a pas de BackupHelper
spécifiquement pour ces typique des bases de données. C'est pourquoi j'ai utilisé le FileBackupHelper
, Je l'ai pointé vers mon .fichier db dans " /databases/
", serrures introduit autour de toute opération db (comme db.insert
) dans mon ContentProviders
, et même essayé de créer le" /databases/
" répertoire avant onRestore()
parce qu'il n'existe pas après installation.
j'ai mis en œuvre une solution similaire pour le SharedPreferences
avec succès dans une application différente dans le passé. Cependant, lorsque je teste ma nouvelle implémentation dans le l'émulateur 2.2, je vois une sauvegarde de LocalTransport
à partir des journaux, ainsi qu'une restauration en cours (et onRestore()
appelé). pourtant, le fichier db lui-même n'est jamais créé.
Notez que c'est tout après une installation, et avant le premier lancement de l'application, après la restauration a été effectuée. En plus, ma stratégie de test était basée sur http://developer.android.com/guide/topics/data/backup.html#Testing .
veuillez noter que je ne parle pas d'une base de données sqlite que je gère moi-même, ni de sauvegarder sur SDcard, own server ou ailleurs.
j'ai vu une mention dans les documents sur les bases de données conseillant d'utiliser une coutume BackupAgent
mais il ne semble pas lié:
cependant, vous pourriez vouloir prolonger BackupAgent directement si vous avez besoin de: * Sauvegarder les données dans une base de données. Si vous avez une base de données SQLite que vous souhaitez restaurer lorsque l'utilisateur re-installe votre application, vous avez besoin pour construire un BackupAgent personnalisé qui lit les données appropriées pendant un opération de sauvegarde, puis de créer votre tableau et insérer les données au cours d'une opération de restauration.
un peu de clarté s'il vous plaît.
si j'ai vraiment besoin de le faire moi-même jusqu'au niveau SQL, alors je m'inquiète des sujets suivants:
-
Ouvrir des bases de données et des transactions. Je n'ai aucune idée de comment les fermer d'une telle classe singleton en dehors du workflow de mon application.
-
Comment informer l'utilisateur qu'une sauvegarde est en cours et la base de données est verrouillée. Ça pourrait prendre du temps, alors je devrais montrer une barre de progression.
-
Comment faire la même chose sur restaurer. Comme je le comprends, la restauration pourrait se produire juste au moment où l'utilisateur a déjà commencé à utiliser l'application (et la saisie des données dans la base de données). Ainsi, vous ne pouvez pas présumer de simplement restaurer les données récupérées en place (en supprimant les données vides ou anciennes). Vous devrez d'une façon ou d'une autre vous y joindre, ce qui pour toute base de données non triviale est impossible en raison des id.
-
comment rafraîchir l'application après la restauration est fait sans obtenir l'utilisateur coincé à un certain - maintenant - point inaccessible.
-
puis-je être sûr que la base de données a déjà été mise à jour sur la sauvegarde ou la restauration? Sinon, le schéma prévu pourrait ne pas correspondre.
6 réponses
une approche plus propre serait de créer une coutume BackupHelper
:
public class DbBackupHelper extends FileBackupHelper {
public DbBackupHelper(Context ctx, String dbName) {
super(ctx, ctx.getDatabasePath(dbName).getAbsolutePath());
}
}
et l'ajouter à BackupAgentHelper
:
public void onCreate() {
addHelper(DATABASE, new DbBackupHelper(this, DB.FILE));
}
après avoir réexaminé ma question, j'ai pu la faire fonctionner après avoir regardé how ConnectBot does it . Merci Kenny et Jeffrey!
c'est en fait aussi facile que d'ajouter:
FileBackupHelper hosts = new FileBackupHelper(this,
"../databases/" + HostDatabase.DB_NAME);
addHelper(HostDatabase.DB_NAME, hosts);
à votre BackupAgentHelper
.
le point que je manquais était le fait que vous deviez utiliser un chemin relatif avec ../databases/
".
Encore, ce n'est pas une solution parfaite. Doc pour FileBackupHelper
mentionner par exemple: " FileBackupHelper
ne doit être utilisé qu'avec de petits fichiers de configuration, Pas de gros fichiers binaires. ", ce dernier étant le cas des bases de données SQLite.
j'aimerais avoir plus de suggestions, un aperçu de ce que l'on attend de nous (quelle est la bonne solution), et des conseils sur la façon dont cela pourrait rompre.
Voici encore une façon plus propre de sauvegarder les bases de données en tant que fichiers. Pas de chemins codés en dur.
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();
}
}
Remarque: il remplace getFilesDir , de sorte que FileBackupHelper œuvres dans les bases de données dir, pas de fichiers dir.
un autre indice: Vous pouvez également utiliser databaseList pour obtenir tous vos noms de DB et de feed à partir de cette liste (sans chemin parent) dans FileBackupHelper. Alors tous les DB de l'application seraient enregistrés dans la sauvegarde.
L'utilisation de FileBackupHelper
pour sauvegarder / restaurer sqlite db soulève de sérieuses questions:
1. Qu'advient-il si l'application utilise le curseur extrait de ContentProvider.query()
et l'agent de sauvegarde essaie de remplacer l'ensemble du dossier?
2. Le link est un bel exemple de test parfait (faible entrophie;). Vous désinstallez l'application, l'installez à nouveau et la sauvegarde est restaurée. Mais la vie peut être brutale. Regardez le lien . Imaginons le scénario lorsqu'un utilisateur achète un nouvel appareil. Comme il n'a pas son propre jeu, l'agent de sauvegarde utilise le jeu d'autres périphériques. L'application est installée et votre backupHelper récupère l'ancien fichier avec le schéma de version db inférieur au courant. SQLiteOpenHelper
appels onDowngrade
avec l'implémentation par défaut:
public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
throw new SQLiteException("Can't downgrade database from version " +
oldVersion + " to " + newVersion);
}
peu importe ce que l'utilisateur fait, il/elle ne peut pas utiliser votre application sur le nouvel appareil.
Je suggère d'utiliser ContentResolver
pour obtenir des données -> serialize (sans _id
s) pour la sauvegarde et deserialize -> insert data pour la restauration.
Note: obtenir / insérer des données est fait par ContentResolver ce qui évite les problèmes de monnaie. Serializing est fait dans votre backupAgent. Si vous faites votre propre curseur<->cartographie objet sérialiser un élément peut être aussi simple que de mettre en œuvre Serializable
avec transient
field _id sur la classe représentant votre entité.
j'utiliserais aussi bulk insert i.e. ContentProviderOperation
exemple et CursorLoader.setUpdateThrottle
pour que l'application ne soit pas bloquée avec le chargeur redémarrant sur le changement de données pendant le processus de restauration de sauvegarde.
si vous vous trouvez dans une situation de déclassement, vous pouvez choisir soit d'annuler les données de restauration, soit de restaurer et de mettre à jour ContentResolver avec les champs pertinents à la version déclassée.
je suis d'accord que le sujet n'est pas facile, pas bien expliquées dans docs et certaines questions restent comme la taille des données en vrac, etc.
Espérons que cette aide.
depuis Android M, une API de sauvegarde/restauration complète des données est disponible pour les applications. Cette nouvelle API inclut une spécification basée sur XML dans le manifeste de l'application qui permet au développeur de décrire les fichiers à sauvegarder d'une manière sémantique directe: 'sauvegarder la base de données appelée "mydata.DB.'" Cette nouvelle API est beaucoup plus facile à utiliser pour les développeurs-vous ne devez pas garder la trace des diffs ou Demander un passe de sauvegarde explicitement, et la description XML des fichiers à sauvegarder signifie souvent que vous n'avez pas besoin de écrire du code.
(vous can participez même à une opération de sauvegarde/restauration de données complètes pour obtenir un rappel lorsque la restauration se produit, par exemple. Il est flexible de cette façon.)
voir la section configurer la sauvegarde automatique pour les applications developer.android.com pour une description de l'utilisation de la nouvelle API.
une option sera de le construire dans la logique de l'application au-dessus de la base de données. Il crie pour un tel nivellement je pense. Pas sûr si vous le faites déjà, mais la plupart des gens (malgré android content manager curseur approche) va introduire une certaine cartographie ORM - soit personnalisé ou une certaine approche orm-lite. Et ce que je préférerais faire dans ce cas est:
- pour s'assurer que votre application fonctionne amende lorsque l'Application / données est ajouté dans le fond avec nouveau données ajouté / supprimé pendant que l'application déjà commencé
- pour en faire Java - > protobuf ou même simplement java cartographie de la sérialisation et écrivez votre propre BackupHelper pour lire les données de la rivière et tout simplement l'ajouter à la base de données....
donc dans ce cas plutôt que de le faire au niveau db le faire au niveau application.