【问题标题】:Android SQLite database gets corruptedAndroid SQLite 数据库损坏
【发布时间】:2010-05-05 15:45:22
【问题描述】:

这个链接准确地描述了我的问题:http://old.nabble.com/Android-database-corruption-td28044218.html#a28044218

现在大约有 300 人在使用我的 Android 应用程序,并且每次我都会通过此堆栈跟踪向服务器发送崩溃报告:

android.database.sqlite.SQLiteDatabaseCorruptException: database disk image is malformed
    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2596)
    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2621)
    at android.app.ActivityThread.access$2200(ActivityThread.java:126)
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1932)
    at android.os.Handler.dispatchMessage(Handler.java:99)
    at android.os.Looper.loop(Looper.java:123)
    at android.app.ActivityThread.main(ActivityThread.java:4595)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:521)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:860)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:618)
    at dalvik.system.NativeStart.main(Native Method) Caused by: android.database.sqlite.SQLiteDatabaseCorruptException: database disk image is malformed
    at android.database.sqlite.SQLiteQuery.native_fill_window(Native Method)
    at android.database.sqlite.SQLiteQuery.fillWindow(SQLiteQuery.java:75)
    at android.database.sqlite.SQLiteCursor.fillWindow(SQLiteCursor.java:295)
    at android.database.sqlite.SQLiteCursor.getCount(SQLiteCursor.java:276)
    at android.database.AbstractCursor.moveToPosition(AbstractCursor.java:171)
    at android.database.AbstractCursor.moveToFirst(AbstractCursor.java:248)

结果是应用程序崩溃,数据库中的所有数据都丢失了。

需要注意的一点是,每次我读取或写入数据库时​​,我都会获得一个新的 SQLiteDatabase,并在完成后立即关闭它。我这样做是为了防止此类损坏错误。

我还尝试使用单个静态对象同步所有数据库读取和写入,但这似乎没有帮助。

这可能只是一个 SQLite 错误吗?

我在这里发现了与内置电子邮件应用程序类似的错误:http://code.google.com/p/android/issues/detail?id=5610

这是我的代码:

public class KeyValueTableAdapter extends BaseTableAdapter {

    private String tableName;
    private String keyColumnName;
    private String valueColumnName;

    public KeyValueTableAdapter(Context context, String tableName, String keyColumnName, String valueColumnName) {
        super(context);
        this.tableName = tableName;
        this.keyColumnName = keyColumnName;
        this.valueColumnName = valueColumnName;
    }

    protected String getStringValue(int key) {
        Cursor cursor = null;
        SQLiteDatabase db = null;
        String value;

        try {
            db = dbOpenHelper.getReadableDatabase();
            cursor = db.query(true, tableName, new String[] { valueColumnName }, keyColumnName + "=" + key, null, null, null, null, null);

            if ((cursor.getCount() == 0) || !cursor.moveToFirst()) {
                value = null;
            } else {
                value = cursor.getString(0);
            }
        } finally {
            if (cursor != null) cursor.close();
            if (db != null) db.close();
            dbOpenHelper.close();
        }

        return value;
    }
}


public abstract class BaseTableAdapter {

    protected DbOpenHelper dbOpenHelper;

    public BaseTableAdapter(Context context) {
        this.dbOpenHelper = new DbOpenHelper(context, DatabaseSettings.DATABASE_NAME, null, DatabaseSettings.DATABASE_VERSION);
    }

}

【问题讨论】:

  • 您在数据库上使用触发器吗?
  • 不,这个特定的表只是一个“键/值表”。两列:一个主键(整数)和另一个非空(文本)。你认为使用文本可能是问题吗?我对 SQLite 的打字有点不熟悉。
  • SQLite 是无类型的,这不算。您可以在日期字段中推送文本:)
  • 这就是我的想法,而且它在 cursor.moveToFirst() 处失败,而不是 getString 或 getInt 或其他。
  • 我有一个损坏的数据库问题,当我在事务中发出一些删除/创建表/视图/触发器时。事实证明,SQLite 事务只能保存特定于表的查询,而不是与架构相关的查询。

标签: android sqlite


【解决方案1】:

数据库进程很可能在 I/O 期间被终止。例如,通过任务杀手,或者如果您允许在应用程序应该关闭或睡眠时继续执行数据库写入操作......

看看您是否可以通过将您的应用置于数据库写入循环中并在其上使用任务杀手来重现该问题。

场景:32 字节被写入数据库,写入任务仅写入 10 字节后被杀死,结果:数据库处于不一致且可能损坏的状态。

另见: Android process killer

编辑:为每个读/写打开和关闭数据库?不要那么做! :)

