2014-10-10 41 views
2

我有一個數據庫幫助程序類和三個數據源類,用於同一數據庫中的三個表。 通過AsyncTasks可以在很多地方訪問數據庫。我遇到了這個「嘗試重新打開一個已經關閉的對象......」的問題,我搜索了一下,發現dbhelper.getReadableDatabase()爲已經打開的連接返回相同的對象。我猜想這個問題一定是由於兩個線程同時執行操作,其中一個線程完成其任務並調用close()連接關閉,並且正在運行的線程拋出此異常。 因此,爲了避免close()我寫了以下兩種方法:嘗試重新打開已關閉的對象

public static synchronized void newOpenRequest() { 
    requestsOpen++; 
    Util.debuglog(TAG, "Open requests: " + requestsOpen); 
} 

public static synchronized boolean canClose() { 
    requestsOpen--; 
    Util.debuglog(TAG, "Open requests: " + requestsOpen); 
    if(requestsOpen == 0) 
     return true; 
    return false; 
} 

在所有這三個數據源類的,當我做它在以下方式:

private void openRead() { 
    database = dbhelper.getReadableDatabase(); 
    DBHelper.newOpenRequest(); 
    Log.i(TAG, "Database opened."); 
} 

private void openWrite() { 
    database = dbhelper.getWritableDatabase(); 
    DBHelper.newOpenRequest(); 
    Log.i(TAG, "Database opened."); 
} 

private void close() { 
    if (DBHelper.canClose()) { 
     dbhelper.close(); 
     Util.debuglog(TAG, "Database closed."); 
    } 
} 

我logcat的輸出如下:

screen grab of partial logcat output

因此,如黑色突出顯示矩形,共openRequests爲0,所以數據庫關閉,正常,但如紅色矩形突出顯示, 首先openRequests爲0,這樣的時間只有數據庫應該關閉,但(我的猜測)發生了什麼是canClose()返回爲一個線程爲true ,並且在致電dbhelper.close();之前調用另一個線程open()(因爲openRequests = 1在關閉之前在LogCat上),然後調用第一個線程的close()給另一個正在運行的線程造成麻煩。

所以尋找解決方案來避免這種併發訪問問題。 謝謝。

+0

是'requestsOpen'一個'volatile'場? – 2014-10-10 14:18:12

+0

@PedroOliveira不,但我猜想使用同步方法會在這裏產生volatile。 – 2014-10-10 16:43:27

+0

我不確定。但由於您可以同時調用close和open(因爲它們是相互獨立同步的),所以可以通過不同的任務同時更改該值。你爲什麼不保存一個以單例打開的數據庫實例並從異步任務中訪問它? – 2014-10-10 16:45:34

回答

1

我學會了永遠不要關閉android中的數據庫。所以也許你的修復是不關閉數據庫。沒有意義,請在應用程序的整個生命週期中保持打開狀態。 Android會在您的應用ID被破壞時釋放資源。

您不需要同步數據庫調用,因爲sqlite可以是線程安全的。

Is Sqlite Database instance thread safe

DBOpenHelper就好工作原理:使用DB幫手

public class DBOpenHelper extends SQLiteOpenHelper { 

    private static final int DATABASE_VERSION = 31; 

    private static DBOpenHelper mInstance; 

    private static final String DATABASE_NAME = "thedb.db"; 

    public static DBOpenHelper getInstance(Context context) { 

     if (mInstance == null) { 
      mInstance = new DBOpenHelper(context.getApplicationContext()); 
     } 
     return mInstance; 
    } 

    private DBOpenHelper(Context context) { 
     super(context, DATABASE_NAME, null, DATABASE_VERSION); 
    } 

} 

樣品 - 關閉遊標,但不是分貝

SQLiteDatabase db = DBOpenHelper.getInstance(context).getWritableDatabase(); 

    Cursor cursor = null; 
    try { 
     cursor = db.query... 

    } 

    finally { 
     cursor.close(); 

    } 
+0

是的,我讀它是線程安全的相同的連接池,但由於只使用關閉,我得到了問題。保持連接打開真的是個好主意嗎?因爲我的應用程序還使用服務來處理來自服務器的傳入推送消息,它再次訪問數據庫,所以它將像我的連接將永遠保持打開狀態。 – 2014-10-10 18:48:43

+0

我在生產中有很多android應用程序,我們不關閉數據庫。如果應用程序已卸載,操作系統將釋放數據庫文件上的鎖定。打開和關閉是不必要的開銷,並導致像你描述的問題。這篇文章:http://stackoverflow.com/questions/4557154/android-sqlite-db-when-to-close談論關於一些onDestroy生命週期方法。我在這裏學到的教訓是,開放連接並不是一件壞事,它只是對文件的鎖定。永遠保持開放imho。 – bsautner 2014-10-10 19:05:48

+0

謝謝。現在一切都很好,我沒有關閉數據庫。 – 2014-10-18 08:36:59

相關問題