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

Android 将数据插入SQLite时出现间歇性NPE,android,sql,database,sqlite,Android,Sql,Database,Sqlite,当我在Android上向SQLite表中插入值时,我得到了一个NullPointerException,我不明白为什么。我正在测试ContentValues和数据库实例是否为null 这是插入代码: public void insertOrIgnore(ContentValues values) { SQLiteDatabase db = this.dbHelper.getWritableDatabase(); try { //I added these n

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

这是插入代码:

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();
        }
    }
} 
在哪里

大多数情况下,此代码都会按照预期处理添加到数据库中的数据。然而,它有时也很少产生以下错误。堆栈跟踪来自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源自(v 4.03)的第290行:


因此,数据库实例似乎为空。当我在事务开始时测试为null时,它如何在事务期间变为null?

此NPE似乎来自自定义ROM,因为指向的方法与您在LogCat中收到的方法不同。对于这种情况,我所做的是:如果这些异常的发生率非常罕见,我会忽略它们,因为很难知道手机上运行的是什么自定义ROM,更难获得此自定义ROM的源代码以了解问题所在

使用自定义ROM的用户并不多,因此,如果您在不同的手机上使用不同的SDK对应用程序进行了广泛的测试,并且出现的异常率没有那么大,那么您可以忽略它们。否则,你可以在黑暗中拍摄,并推测这个定制ROM中会有什么导致NPE(我个人认为不值得这么做)。

如本文所述

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

我跟踪了一下stacktrace,大致情况如下:

  • AlbumsData.insertOrIgnore(AlbumsData.java:89)

    调用
    insertWithOnConflict
    ,它会生成生成的sql字符串(
    “插入或忽略到…”
    ),然后将其与
    ContentValues
    中的值一起包装到
    SQLiteStatement
  • SQLiteDatabase.insertWithOnConflict(SQLiteDatabase.java:1844)
    -现在执行生成的语句
  • SQLiteStatement.executeInsert(SQLiteStatement.java:112)
    -在实际插入之前,数据库需要获取一个锁
  • SQLiteStatement.acquireAndLock(SQLiteStatement.java:247)
    -这里发生了一些检查,数据库对象就我所能看到的而言,在这一点上不为null。代码决定它必须启动一个事务。据我所知,数据库对象本身在那个点上没有被锁定
  • SQLiteDatabase.beginTransactionNoneExclusive(SQLiteDatabase.java:605)
    -只是转发
  • SQLiteDatabase.beginTransaction(SQLiteDatabase.java:690)
    -经过一些检查(不确定这里是否必须存在数据库),它将尝试执行
    execSQL(“立即开始;”)
  • SQLiteDatabase.execSQL(SQLiteDatabase.java:1965)
    -转发即可
  • SQLiteDatabase.executeSql(SQLiteDatabase.java:2025)
    -从
    立即开始;
    构建另一个
    SQLiteStatement
    。现在应该执行此语句
  • SQLiteStatement.executeUpdateDelete(SQLiteStatement.java:96)
    -首先检查数据库锁,这似乎没有问题,数据库在此不应为空。然后执行该语句,最后再次解锁数据库
  • SQLiteStatement.releaseAndUnlock(SQLiteStatement.java:290)
    -清理一些内容,最后使用NPE失败,因为数据库为
    null
行号不匹配,因此该代码中可能存在供应商修改/添加

如您所见,代码在实际使用您提供的数据之前崩溃

BEGIN TRANSACTION IMMEDIATE; -- crash
INSERT INTO table (...) VALUES (...);
-- (end transaction)
在我看来,这是一个框架缺陷。在那里进行内部处理的数据库对象不应该是
null
,尤其是在堆栈中似乎不是null的时候

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

尤其是
executeUpdateDelete()
类似

try {
    acquireAndLock(WRITE);
    // actual statement execution
} finally {
    releaseAndUnlock();
}
如果数据库在该点关闭,
acquiredandlock
try
部分中的任何代码都可能失败,这可能会使数据库对象处于
null
位置,从而导致
release和unlock
再次失败。您应该获得相同的堆栈跟踪


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

您检查过表是否为空吗?我已经用表的值更新了这个问题。它是AlbumsData类中的一个静态字段,InsertOrgNore是一个方法。它的初始化如上所示。第89行是db.insertWithOnConflict(…)。我编辑了这个问题来澄清这一点。应该是同一个问题。对我来说,看起来像是一个框架错误。@zapl您的链接对帮助我理解这个问题非常有价值。我已删除了链接中建议的db.close语句,到目前为止,我
BEGIN TRANSACTION IMMEDIATE; -- crash
INSERT INTO table (...) VALUES (...);
-- (end transaction)
try {
    acquireAndLock(WRITE);
    // actual statement execution
} finally {
    releaseAndUnlock();
}