【讨论】:

  • 谢谢,我真的回到这里准确地报告了这一点。我可以使用任务杀手应用程序重新创建它。根据这篇文章,这是 SQL lite 中的一个错误:pubbs.net/201003/sqlite/…
  • 另外,我试图为每次写入打开和关闭数据库的原因是为了避免这个问题。那不就解决问题了吗?用户必须非常幸运并在打开和关闭之间杀死它。
  • 这是 SQLite 中的一个错误(根据您对该答案的第一条评论),我现在可以得到赏金吗:p
  • 即使数据库写入被随机中止,它也不应该损坏——这就是重点。因此,要么在提交/恢复逻辑中存在 sqlite 错误,要么存在与 fsync(或等效)有关的操作系统错误 - 并且为每次读写打开和关闭数据库应该没有问题,尽管它可能会降低性能(再说一次,它可能不会,特别是如果你有一堆写)
  • 这也发生在我不使用任务杀手的用户身上。我非常感谢您的回答,但我也在尝试在不使用任务杀手的情况下找出解决方法。我接近放弃 SQLite 并使用文件或共享首选项滚动我自己的持久层。
【解决方案2】:

如果您有两个实例同时更新同一个数据库文件,那么使用多个 SQLiteDatabase 实例可能会导致您的问题。

【讨论】:

  • 我只是在我的一个应用程序中这样做,没有任何问题...... sqlite 在写操作期间锁定数据库文件,因此它们在文件系统级别实际上是原子的(因此是线程安全的)。跨度>
【解决方案3】:

每天执行一个备份数据库进程,如果数据库损坏,您只需用备份替换数据库即可。您可以使用简单的文件复制粘贴方法来维护每天的备份。

【讨论】:

  • 谢谢,但是数据库保存着会话信息,所以做备份不太可行。数据每分钟都在变化,对于应用程序的运行至关重要。
  • 如果您不想失去一切,这仍然是一种备用方式。我知道这是地球上最后要做的事情。
【解决方案4】:

因为我还不能评论 Brad 的帖子。

我不得不同意额外的开销。

我有一部 HTC Magic 作为我的日常手机,而且内存一直是个问题。

Android 手机处于非常不同的阶段

有些超级便宜,有些超级贵,这基本上归结为ram和cpu。

运行任务杀手的人确实毁了他们的安卓手机。

作为开发人员,您应该建议人们不要使用它们,或者只是拒绝支持使用任务杀手的人,因为 android 不需要这些“改进”(问 steve (cyanogen))

另外,android 中的new 语句非常昂贵。

在为 android 编程时,您希望限制 new 调用的数量。

android 编程就是重用宝贵的内存。 (HTC Magics/Dreams 只有 96MB 可供应用程序使用,其中大部分已在使用中)

至于您的 SQLiteDB...API 表示您的 SQLiteDB 对您的应用程序是私有的。

我不明白为什么每次要读取或写入时都需要打开和关闭与它的新连接。

我宁愿保持连接打开,直到用户失去焦点。

但是,如果您正在编写内容提供商,那就另当别论了。

【讨论】:

  • 它仍然每天发生大约 5 次,所以我很确定这不仅仅是人们使用任务杀手。我刚刚发现我有时会使用任务杀手导致同样的异常。至于每次打开和关闭数据库连接——我只是想说我愿意增加额外的开销,只要数据库不会损坏(虽然我不确定打开/关闭是否真的能解决问题,我只是在尝试一切)
  • 这可能是 android 中的 SQLite 错误,但 android 设备通常资源非常有限。因此,创建尽可能少的开销将提高任何 android 应用程序的速度和稳定性。似乎您正在为每个查询创建不必要的垃圾。操作系统的 lowMemoryKiller 可能会产生与任务杀手相同的问题。因为您的应用程序不如其他系统服务重要。
【解决方案5】:

"数据库保存会话信息,所以 做备份不是很可行。 数据每分钟都在变化”

您应该尝试使用 SharedPreferences:它存储键值对(在后台,它使用文件)。 存储值:

SharedPreferences sp=MyActivity.getSharedPreferences("Name", Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sp.edit();
editor.putString("key", value);
editor.putBoolean("another", true);
editor.commit();

检索数据:

sp.getString("key", "Not found"); 
// "Not found" is the default value
// if sp does not contain the specified key
sp.getBoolean("another", false); 
// false is the default value
// if sp does not contain the specified key

有关更详细的说明,请参阅 getSharedPreferencesSharedPreferences

【讨论】:

  • 问题与数据库有关,因此使用共享首选项的解决方案不是解决方案。
  • 问题与数据库有关,因此不使用数据库可能是一种解决方案,尤其是当基于 Brandon 的 cmets 时,它甚至不是存储他的数据的最佳方式(小,经常更改的 key-值对)。
  • 这里的重点是解决问题而不是解决它。通常变通办法有办法回击你。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-11-21
  • 2011-12-25
  • 2011-11-22
  • 1970-01-01
  • 2014-03-17
  • 1970-01-01
相关资源
最近更新 更多