2014-10-10 55 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

相关问题