当Android打开SQLite文件且文件已损坏时,Android 会删除该文件。
虽然听起来很令人惊讶,但这种行为在Android源代码中明确实现,导致consternation和此Android issue。
无论如何,作为应用程序开发人员,我们只需处理它。打开SQLite文件时最好的策略是什么?
答案 0 :(得分:10)
此问题已从API级别11开始修复。现在存在一个界面: DatabaseErrorHandler ,您可以实现该界面来定义自己的 onCorruption()方法。在数据库打开时,您可以将此DatabaseErrorHandler作为参数传递给 SQLiteOpenHelper 的构造函数。
e.g。
public class MyDbErrorHandler implements DatabaseErrorHandler {
@Override
onCorruption(SQLiteDatabase db) {
// Back up the db or do some other stuff
}
}
SQLiteOpenHelper dbHelper = new SQLiteOpenHelper(context, "MyDbName", null, 1,
new MyDbErrorHandler());
SQLiteDatabase db = dbHelper.getWritableDatabase();
对于API级别低于11的系统,对于那些不想使用此方法的系统,有几种选择。
<强> 1。 Android数据备份
Android提供备份服务,可自动将应用程序数据复制到远程“云”存储。如果数据库损坏或在出厂重置后重新安装应用程序。可以从远程数据恢复应用程序数据。
有关详细信息,请参阅:http://developer.android.com/guide/topics/data/backup.html
<强> 2。 JDBC(sqldroid)
一种方法可能是实现您自己的数据库连接器,可以是本机JDBC,也可以是 sqldroid library 。谷歌官方不支持它,你不能确定它是否仍然可以在未来的Android版本中使用。
第3。 Berkley DB Java版
一种有趣的方法,也是处理大量数据的性能,是 Berkley DB Java Edition 。
以下是如何在Android中使用它的教程:http://download.oracle.com/docs/cd/E17277_02/html/HOWTO-Android.html
<强> 4。自定义android库
另一种更危险的方法是通过从android源代码复制或扩展SQLiteDatabase.java来实现自己的数据库类,并重新实现或覆盖以下关键部分:
public static SQLiteDatabase openDatabase(String path, CursorFactory factory, int flags) {
SQLiteDatabase sqliteDatabase = null;
try {
// Open the database.
sqliteDatabase = new SQLiteDatabase(path, factory, flags);
if (SQLiteDebug.DEBUG_SQL_STATEMENTS) {
sqliteDatabase.enableSqlTracing(path);
}
if (SQLiteDebug.DEBUG_SQL_TIME) {
sqliteDatabase.enableSqlProfiling(path);
}
} catch (SQLiteDatabaseCorruptException e) {
// Try to recover from this, if we can.
// TODO: should we do this for other open failures?
Log.e(TAG, "Deleting and re-creating corrupt database " + path, e);
EventLog.writeEvent(EVENT_DB_CORRUPT, path);
if (!path.equalsIgnoreCase(":memory")) {
// delete is only for non-memory database files
new File(path).delete();
}
sqliteDatabase = new SQLiteDatabase(path, factory, flags);
}
ActiveDatabases.getInstance().mActiveDatabases.add(
new WeakReference<SQLiteDatabase>(sqliteDatabase));
return sqliteDatabase;
}
和
/* package */ void onCorruption() {
Log.e(TAG, "Removing corrupt database: " + mPath);
EventLog.writeEvent(EVENT_DB_CORRUPT, mPath);
try {
// Close the database (if we can), which will cause subsequent operations to fail.
close();
} finally {
// Delete the corrupt file. Don't re-create it now -- that would just confuse people
// -- but the next time someone tries to open it, they can set it up from scratch.
if (!mPath.equalsIgnoreCase(":memory")) {
// delete is only for non-memory database files
new File(mPath).delete();
}
}
}
关于这一点的危险部分是,您还必须重新实现访问SQLiteDatabase的帮助程序类,例如SQLiteOpenHelper
。由于SQLiteDatabase
类使用工厂方法,您可能会遇到意外的副作用。
答案 1 :(得分:2)
我遇到了同样的问题并提出了一个问题here。我经常将我的数据库备份到SD卡,但我不推荐它。似乎从旧版Android手机中使用的SD卡中复制的数据库在旧版本的SQLite上完成复制后仍被视为损坏,该版本仍然在Android 2.3.6上使用。
如果您的数据库足够小,那么我建议保留备份,但将其保留在内部存储器中。除非它会激怒你的用户,否则不要启用“安装到SD卡”选项,我相信它与问题相关。在这些预防措施之后,您的数据库应该相对安全。
关于较慢的开始时间:当应用程序关闭时,我在后台线程中进行备份,这是手机最有可能在不麻烦用户的情况下完成后台工作的时间。
答案 2 :(得分:1)
Not for at opening every time
,但我认为当时我们的database make a changes or upgrades
make copy of DB files for back-up
是one of the solutions
。
如果可以使用SQLite source and modifies
并在JNI or Library
的应用程序中使用它,那么我们就可以实现它。
感谢。
答案 3 :(得分:0)
解决此问题的一个简单方法是完全复制数据库。
例如,在你的应用程序的on Destroy方法中。在每个Destroy上复制数据库,当主数据库损坏(并被android删除)时,你可以切换到备份数据库。