android - getReadableDatabase 经常但不总是返回 null

标签 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

最佳答案

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

您是否曾在运行 2.3.x 的模拟器上看到过这种情况?

我查看了 getReadableDatabase 的 google 代码,但没有看到它可以返回 null 的方法。如果您有兴趣,下面是血淋淋的详细信息。根据我所看到的情况,我怀疑您的代码中存在多线程错误,或者您测试的设备制造商对 android 代码的自定义引入了错误(如果这似乎有道理的话。)

血淋淋的细节 所有通过 getReadableDatabase 的路径都会在创建返回对象后调用方法。所以此时该值不能为空。否则 NPE 会从内部升起。

这是 getReadableDatabase 的 2.3.6 代码 fragment 。实际来源可在 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 方法都带有同步标记,因此如果您有多个线程,则一个方法无法破坏另一个方法的状态同时调用这些方法..

关于android - getReadableDatabase 经常但不总是返回 null,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25917800/

相关文章:

java - 是否可以查明是否安装了 apk 以及是否安装了应用程序

java - 获取选定的回收 View 的文本(使用 FireBase)

java - 我的数组出现空指针异常?

android - 弱引用对象已在静态 Handler 中被垃圾回收

android - 在sqlite数据库中插入多条记录的有效方法

java - 代码 14 : Unable to open database

Android Geocoder 获取管理区域的简称

java - 尽管组件的 ID 存在,findViewById 返回 null

java - SqLiteDatabase 可写 Vs。只读

Android dumpsys Activity 输出格式