【问题标题】:When should I close an SQLiteDatabase object?我应该何时关闭 SQLiteDatabase 对象?
【发布时间】:2017-06-16 07:51:54
【问题描述】:

我一直收到这个烦人的运行时错误几个小时,这导致我的应用程序崩溃:

java.lang.RuntimeException: 执行时出错 doInBackground().

引起:java.lang.IllegalStateException:尝试 重新打开一个已经关闭的对象:SQLiteDatabase

经过一些调试后,我发现这是因为我在 onDestory() 方法中关闭了我的 SQLiteDatabse 对象。当我打电话给SQLiteOpenHelper.close() 时也会发生这种情况。

@Override
protected void onDestroy() {
    super.onDestroy();
    _cursor.close(); //is fine
    _db.close(); //causes error
    _databaseHelper.close(); //causes error too (probably calls db.close() internally..?)
    SharedFunctions.d("closed!"); //ignore this ugly thing
}

这就引出了两个问题

  1. 我做得对吗? (可能不会)
  2. 如果onDestroy 方法中没有,我是否需要关闭 SQLiteDatabase 对象?

编辑: DB 和 Helper 类是静态的:

public class MainActivity extends Activity {

    private Cursor _cursor = null;
    private MyCursorAdapter _myCursorAdapter = null;
    private ListView _listView = null;

    private static SalaryDatabaseHelper _databaseHelper = null;
    public static SQLiteDatabase db = null;

    ...

我在 onCreate() 方法中初始化 _databaseHelper:

//get database helper
        if(_databaseHelper == null)
            _databaseHelper = SalaryDatabaseHelper.getInstance(this);

dbAsyncTask.doInBackground() 中初始化:

protected Boolean doInBackground(Integer... data)
{
    try {
        //get writable database
        if(db == null)
            db = SalaryDatabaseHelper.getDbInstance();

我对助手类和数据库类使用单例:(都通过助手类访问)

class MyDatabaseHelper extends SQLiteOpenHelper{

    private static SalaryDatabaseHelper _instance = null;
    private static SQLiteDatabase _dbInstance = null;

    //singletons
    public static synchronized SalaryDatabaseHelper getInstance(Context context)
    {
        // Use the application context, which will ensure that you
        // don't accidentally leak an Activity's context.
        if (_instance == null)
            _instance = new SalaryDatabaseHelper(context.getApplicationContext());

        return _instance;
    }

    public static synchronized SQLiteDatabase getDbInstance() {
        if(_dbInstance == null)
            _dbInstance = _instance.getWritableDatabase();

        return _dbInstance;
    }

    ...

【问题讨论】:

  • 什么是_db_databaseHelper 是什么?这些是常规字段还是static 字段?这个异常是在哪里引发的?
  • 我添加了一些代码

标签: android


【解决方案1】:

您的SQLiteOpenHelper 实例static,因此在范围内是全局的。考虑到这一点:

我做得对吗? (可能不会)

没有。

如果不是在 onDestroy 方法中,我什么时候需要关闭 SQLiteDatabase 对象?

从来没有。 SQLite 是事务性的。没有关闭数据库失败的风险。

是的,这也让我很烦,但我已经为此经历了七个悲伤阶段,我正在“接受”

在更简单的场景中,只有一个组件可以访问数据库,您可以在该组件被销毁时关闭它。在您的情况下,您的整个应用程序,包括后台线程,都可以访问数据库。在这种情况下,您永远不会关闭它。

【讨论】:

  • 那么它什么时候关闭呢?只有当用户手动终止应用程序时?另外,将 SQLiteDatabase 和 SQLiteOpenHelper 声明为静态是错误的吗?
  • @Pilpel:“那么它什么时候关闭?” -- 就调用close() 方法而言,它没有。最终,您的进程终止,您的所有对象都消失了,包括您的SQLiteOpenHelperSQLiteDatabase。 “将 SQLiteDatabase 和 SQLiteOpenHelper 声明为静态是错误的吗?” -- 拥有static SQLiteOpenHelper,或者让其他一些单身人士拥有SQLiteOpenHelper,是很常见的。我不会将SQLiteDatabase 作为任何地方的字段;调用助手上的getWritableDatabase() 来获取它。
  • @Pilpel:另外,您的implementing double-checked locking 不适合您的static SQLiteOpenHelper。如果您将字段设为static volatile 而不仅仅是static,则应该没问题。
  • @CommonsWare:您写道:“我不会将 SQLiteDatabase 作为任何地方的字段”。这是否意味着在上面的例子中你永远不会有“db.query(...);”但总是“databaseHelper.getWritableDatabase().query(...);”?这不会有任何相关的性能损失吗?
  • @j3App:调用getWritableDatabase() 的代码将只是生成的数据库 I/O 成本的一小部分。
【解决方案2】:

真的,您不必关闭数据库连接。 您可以将数据库保存为 Application 对象中的字段。

官方文档没有说明数据库关闭的时间。另外this question 引用了谷歌工程师的旧(现已删除)帖子 也就是说,这种方法是可以的。

在实际应用中,它可以运行多年。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-08-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多