我已经使用提供的 FileBackupHelper
实现了一个 BackupAgentHelper
来备份和恢复我拥有的本机数据库。这是您通常与 ContentProviders
一起使用的数据库,位于 /data/data/yourpackage/databases/
中。
人们可能会认为这是一个常见的情况。然而,文档并不清楚该怎么做: http://developer.android .com/guide/topics/data/backup.html。 没有专门针对这些典型数据库的BackupHelper
。因此,我使用了 FileBackupHelper
,将其指向“/databases/
”中的 .db 文件,在任何数据库操作周围引入了锁(例如 db.insert
) 在我的 ContentProviders
中,甚至尝试在 onRestore()
之前创建“/databases/
”目录,因为它安装后不存在。
我过去曾在不同的应用程序中成功地为 SharedPreferences
实现了类似的解决方案。然而,当我在 emulator-2.2 中测试我的新实现时,我看到正在从日志执行到 LocalTransport
的备份,以及正在执行的恢复(以及 onRestore()
> 称为)。 但是,数据库文件本身永远不会创建。
请注意,这都是在安装之后、首次启动应用程序之前、执行恢复之后。除此之外,我的测试策略基于 http://developer.android。 com/guide/topics/data/backup.html#Testing。
另请注意,我不是在谈论我自己管理的某些 sqlite 数据库,也不是在谈论备份到 SD 卡、自己的服务器或其他地方。
我确实在文档中看到有关建议使用自定义 BackupAgent
的数据库的提及,但它似乎并不相关:
但是,您可能想要扩展
如果您需要,请直接 BackupAgent:
* 备份数据库中的数据。如果您有一个 SQLite 数据库
当用户想要恢复时
重新安装您的应用程序,您需要
构建一个自定义的 BackupAgent
期间读取适当的数据
备份操作,然后创建您的
表并在期间插入数据
恢复操作。
请说清楚。
如果我确实需要自己完成 SQL 级别的工作,那么我会担心以下主题:
-
打开数据库和事务。我不知道如何从应用程序工作流程之外的此类单例类中关闭它们。
-
如何通知用户备份正在进行中并且数据库已锁定。这可能需要很长时间,所以我可能需要显示一个进度条。
-
如何在恢复时执行相同的操作。据我了解,恢复可能会在用户已经开始使用应用程序(并将数据输入数据库)时发生。因此,您不能假设只是将备份的数据恢复到位(删除空数据或旧数据)。您必须以某种方式加入它,这对于任何重要的数据库来说都是不可能的,因为 ID 的原因。
-
如何在恢复完成后刷新应用程序,而不会让用户卡在某些 - 现在 - 无法访问的点。
-
我能否确定数据库已在备份或恢复时升级?否则,预期的架构可能不匹配。
I have implemented a BackupAgentHelper
using the provided FileBackupHelper
to backup and restore the native database I have. This is the database you typically use along with ContentProviders
and which resides in /data/data/yourpackage/databases/
.
One would think this is a common case. However the docs aren't clear on what to do: http://developer.android.com/guide/topics/data/backup.html. There is no BackupHelper
specifically for these typical databases. Hence I used the FileBackupHelper
, pointed it to my .db file in "/databases/
", introduced locks around any db operation (such as db.insert
) in my ContentProviders
, and even tried creating the "/databases/
" directory before onRestore()
because it does not exist after install.
I have implemented a similar solution for the SharedPreferences
successfully in a different app in the past. However when I test my new implementation in the emulator-2.2, I see a backup being performed to LocalTransport
from the logs, as well as a restore being performed (and onRestore()
called). Yet, the db file itself is never created.
Note that this is all after an install, and before first launch of the app, after the restore has been performed. Apart from that my test strategy was based on http://developer.android.com/guide/topics/data/backup.html#Testing.
Please also note I'm not talking about some sqlite database I manage myself, nor about backing up to SDcard, own server or elsewhere.
I did see a mention in the docs about databases advising to use a custom BackupAgent
but it does not seem related:
However, you might want to extend
BackupAgent directly if you need to:
* Back up data in a database. If you have an SQLite database that you
want to restore when the user
re-installs your application, you need
to build a custom BackupAgent that
reads the appropriate data during a
backup operation, then create your
table and insert the data during a
restore operation.
Some clarity please.
If I really need to do it myself up to the SQL level, then I'm worried about the following topics:
-
Open databases and transactions. I have no idea how to close them from such a singleton class outside of my app's workflow.
-
How to notify the user that a backup is in progress and the database is locked. It might take a long time, so I might need to show a progress bar.
-
How to do the same on restore. As I understand, the restore might happen just when the user has already started using the app (and inputting data into the database). So you can't presume to just restore the backupped data in place (deleting the empty or old data). You'll have to somehow join it in, which for any non-trivial database is impossible due to the id's.
-
How to refresh the app after the restore is done without getting the user stuck at some - now - unreachable point.
-
Can I be sure the database has already been upgraded on backup or restore? Otherwise the expected schema might not match.
发布评论
评论(7)
一种选择是将其构建在数据库之上的应用程序逻辑中。我认为它实际上尖叫着这样的水平。
不确定你是否已经这样做了,但大多数人(尽管有 android 内容管理器光标方法)会引入一些 ORM 映射 - 自定义或一些 orm-lite 方法。在这种情况下我宁愿做的是:
添加应用程序/数据后就可以了
新数据的背景
应用程序时添加/删除
已经开始
Java->protobuf 甚至简单的 java
序列化映射并编写您的
自带BackupHelper读取数据
从流中并将其添加到
数据库....
所以在这种情况下,不是在数据库级别上执行,而是在应用程序级别上执行。
One option will be to build it in application logic above the database. It actually screams for such levell I think.
Not sure if you are doing it already but most people (despite android content manager cursor approach) will introduce some ORM mapping - either custom or some orm-lite approach. And what I would rather do in this case is:
fine when the app/data is added in
the background with new data
added/removed while the application
already started
Java->protobuf or even simply java
serialization mapping and write your
own BackupHelper to read the data
from the stream and simply add it to
database....
So in this case rather than doing it on db level do it on application level.
文档建议扩展
BackupAgent
而不是BackupAgentHelper
当您需要对正在备份的数据进行精细控制时,数据库在此处特别命名:这是一个有效的示例,因为线程中的其他答案都不适合我(尽管我希望相对路径黑客!)
关于使用自定义备份代理的一些注意事项:
您不需要用于数据提取规则和旧备份规则的 XML。自定义代理覆盖自动备份系统而不是扩展它。
另请注意,运行
bmgr backupnow
时看不到任何进度日志是正常的,就像在没有自定义代理的情况下看到的那样。最后,在使用模拟器进行测试期间,备份不会像您所期望的那样保存在
/data/backup/com.android.localtransport.LocalTransport
中。它是模拟器的默认传输(否则可以手动设置),但此处找不到备份文件。The docs suggest extending
BackupAgent
and notBackupAgentHelper
when you require granular control over the data you are backing up and databases are specifically named here:This is a working example of that because none of the other answers in the thread worked for me (though I had hope for the relative path hack!)
A few notes on using a custom backup agent:
You don't need the XMLs for data extraction rules and the legacy backup rules. The custom agent overrides the auto backup system instead of extending it.
Also note that it's normal not to see any progress logs when running
bmgr backupnow
as you would have seen without the custom agent.And finally, during testing with e.g. an emulator, the backups are not saved in
/data/backup/com.android.localtransport.LocalTransport
as you might expect. It is the default transport for emulators (and otherwise can be manually set), but the backup files are not found here.重新审视我的问题后,我在查看 ConnectBot 如何做到这一点。谢谢肯尼和杰弗里!
实际上就像添加:到您的
BackupAgentHelper
一样简单。
我忽略的一点是,您必须使用带有“
../databases/
”的相对路径。尽管如此,这绝不是一个完美的解决方案。
FileBackupHelper
的文档提到,例如:“FileBackupHelper
应该仅用于小型配置文件,而不是大型二进制文件。”,后者是SQLite 数据库就是这样。我想获得更多建议,深入了解我们的期望(正确的解决方案是什么),以及如何解决这个问题的建议。
After revisiting my question, I was able to get it to work after looking at how ConnectBot does it. Thanks Kenny and Jeffrey!
It's actually as easy as adding:
to your
BackupAgentHelper
.The point I was missing was the fact that you'd have to use a relative path with "
../databases/
".Still, this is by no means a perfect solution. The docs for
FileBackupHelper
mention for instance: "FileBackupHelper
should be used only with small configuration files, not large binary files.", the latter being the case with SQLite databases.I'd like to get more suggestions, insights into what is expected of us (what is the proper solution), and advice on how this might break.
这是将数据库备份为文件的更简洁的方法。没有硬编码路径。
注意:它覆盖 getFilesDir 以便 FileBackupHelper 在数据库目录中工作,而不是文件目录。
另一个提示:您还可以使用 databaseList 来获取所有数据库和将此列表中的名称(不带父路径)馈送到 FileBackupHelper 中。然后所有应用程序的数据库将保存在备份中。
Here's yet cleaner way to backup databases as files. No hardcoded paths.
Note: it overrides getFilesDir so that FileBackupHelper works in databases dir, not files dir.
Another hint: you may also use databaseList to get all your DB's and feed names from this list (without parent path) into FileBackupHelper. Then all app's DB's would be saved in backup.
更简洁的方法是创建自定义
BackupHelper
:然后将其添加到
BackupAgentHelper
:A cleaner approach would be to create a custom
BackupHelper
:and then add it to
BackupAgentHelper
:使用
FileBackupHelper
备份/恢复 sqlite 数据库会引发一些严重的问题:1. 如果应用程序使用从
ContentProvider.query()
检索的游标并且备份代理尝试覆盖整个文件,会发生什么情况?2. 链接是完美(低熵;)测试。您卸载应用程序,重新安装它,备份就会恢复。然而生活可能是残酷的。查看
无论用户做什么,他/她都无法在新设备上使用您的应用。
我建议使用
ContentResolver
来获取数据 ->序列化(不带_id
s)以进行备份和反序列化 ->插入数据进行恢复。注意:获取/插入数据是通过 ContentResolver 完成的,从而避免了并发问题。序列化是在您的 backupAgent 中完成的。如果您自己进行光标<->对象映射,序列化一个项目就可以像在表示您的实体的类上使用
transient
字段_id实现Serialized
一样简单。我还使用批量插入,即
ContentProviderOperation
example 和CursorLoader.setUpdateThrottle
以便应用程序不会在备份恢复过程中因数据更改而重新启动加载程序。如果您碰巧遇到降级情况,您可以选择中止恢复数据或使用与降级版本相关的字段恢复和更新 ContentResolver。
我同意这个主题并不容易,文档中没有很好地解释,并且一些问题仍然存在,例如批量数据大小等。
希望这会有所帮助。
Using
FileBackupHelper
to backup/restore sqlite db raises some serious questions:1. What happens if the app uses cursor retrieved from
ContentProvider.query()
and backup agent tries to override the whole file?2. The link is a nice example of perfect (low entrophy ;) testing. You uninstall app, install it again and the backup is restored. However life can be brutal. Take a look at link. Let's imagine scenario when a user buys a new device. Since it doesn't have its own set, the backup agent uses other device's set. The app is installed and your backupHelper retrieves old file with db version schema lower than the current.
SQLiteOpenHelper
callsonDowngrade
with the default implementation:No matter what the user does he/she can't use your app on the new device.
I'd suggest using
ContentResolver
to get data -> serialize (without_id
s) for backup and deserialize -> insert data for restore.Note: get/insert data is done through ContentResolver thus avoiding cuncurrency issues. Serializing is done in your backupAgent. If you do your own cursor<->object mapping serializing an item can be as simple as implementing
Serializable
withtransient
field _id on the class representing your entity.I'd also use bulk insert i.e.
ContentProviderOperation
example andCursorLoader.setUpdateThrottle
so that the app is not stuck with restarting loader on data change during backup restore process.If you happen do be in a situation of a downgrade, you can choose either to abort restore data or restore and update ContentResolver with fields relevant to the downgraded version.
I agree that the subject is not easy, not well explained in docs and some questions still remain like bulk data size etc.
Hope this helps.
从 Android M 开始,现在有一个可供应用程序使用的完整数据备份/恢复 API。这个新的 API 在应用程序清单中包含基于 XML 的规范,使开发人员能够以直接语义方式描述要备份的文件:“备份名为“mydata.db”的数据库”。这个新的 API 对于开发人员来说更容易使用——您不必跟踪差异或显式请求备份通道,并且要备份的文件的 XML 描述意味着您通常不需要编写任何代码根本不。
(例如,您甚至甚至可以参与完整数据备份/恢复操作,以便在恢复发生时获得回调。这样很灵活。)
请参阅为应用程序配置自动备份部分位于developer.android.com,了解如何使用新 API 的说明。
As of Android M, there is now a full-data backup/restore API available to apps. This new API includes an XML-based specification in the app manifest that lets the developer describe which files to back up in a direct semantic way: 'back up the database called "mydata.db"'. This new API is much easier for developers to use -- you don't have to keep track of diffs or request a backup pass explicitly, and the XML description of which files to back up means you often don't need to write any code at all.
(You can get involved even in a full-data backup/restore operation to get a callback when the restore happens, for example. It's flexible that way.)
See the Configuring Auto Backup for Apps section on developer.android.com for a description of how to use the new API.