微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

android – 将数据插入SQLite时间歇性NPE

当我在 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的第290行(v 4.03):

setNativeHandle(mDatabase.mNativeHandle);

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

解决方法

正如这里提到的 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) – 这里发生了一些检查,数据库对象就此而言在那时我看不到null.代码决定它必须启动一个事务.数据库对象本身就是我看不到的那一点.
> sqliteDatabase.beginTransactionNonExclusive(sqliteDatabase.java:605) – 只是转发
> sqliteDatabase.beginTransaction(sqliteDatabase.java:690) – 经过一些检查(不确定数据库是否必须存在),它将尝试执行execsql(“BEGIN IMMEDIATE;”)
> sqliteDatabase.execsql(sqliteDatabase.java:1965) – 只需转发
> sqliteDatabase.executesql(sqliteDatabase.java:2025) – 从“BEGIN IMMEDIATE”开始构建另一个sqliteStatement;这个应该立即执行
> sqliteStatement.executeUpdateDelete(sqliteStatement.java:96) – 从检查数据库锁开始,这似乎没问题,数据库不应该为null.然后执行该语句,最后再次解锁数据库.
> sqliteStatement.releaseAndUnlock(sqliteStatement.java:290) – 清理一些东西,最后因NPE而失败,因为数据库为空.

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

如您所见,代码在实际使用您提供的数据之前崩溃.它即将要做

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

这在我看来是一个框架错误.在那里内部处理的数据库对象不应该在某个地方是空的,特别是当它似乎在堆栈中没有进一步为空时.

我也认为另一个隐藏的异常可能是导致此问题的根本原因.代码中有很多try {/ * do stuff * /}最后{/ *清理* /}块,即使try部分抛出异常,也会执行finally部分.现在finally块可能导致另一个异常,结果是AFAIK,原始异常被finally块中的新异常替换.

特别是executeUpdateDelete()就像

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

如果数据库在此时关闭,则acquireAndLock或try部分中的任何代码都可能失败,这可能使数据库对象保持为null,从而导致releaseAndUnlock再次失败.你应该得到相同的堆栈跟踪.

除此之外,不要像catch(sqliteException e){/ * empty * /}那样执行空的catch块.如果可能,请使用ACRA记录它们/您不要这样做.

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