Android SQLite DB 何时关闭

Posted

技术标签:

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

我正在使用 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 后变得至关重要的问题(请参阅***.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/…

以上是关于Android SQLite DB 何时关闭的主要内容,如果未能解决你的问题,请参考以下文章

何时关闭 SQLite 数据库(使用 FMDB)

将 Android 应用上的 SQLite DB 与远程 MySQL DB 同步

将本地 HTML5 DB(WebSQL 存储,SQLite)与服务器同步的最佳方法(2 路同步)[关闭]

Android 数据存储,何时使用 Sqlite,何时使用 JSON,Linq 替代方案

Android API 28 使用啥 SQLite 版本,历史上何时更新? [复制]

CoreData 何时真正写入 sqlite 文件?