当我在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();
}
}
}
,其中
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(v 4.03)的第290行:
setNativeHandle(mDatabase.mNativeHandle);
所以看起来数据库实例是null。当我在事务开始时测试null时,它如何在事务期间变为空?
答案 0 :(得分:2)
如此处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)
- 从SQLiteStatement
中构建另一个"BEGIN IMMEDIATE;
。这个应该现在执行SQLiteStatement.executeUpdateDelete(SQLiteStatement.java:96)
- 从检查数据库锁开始,这似乎没问题,数据库不应该为null。然后执行该语句,最后再次解锁数据库。SQLiteStatement.releaseAndUnlock(SQLiteStatement.java:290)
- 清理一些内容,最后由于数据库为null
而无法使用NPE。行号不匹配,因此该代码中可能有供应商修改/添加。
如您所见,代码在实际使用您提供的数据之前崩溃。它即将开始
BEGIN TRANSACTION IMMEDIATE; -- crash
INSERT INTO table (...) VALUES (...);
-- (end transaction)
这在我看来是一个框架错误。在那里内部处理的数据库对象不应该在某个地方null
,特别是当它似乎在堆栈中没有进一步为空时。
我还认为另一个隐藏的异常可能是导致此问题的根本原因。代码中有很多try { /* do stuff */ } finally { /* clean up */ }
块,即使finally
部分抛出异常,也会执行try
部分。现在finally
块可能导致另一个异常,结果是AFAIK,原始异常被finally块中的新异常替换。
特别是executeUpdateDelete()
就像
try {
acquireAndLock(WRITE);
// actual statement execution
} finally {
releaseAndUnlock();
}
如果此时数据库已关闭,acquireAndLock
或try
部分中的任何代码都可能失败,这可能使数据库对象保持null
,从而导致releaseAndUnlock
再次失败。你应该得到相同的堆栈跟踪。
除此之外,不要像catch (SQLiteException e) { /* empty */ }
那样做空的挡块。如果可能的话,用ACRA记录它们/你不会那样做。
答案 1 :(得分:1)
此NPE似乎来自自定义ROM,因为Android source code指向的方法与您在LogCat中收到的方法不同。我为这种情况做的是:如果这些异常的比率非常罕见,我会忽略它们,因为很难知道手机上运行的是什么自定义ROM,并且更难以知道这个自定义ROM的源代码问题所在。
没有多少用户使用自定义ROM,因此如果您在具有不同SDK的不同手机上广泛测试您的应用程序并且您获得的例外率并不那么重要,您可以忽略它们。否则,您可以在黑暗中进行拍摄,并推测可能导致NPE的自定义ROM中的内容(个人而言,我认为这不值得付出努力)。