android - 将数据插入 SQLite 时出现间歇性 NPE

标签 android sql database sqlite

当我在 Android 上向我的 SQLite 表中插入值时,我收到 NullPointerException,我不明白为什么。我正在测试 ContentValues 和数据库实例是否为空。

这是插入代码:

public void insertOrIgnore(ContentValues values) {

    SQLiteDatabase db = this.dbHelper.getWritableDatabase();

    try {

        //I added these null value checks to stop NPE, but doesn't help.
        if (values != null && db != null) {
            db.insertWithOnConflict(TABLE, null, values, SQLiteDatabase.CONFLICT_IGNORE);
        }
    } catch (SQLiteException e) {

    } finally {
        if (db != null) {
            db.close();
        }
    }
} 

在哪里

public static final String TABLE = "albums";

大多数情况下,此代码会按预期处理添加到数据库中的数据。但是,它有时但很少 会产生以下错误。堆栈跟踪来自 ACRA,我无法确定此错误是在什么情况下发生的。我正在寻找关于为什么会发生这种情况以及条件是什么的指示。我对 SQLite 的了解是初级水平。

java.lang.NullPointerException
    at android.database.sqlite.SQLiteStatement.releaseAndUnlock(SQLiteStatement.java:290)
    at android.database.sqlite.SQLiteStatement.executeUpdateDelete(SQLiteStatement.java:96)
    at android.database.sqlite.SQLiteDatabase.executeSql(SQLiteDatabase.java:2025)
    at android.database.sqlite.SQLiteDatabase.execSQL(SQLiteDatabase.java:1965)
    at android.database.sqlite.SQLiteDatabase.beginTransaction(SQLiteDatabase.java:690)
    at android.database.sqlite.SQLiteDatabase.beginTransactionNonExclusive(SQLiteDatabase.java:605)
    at android.database.sqlite.SQLiteStatement.acquireAndLock(SQLiteStatement.java:247)
    at android.database.sqlite.SQLiteStatement.executeInsert(SQLiteStatement.java:112)
    at android.database.sqlite.SQLiteDatabase.insertWithOnConflict(SQLiteDatabase.java:1844)
    at com.mydomain.myapp.albums.AlbumsData.insertOrIgnore(AlbumsData.java:89)

第 89 行是上面显示的 db.insertWithOnConflict(...) 调用。

我不是在寻找具有完整代码的答案,而是在寻找问题所在的指针和解释,以便我可以开始自己修复它。

编辑: 堆栈跟踪显示 NPE 源自 SQLiteStatement 的第 290 行(4.03 版):

 setNativeHandle(mDatabase.mNativeHandle);

所以看起来数据库实例是空的。当我在事务开始时测试为空时,它如何在事务期间变为空?

最佳答案

如此处所述SQLiteDatabase close() function causing NullPointerException when multiple threads

出现错误的原因可能是您在某个时候关闭了数据库。可能在失败的任务尚未完成时同时发生。

我稍微跟踪了一下堆栈跟踪,大致情况如下:

  • AlbumsData.insertOrIgnore(AlbumsData.java:89)
    你打电话insertWithOnConflict ,它构建生成的 sql 字符串 ( "INSERT OR IGNORE INTO..." ),然后将其与来自 ContentValues 的值包装在一起进入 SQLiteStatement .
  • SQLiteDatabase.insertWithOnConflict(SQLiteDatabase.java:1844) - 立即执行结果语句
  • SQLiteStatement.executeInsert(SQLiteStatement.java:112) - 在实际插入发生之前,数据库需要获取锁。
  • SQLiteStatement.acquireAndLock(SQLiteStatement.java:247) - 这里进行了一些检查,据我所知,数据库对象此时不为空。代码决定它必须开始一个事务。据我所知,数据库对象本身当时并未锁定。
  • SQLiteDatabase.beginTransactionNonExclusive(SQLiteDatabase.java:605) - 只是转发
  • SQLiteDatabase.beginTransaction(SQLiteDatabase.java:690) - 经过一些检查(不确定数据库是否必须存在)后,它将尝试执行 execSQL("BEGIN IMMEDIATE;")
  • SQLiteDatabase.execSQL(SQLiteDatabase.java:1965) - 向前
  • SQLiteDatabase.executeSql(SQLiteDatabase.java:2025) - build 另一个 SQLiteStatement来自 "BEGIN IMMEDIATE; .这个应该立即执行
  • SQLiteStatement.executeUpdateDelete(SQLiteStatement.java:96) - 从检查数据库锁开始,这似乎没问题,这里的数据库不应该为空。然后执行该语句,最后再次解锁数据库。
  • SQLiteStatement.releaseAndUnlock(SQLiteStatement.java:290) - 清理了一些东西,但最终因 NPE 而失败,因为数据库是 null .

行号不匹配,因此该代码中可能有供应商修改/添加。

如您所见,代码在实际使用您提供的数据之前就崩溃了。正要去做

BEGIN TRANSACTION IMMEDIATE; -- crash
INSERT INTO table (...) VALUES (...);
-- (end transaction)

在我看来,这使它成为一个框架错误。在那里内部处理的数据库对象不应该是 null在线的某个地方,尤其是当它似乎在堆栈的更上方不为空时。

我还认为另一个隐藏的异常可能是造成这种情况的根本原因。有很多try { /* do stuff */ } finally { /* clean up */ }代码块和 finally即使 try 部分也会被执行部分抛出异常。现在 finally block 可能会导致另一个异常,结果据我所知,原始异常被 finally block 中的新异常所取代。

特别是executeUpdateDelete()就像

try {
    acquireAndLock(WRITE);
    // actual statement execution
} finally {
    releaseAndUnlock();
}

如果此时数据库关闭,acquireAndLocktry 中的任何代码部分可能会失败,这可能会将数据库对象留在 null这导致releaseAndUnlock再次失败。你应该得到相同的堆栈跟踪。

除此之外,不要像 catch (SQLiteException e) { /* empty */ } 这样的空 catch block 。 .如果可能的话用 ACRA 记录它们/你还没有这样做。

关于android - 将数据插入 SQLite 时出现间歇性 NPE,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13565575/

相关文章:

android - OnePlus One Custom ROM 无法启动的未知原因

mysql按周分组

mysql - 使用日期格式的 INSERT ... SELECT

android - 为什么在使用 InputStream 从 .txt 读取时得到问号而不是符号?

php - 构建用于在 Android 平板电脑上运行 PHP 和 MySQL 的 Android 应用程序

android - 有没有办法在没有 USB 连接的情况下在手机上调试 android 应用程序?

mysql - INSERT 如果不存在 Mysql

SQL:查找每组的最大记录

sql - 每个 ID 的 Microsoft Access 计数唯一值

php - MYSQL:导出/导入数据以防止表一致性的最佳方式