【问题标题】:android: Save to database in separate thread causes exception db is closedandroid:在单独的线程中保存到数据库导致异常数据库关闭
【发布时间】:2014-12-27 00:55:20
【问题描述】:

所以我尝试使用单独的线程将数据保存到数据库,但这会导致异常:

java.lang.IllegalStateException:数据库未打开

我认为这不是Loader 的任务,因为我不需要任何结果游标,它只是为了保存我的数据。保存到 db 代码如下所示:

Thread thread = new Thread(new Runnable() {
    @Override
    public void run() {
        dbHelper.saveMessages(messages);
    }
});
thread.setPriority(Thread.MIN_PRIORITY);
thread.start();

如果我从线程中删除此数据库操作,一切正常,但会导致 UI 滞后。如果我尝试从单独的线程读取数据库,也会发生同样的事情。
这是数据库编写代码的一部分:

    SQLiteDatabase db = dbAdapter.getWritableDatabase());
    try {
        if (existingMessage != null)
            insertId = db.update(Tables.Messages.TABLE_NAME, values, Tables.Messages.SERVER_ID + "=?", new String[]{String.valueOf(message.serverId)});
        else
            insertId = db.insertOrThrow(Tables.Messages.TABLE_NAME, null, values);
    } catch (Exception e) {
        e.printStackTrace();
    }

实际上异常发生在 db.update() 或 db.insertOrThrow() 行。它不保证会抛出异常,但它会不时发生。例如,它可以写入 5 个对象,然后在 6 日失败。
我在做什么错或 SQLiteOpenHelper 有什么问题? 完整的异常日志:

10-30 22:34:52.746  10431-10536/com.myapp W/System.err﹕ java.lang.IllegalStateException: database not open
10-30 22:34:52.746  10431-10536/com.myapp W/System.err﹕ at android.database.sqlite.SQLiteDatabase.queryWithFactory(SQLiteDatabase.java:1224)
10-30 22:34:52.746  10431-10536/com.myapp W/System.err﹕ at android.database.sqlite.SQLiteDatabase.query(SQLiteDatabase.java:1184)
10-30 22:34:52.746  10431-10536/com.myapp W/System.err﹕ at android.database.sqlite.SQLiteDatabase.query(SQLiteDatabase.java:1264)
10-30 22:34:52.746  10431-10536/com.myapp W/System.err﹕ at com.myapp.db.DatabaseHelper.saveMessages(DatabaseHelper.java:478)
10-30 22:34:52.746  10431-10536/com.myapp W/System.err﹕ at com.myapp.db.DatabaseHelper.requestMessages(DatabaseHelper.java:1245)
10-30 22:34:52.746  10431-10536/com.myapp W/System.err﹕ at com.myapp.activity.LoginActivity$9.run(LoginActivity.java:425)
10-30 22:34:52.746  10431-10536/com.myapp W/System.err﹕ at java.lang.Thread.run(Thread.java:1096)

更新:
我发现它实际上都是关于从 db 读取的。在我的代码中,我首先从 db 读取以确保没有这样的记录。之后我决定做什么——插入或更新。这个问题发生在可读的数据库上。在打开 db 进行读取之后,下一行应该从 db 读取,它显示为“未打开”。而且没有阅读线程,一切都发生在一个线程中:第一次读取然后写入。然而,随机阅读有时会失败。所以它不像我之前提到的那样写。

【问题讨论】:

  • 确切的堆栈跟踪是什么以及它出现在您的代码中的什么位置?也发布saveMessages() 代码。
  • 您可能没有非常仔细地处理多线程。看this question。它的一些答案可能会有所帮助。
  • 其实我想知道你为什么不使用 AtomicInteger 而不是只为 counter++ 或 -- 锁定一个假对象。无论如何,我正在使用 Dmytro Danylyk 的解决方案。但是,我的问题与多线程无关,因为没有其他线程同时使用 db 来读取或写入。
  • 这不是导致异常的代码。代码中没有query(),但堆栈跟踪中有。
  • @Stan,好点;我改变了它。如果您正在使用该解决方案,则不应使用 getWritableDatabase()。您应该使用openDatabase()closeDatabase()。此外,您可以在 open 和 close 方法中添加一条日志消息,以确保您关闭的次数不会超过打开的次数。

标签: java android multithreading sqlite


【解决方案1】:

我发现您的实施存在多个问题。第一个 dbHelper 可以被另一个线程使用并且可能会造成死锁,所以你应该用锁来保护它。 proceedToNextActivity() 看起来在 UI 上做了一些工作,从后台线程做任何事情都是错误的,会导致崩溃。

如果 dbHelper 不需要任何 UI 元素,只需将消息副本传递给该线程并创建 dbHelper -> 做一些工作 -> 关闭助手 -> 结束线程。希望这能解决您的问题。

【讨论】:

  • proceedToNextActivity() 对 UI 没有任何作用,无论如何这不是问题的一部分。至于dbHelper,理论上可以被其他线程使用,但实际上并没有
【解决方案2】:

创建一个插入或更新方法。如果由于重复键而导致插入失败,则更新。

【讨论】:

  • -1,这个答案没有回答问题,也没有解决被抛出的IllegalStateException。如果有的话,这应该作为评论留下。
  • 我无法想象没有 insert_or_update 方法的数据库。您说它很可能发生在您没有处理重复密钥的更新中。更新失败,然后插入。
【解决方案3】:

由于我使用的是Dmytro Danylyk's way to work with DB (also mentioned on SO here),我犯了一个错误,认为出于阅读目的,我应该使用标准getReadableDatabase() 方法返回的可读数据库。相反,我应该始终使用openDatabase() 方法返回的可写方法(这是线程安全的)。

【讨论】:

    猜你喜欢
    • 2019-05-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-19
    • 2015-10-15
    • 2014-10-25
    相关资源
    最近更新 更多