2014-09-18 45 views
4

我在Android應用程序的Crashlytics,登錄驗證碼一個NullPointerException看到:getReadableDatabase經常,但並不總是返回null

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

mSQLDBreader... // NPE 

正如前面的開發商是不提供任何更多,我不知道知道爲什麼嘗試兩次,但代碼似乎有時工作,但往往不行。這個調用返回null是什麼原因?

看來,如果這隻發生在2.3.x設備上,在我的crashlog中,所有受影響的設備都是2.3.5和2.3.6。

enter image description here

回答

0

我已跑進過類似的問題。好像繼續關閉並打開數據庫爲我創建了這個問題。如果您使用的是SQLLiteOpenHelper,則除非您有充分的理由才能關閉數據庫。見CommonsWare答案here它說

SQLiteOpenHelper持有到你 getReadableDatabase()檢索數據庫/ getWritableDatabase(),點是你 重用打開SQLiteDatabase對象,尤其是您在做 工作多線程。

1

你有沒有對自己的創作有任何線索?一些代碼可能會通過另一種方法重置mSQLDBreader。也許該線程偶爾運行並在使用之前破壞mSQLDBreader的值?

你有沒有在運行2.3.x的模擬器上目睹過這一點?

我看着谷歌的代碼getReadableDatabase,我沒有看到它可以返回null的方式。血腥細節如下,如果你有興趣。根據我所看到的,我會懷疑您的代碼中存在多線程錯誤,或者由您測試的設備的製造商對android代碼進行自定義所引入的錯誤(如果這甚至是合理的)。

Gory details通過getReadableDatabase的所有路徑在創建它之後調用返回對象上的方法。所以這個值不能爲零。否則,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的接近方法,標記有同步是很重要的,所以沒有辦法對一個方法垃圾桶如果你有多個線程調用這些方法在同一時間其他的狀態..

0

使用前打開數據庫

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