J'ai implémenté un en BackupAgentHelper
utilisant le fourni FileBackupHelper
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, la documentation n'est pas claire sur ce qu'il faut faire: http://developer.android.com/guide/topics/data/backup.html . Il n'y a pas BackupHelper
spécifiquement pour ces bases de données typiques. Par conséquent, j'ai utilisé leFileBackupHelper
, je l'ai pointé vers mon fichier .db dans " /databases/
", j'ai introduit des verrous autour de toute opération de base de données (comme db.insert
) dans my ContentProviders
, et j'ai même essayé de créer le /databases/
répertoire " " avant onRestore()
car il n'existe pas après l'installation.
J'ai implémenté une solution similaire pour le SharedPreferences
succès dans une application différente dans le passé. Cependant, lorsque je teste ma nouvelle implémentation dans l'émulateur-2.2, je vois une sauvegarde effectuée à LocalTransport
partir des journaux, ainsi qu'une restauration en cours (et onRestore()
appelée). Pourtant, le fichier db lui-même n'est jamais créé.
Notez que tout cela se produit après une installation et avant le premier lancement de l'application, une fois la restauration effectuée. En dehors de cela, ma stratégie de test était basée sur http://developer.android.com/guide/topics/data/backup.html#Testing .
Veuillez noter également que je ne parle pas d'une base de données sqlite que je gère moi-même, ni d'une sauvegarde sur SDcard, sur mon propre serveur ou ailleurs.
J'ai vu une mention dans la documentation sur les bases de données conseillant d'utiliser une personnalisation BackupAgent
mais cela ne semble pas lié:
Cependant, vous souhaiterez peut-être étendre BackupAgent directement si vous avez besoin de: * Sauvegarder les données dans une base de données. Si vous disposez d'une base de données SQLite que vous souhaitez restaurer lorsque l'utilisateur réinstalle votre application, vous devez créer un BackupAgent personnalisé qui lit les données appropriées lors d'une opération de sauvegarde, puis créez votre table et insérez les données lors 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, je m'inquiète pour les sujets suivants:
Ouvrez les bases de données et les transactions. Je ne sais pas comment les fermer à partir d'une telle classe singleton en dehors du flux de travail de mon application.
Comment informer l'utilisateur qu'une sauvegarde est en cours et que la base de données est verrouillée. Cela peut prendre un certain temps, donc je devrai peut-être afficher une barre de progression.
Comment faire de même lors de la restauration. Si je comprends bien, la restauration peut se produire juste lorsque l'utilisateur a déjà commencé à utiliser l'application (et à saisir des données dans la base de données). Vous ne pouvez donc pas prétendre simplement restaurer les données sauvegardées sur place (en supprimant les données vides ou anciennes). Vous devrez en quelque sorte le rejoindre, ce qui pour toute base de données non triviale est impossible en raison des identifiants.
Comment actualiser l'application une fois la restauration terminée sans que l'utilisateur soit bloqué à un point - maintenant - inaccessible.
Puis-je être sûr que la base de données a déjà été mise à niveau lors d'une sauvegarde ou d'une restauration? Sinon, le schéma attendu pourrait ne pas correspondre.
Réponses:
Une approche plus propre consisterait à créer une personnalisation
BackupHelper
:public class DbBackupHelper extends FileBackupHelper { public DbBackupHelper(Context ctx, String dbName) { super(ctx, ctx.getDatabasePath(dbName).getAbsolutePath()); } }
puis ajoutez-le à
BackupAgentHelper
:public void onCreate() { addHelper(DATABASE, new DbBackupHelper(this, DB.FILE)); }
la source
Après avoir revisité ma question, j'ai pu la faire fonctionner après avoir examiné comment ConnectBot le fait . Merci Kenny et Jeffrey!
C'est en fait aussi simple que d'ajouter:
FileBackupHelper hosts = new FileBackupHelper(this, "../databases/" + HostDatabase.DB_NAME); addHelper(HostDatabase.DB_NAME, hosts);
à votre
BackupAgentHelper
.Le point qui me manquait était le fait que vous deviez utiliser un chemin relatif avec "
../databases/
".Pourtant, ce n'est en aucun cas une solution parfaite. La documentation
FileBackupHelper
mentionne par exemple: "FileBackupHelper
ne doit être utilisé qu'avec de petits fichiers de configuration, pas de gros fichiers binaires. ", Ce dernier étant le cas avec les bases de données SQLite.J'aimerais avoir plus de suggestions, des informations sur ce que l'on attend de nous (quelle est la bonne solution) et des conseils sur la façon dont cela pourrait casser.
la source
db.open()
traversdb.close()
ou juste desinsert
déclarations ou quoi.Voici un moyen encore plus propre de sauvegarder les bases de données sous forme de 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 pour que FileBackupHelper fonctionne dans le répertoire des bases de données, pas dans le répertoire des fichiers.
Un autre conseil: vous pouvez également utiliser databaseList pour obtenir tous vos noms de base de données et de flux de cette liste (sans chemin parent) dans FileBackupHelper. Ensuite, toutes les bases de données de l'application seraient enregistrées dans la sauvegarde.
la source
Utiliser
FileBackupHelper
pour sauvegarder / restaurer sqlite db soulève de sérieuses questions:1. Que se passe-t-il si l'application utilise le curseur récupéré
ContentProvider.query()
et que l'agent de sauvegarde tente de remplacer le fichier entier?2. Le lien est un bel exemple de test parfait (faible entrophie;). Vous désinstallez l'application, réinstallez-la et la sauvegarde est restaurée. Cependant, la vie peut être brutale. Jetez un œil au lien . Imaginons un scénario lorsqu'un utilisateur achète un nouvel appareil. Puisqu'il n'a pas son propre ensemble, l'agent de sauvegarde utilise l'ensemble d'un autre périphérique. L'application est installée et votre backupHelper récupère l'ancien fichier avec un schéma de version de base de données inférieur à l'actuel.
SQLiteOpenHelper
appelsonDowngrade
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 fait l'utilisateur, il ne peut pas utiliser votre application sur le nouvel appareil.
Je suggérerais d'utiliser
ContentResolver
pour obtenir des données -> sérialiser (sans_id
s) pour la sauvegarde et désérialiser -> insérer des données pour la restauration.Remarque: obtenir / insérer des données se fait via ContentResolver, évitant ainsi les problèmes de devises. La sérialisation est effectuée dans votre backupAgent. Si vous faites votre propre mappage d'objet curseur <->, la sérialisation d'un élément peut être aussi simple que l'implémentation
Serializable
avec letransient
champ _id sur la classe représentant votre entité.J'utiliserais également un
ContentProviderOperation
exemple d' insertion en masse, par exemple, etCursorLoader.setUpdateThrottle
pour que l'application ne soit pas bloquée avec le redémarrage du chargeur lors du changement de données pendant le processus de restauration de sauvegarde.Si vous vous trouvez dans une situation de rétrogradation, vous pouvez choisir d'interrompre la restauration des données ou de restaurer et de mettre à jour ContentResolver avec des champs correspondant à la version rétrogradée.
Je conviens que le sujet n'est pas facile, pas bien expliqué dans la documentation et que certaines questions subsistent comme la taille des données en vrac, etc.
J'espère que cela t'aides.
la source
À partir d'Android M, une API de sauvegarde / restauration complète des données est désormais 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 de 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 n'avez pas besoin de suivre les différences ou de demander une passe de sauvegarde explicitement, et la description XML des fichiers à sauvegarder signifie que vous n'avez souvent pas besoin d'écrire de code du tout.
(Vous pouvez vous impliquer même dans une opération de sauvegarde / restauration de données complètes pour obtenir un rappel lorsque la restauration a lieu, par exemple. C'est flexible de cette façon.)
Consultez la section Configuration de la sauvegarde automatique pour les applications sur developer.android.com pour obtenir une description de l'utilisation de la nouvelle API.
la source
Une option sera de le construire dans la logique d'application au-dessus de la base de données. Il crie en fait pour un tel niveau, je pense. Je ne sais pas si vous le faites déjà, mais la plupart des gens (malgré l'approche du curseur du gestionnaire de contenu Android) introduiront un mappage ORM - soit une approche personnalisée, soit une approche orm-lite. Et ce que je préférerais faire dans ce cas est:
Donc, dans ce cas, plutôt que de le faire au niveau de la base de données, faites-le au niveau de l'application.
la source