getReadableDatabase经常,但不总是返回null

时间:2014-09-18 16:23:47

标签: android nullpointerexception sqliteopenhelper

我在我的android应用程序的Crashlytics-Logs中看到了这段代码中的NullPointerException:

try {
    mSQLDBreader = this.getReadableDatabase();
} catch (SQLException e) {
    if (mSQLDBreader != null) {
        mSQLDBreader.close();
        mSQLDBreader = this.getReadableDatabase();
    }
}

mSQLDBreader... // NPE

由于以前的开发人员不再可用,我不知道为什么会尝试两次,但代码似乎有时会起作用,但往往不行。这个调用返回null的原因是什么?

似乎这只发生在2.3.x设备上,在我的崩溃日志中,所有受影响的设备都是2.3.5和2.3.6。

enter image description here

3 个答案:

答案 0 :(得分:1)

你有自己创作的线程吗?一些可能通过其他方法重置mSQLDBreader的代码。也许该线程在使用之前偶尔会运行并破坏mSQLDBreader的值?

你有没有在运行2.3.x的模拟器上见过这个?

我查看了google的getReadableDatabase代码,但我没有看到它可以返回null的方法。如果您有兴趣,请参阅下面的血腥细节。基于我所看到的,我怀疑你的代码中存在多线程错误,或者是你所测试设备的制造商对android代码的自定义引入的错误(如果这甚至是合理的话。)

Gory详细信息所有通过getReadableDatabase的路径在创建后都会在返回对象上调用方法。因此,该点的值不能为null。否则NPE将从内部升起。

以下是getReadableDatabase的2.3.6代码片段。实际来源位于grepcode

public synchronized SQLiteDatabase getReadableDatabase() {
    if (mDatabase != null && mDatabase.isOpen()) {
        return mDatabase;  // The database is already open for business
    }

    if (mIsInitializing) { /* snip throw ISE */ }

    try {
        return getWritableDatabase();
    } catch (SQLiteException e) {
        // snip : throws or falls through below
    }

    SQLiteDatabase db = null;
    try {
        mIsInitializing = true;
        String path = mContext.getDatabasePath(mName).getPath();
        db = SQLiteDatabase.openDatabase(path, mFactory, SQLiteDatabase.OPEN_READONLY);

        // *** next line calls method on db. NPE would be here if db was null at this point. ***
        if (db.getVersion() != mNewVersion) {
            // snip throw. 
        }

        onOpen(db);
        Log.w(TAG, "Opened " + mName + " in read-only mode");
        mDatabase = db;
        return mDatabase;
    } finally {
        // snip : not relevant
    }
}

请注意,getReadableDatabase通常只返回getWritableDatabase的结果。他看起来像这样:

public synchronized SQLiteDatabase getWritableDatabase() {
    if (mDatabase != null && mDatabase.isOpen() && !mDatabase.isReadOnly()) {
        return mDatabase;  // The database is already open for business
    }

    if (mIsInitializing) {
        throw new IllegalStateException("getWritableDatabase called recursively");
    }

    // snip comment about locking
    boolean success = false;
    SQLiteDatabase db = null;
    if (mDatabase != null) mDatabase.lock();
    try {
        mIsInitializing = true;
        if (mName == null) {
            db = SQLiteDatabase.create(null);
        } else {
            db = mContext.openOrCreateDatabase(mName, 0, mFactory);
        }

        int version = db.getVersion();  // ** method called on result!
        // snip block that has more method calls and never nulls out db
        onOpen(db);
        success = true;
        return db;
    } finally {
        // snip

        mDatabase = db;

        // snip rest of finally block that isn't relevant.
    }
}

最后,重要的是要注意这两个方法以及SqliteOpenHelper的close方法都使用synchronize进行标记,因此如果您有多个线程,则一个方法无法废弃另一个方法的状态同时调用这些方法..

答案 1 :(得分:0)

我过去遇到过类似的问题。似乎继续关闭并打开数据库为我创建了这个问题。如果您使用的是SQLLiteOpenHelper,除非您有充分的理由,否则不应该关闭数据库。请参阅CommonsWare回答here,其中说

  

SQLiteOpenHelper保留在您检索的数据库中   getReadableDatabase()/ getWritableDatabase(),重点是你   重用那个打开的SQLiteDatabase对象,特别是像你一样   跨多个线程工作。

答案 2 :(得分:0)

使用前打开dataBase

                        echo "<tr><td colspan='6'><small>There's no player on ban list</small></td></tr>";
                    }
                    while($data = $query->fetch())
                    {
                        echo "<tr><td>".$data['name']."</td>";
                        echo "<td>".$data['kill']."</td>";
                        echo "<td>".$data['death']."</td>";
                        $kd = $data['kill'] / $data['death'];
                        echo "<td>".$kd."</td></tr>";
                    }
                    ?>