【问题标题】:Android SQLite DB When to CloseAndroid SQLite DB 何时关闭
【发布时间】:2011-06-01 05:24:53
【问题描述】:

我正在使用 android 上的 SQLite 数据库。我的数据库管理器是一个单例,现在在初始化时打开与数据库的连接。让数据库一直保持打开状态是安全的,这样当有人打电话给我的班级使用数据库时,它已经打开了吗?或者我应该在需要每次访问之前和之后打开和关闭数据库。一直开着有什么坏处吗?

谢谢!

【问题讨论】:

    标签: android sqlite


    【解决方案1】:

    我会一直保持打开状态,并以某种生命周期方法关闭它,例如onStoponDestroy。这样,您可以在每次使用之前通过在单个 SQLiteDatabase 对象上调用 isDbLockedByCurrentThreadisDbLockedByOtherThreads 轻松检查数据库是否已在使用中。这将防止对数据库进行多次操作并避免您的应用程序崩溃

    所以在你的单例中,你可能有这样的方法来获取你的单个 SQLiteOpenHelper 对象:

    private SQLiteDatabase db;
    private MyDBOpenHelper mySingletonHelperField;
    public MyDBOpenHelper getDbHelper() {
        db = mySingletonHelperField.getDatabase();//returns the already created database object in my MyDBOpenHelper class(which extends `SQLiteOpenHelper`)
        while(db.isDbLockedByCurrentThread() || db.isDbLockedByOtherThreads()) {
            //db is locked, keep looping
        }
        return mySingletonHelperField;
    }
    

    所以每当你想使用你的开放帮助对象时,调用这个 getter 方法(确保它是线程的)

    您的单例中的另一种方法可能是(在您尝试调用上面的 getter 之前每次都调用):

    public void setDbHelper(MyDBOpenHelper mySingletonHelperField) {
        if(null == this.mySingletonHelperField) {
            this.mySingletonHelperField = mySingletonHelperField;
            this.mySingletonHelperField.setDb(this.mySingletonHelperField.getWritableDatabase());//creates and sets the database object in the MyDBOpenHelper class
        }
    }
    

    您可能还想在单例中关闭数据库:

    public void finalize() throws Throwable {
        if(null != mySingletonHelperField)
            mySingletonHelperField.close();
        if(null != db)
            db.close();
        super.finalize();
    }
    

    如果您的应用程序的用户能够非常快速地创建许多数据库交互,您应该使用我上面演示过的东西。但是如果数据库交互很少,我就不用担心了,每次都创建和关闭数据库。

    【讨论】:

    • 我可以使用这种方法将数据库访问速度提高 2 倍(保持数据库打开),谢谢
    • 纺纱是一种非常糟糕的技术。请参阅下面的答案。
    • @mixel 好点。我相信我在 API 16 可用之前发布了这个答案,但我可能是错的
    • @binnyb 我认为最好更新您的答案,以免误导人们。
    • WARN isDbLockedByOtherThreads() 自 API 16 以来已弃用并返回 false。同时检查 isDbLockedByCurrentThread() 将提供无限循环,因为它会停止当前线程并且没有任何东西可以“解锁 DB”,因此此方法返回 true。
    【解决方案2】:

    到目前为止,无需检查数据库是否被另一个线程锁定。 当您在每个线程中使用单例 SQLiteOpenHelper 时,您是安全的。 来自isDbLockedByCurrentThread 文档:

    这个方法的名字来自一个活跃的时间 与数据库的连接意味着线程持有一个实际的 锁定数据库。如今,已经没有真正的“数据库 锁”,尽管如果线程无法获取数据库,它们可能会阻塞 连接以执行特定操作。

    isDbLockedByOtherThreads 自 API 级别 16 起已弃用。

    【讨论】:

    • 每个线程使用一个实例是没有意义的。 SQLiteOpenHelper 是线程安全的。这也是非常低效的内存。相反,应用程序应该为每个数据库保留一个 SQLiteOpenHelper 实例。为了获得更好的并发性,建议使用 WriteAheadLogging,它为最多 4 个连接提供连接池。
    • @ejboy 我是这个意思。 “在每个线程中使用单例(= 一个)SQLiteOpenHelper”,而不是“每个线程”。
    【解决方案3】:

    关于问题:

    我的数据库管理器是单例的,在初始化时会立即打开与数据库的连接。

    我们应该划分'opening DB','opening a connection'。 SQLiteOpenHelper.getWritableDatabase() 给出一个打开的数据库。 但是我们不必控制连接,因为它是在内部完成的。

    让数据库一直打开是安全的,这样当有人打电话给我的班级使用数据库时,它已经打开了吗?

    是的,是的。如果事务正确关闭,连接不会挂起。请注意,如果 GC 完成,您的数据库也会自动关闭。

    或者我应该在每次访问之前和之后打开和关闭数据库。

    关闭 SQLiteDatabase 实例除了关闭连接之外并没有什么大不了的,但如果此时有一些连接,这对开发人员来说很糟糕。此外,在 SQLiteDatabase.close() 之后,SQLiteOpenHelper.getWritableDatabase() 将返回一个新实例。

    一直开着有什么坏处吗?

    不,没有。另请注意,在不相关的时刻关闭数据库和线程,例如在 Activity.onStop() 中可能会关闭活动连接并使数据处于不一致状态。

    【讨论】:

    • 谢谢你,在阅读了你的话“在 SQLiteDatabase.close() 之后,SQLiteOpenHelper.getWritableDatabase() 将返回一个新实例”后,我意识到我终于找到了我应用程序的一个老问题的答案.对于迁移到 Android 9 后变得至关重要的问题(请参阅stackoverflow.com/a/54224922/297710
    【解决方案4】:

    Android 8.1 有一个SQLiteOpenHelper.setIdleConnectionTimeout(long) 方法:

    设置 SQLite 连接的最大毫秒数 在关闭并从池中移除之前允许处于空闲状态。

    https://developer.android.com/reference/android/database/sqlite/SQLiteOpenHelper.html#setIdleConnectionTimeout(long)

    【讨论】:

    • 这些天它已被弃用。
    【解决方案5】:

    从性能的角度来看,最佳方法是在应用程序级别保留一个 SQLiteOpenHelper 实例。打开数据库可能很昂贵并且是一个阻塞操作,因此不应该在主线程和/或活动生命周期方法中完成。

    setIdleConnectionTimeout() 方法(在 Android 8.1 中引入)可用于在不使用数据库时释放 RAM。如果设置了空闲超时,数据库连接将在一段时间不活动后关闭,即当数据库未被访问时。执行新查询时,将对应用透明地重新打开连接。

    除此之外,应用程序可以在进入后台或检测到内存压力时调用releaseMemory(),例如在onTrimMemory()

    【讨论】:

      【解决方案6】:

      您也可以使用 ContentProvider。它会为你做这些事情。

      【讨论】:

        【解决方案7】:

        创建您自己的应用程序上下文,然后从那里打开和关闭数据库。 该对象还具有可用于关闭连接的 OnTerminate() 方法。 我还没有尝试过,但它似乎是一个更好的方法。

        @binnyb:我不喜欢使用 finalize() 来关闭连接。 可能可行,但据我了解,在 Java finalize() 方法中编写代码是个坏主意。

        【讨论】:

        • 你不想依赖 finalize 因为你永远不知道它什么时候会被调用。在 GC 决定清理它之前,一个对象可能会一直处于不确定状态,然后才会调用 finalize()。这就是它没用的原因。正确的做法是在对象不再使用时调用一个方法(不管是否被垃圾收集),而这正是 onTerminate() 的作用。
        • 文档说得很清楚,onTerminate 不会在生产环境中被调用 - developer.android.com/reference/android/app/…
        猜你喜欢
        • 2011-09-26
        • 2018-11-24
        • 2016-02-12
        • 1970-01-01
        • 1970-01-01
        • 2019-04-19
        • 2015-04-27
        • 2012-10-28
        • 1970-01-01
        相关资源
        最近更新 更多