关于Android自动删除损坏的SQLite文件这一事实可以做些什么?

时间:2011-10-14 08:24:51

标签: android sqlite

当Android打开SQLite文件且文件已损坏时,Android 会删除该文件。

虽然听起来很令人惊讶,但这种行为在Android源代码中明确实现,导致consternation和此Android issue

无论如何,作为应用程序开发人员,我们只需处理它。打开SQLite文件时最好的策略是什么?

  • 损坏的文件实际上通常是可以恢复的,因此我们承担不起丢失其中一个损坏文件的风险。
  • 在打开之前创建一个备份是非常耗时的,并且会让应用启动变得非常慢,所以任何更聪明的东西都会非常感激。

4 个答案:

答案 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-upone of the solutions

如果可以使用SQLite source and modifies并在JNI or Library的应用程序中使用它,那么我们就可以实现它。

感谢。

答案 3 :(得分:0)

解决此问题的一个简单方法是完全复制数据库。

例如,在你的应用程序的on Destroy方法中。在每个Destroy上复制数据库,当主数据库损坏(并被android删除)时,你可以切换到备份数据库。