当我在 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();
}
如果此时数据库关闭,acquireAndLock
或 try
中的任何代码部分可能会失败,这可能会将数据库对象留在 null
这导致releaseAndUnlock
再次失败。你应该得到相同的堆栈跟踪。
除此之外,不要像 catch (SQLiteException e) { /* empty */ }
这样的空 catch block 。 .如果可能的话用 ACRA 记录它们/你还没有这样做。
关于android - 将数据插入 SQLite 时出现间歇性 NPE,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13565575/