【发布时间】:2011-06-01 05:24:53
【问题描述】:
我正在使用 android 上的 SQLite 数据库。我的数据库管理器是一个单例,现在在初始化时打开与数据库的连接。让数据库一直保持打开状态是安全的,这样当有人打电话给我的班级使用数据库时,它已经打开了吗?或者我应该在需要每次访问之前和之后打开和关闭数据库。一直开着有什么坏处吗?
谢谢!
【问题讨论】:
我正在使用 android 上的 SQLite 数据库。我的数据库管理器是一个单例,现在在初始化时打开与数据库的连接。让数据库一直保持打开状态是安全的,这样当有人打电话给我的班级使用数据库时,它已经打开了吗?或者我应该在需要每次访问之前和之后打开和关闭数据库。一直开着有什么坏处吗?
谢谢!
【问题讨论】:
我会一直保持打开状态,并以某种生命周期方法关闭它,例如onStop 或onDestroy。这样,您可以在每次使用之前通过在单个 SQLiteDatabase 对象上调用 isDbLockedByCurrentThread 或 isDbLockedByOtherThreads 轻松检查数据库是否已在使用中。这将防止对数据库进行多次操作并避免您的应用程序崩溃
所以在你的单例中,你可能有这样的方法来获取你的单个 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();
}
如果您的应用程序的用户能够非常快速地创建许多数据库交互,您应该使用我上面演示过的东西。但是如果数据库交互很少,我就不用担心了,每次都创建和关闭数据库。
【讨论】:
到目前为止,无需检查数据库是否被另一个线程锁定。
当您在每个线程中使用单例 SQLiteOpenHelper 时,您是安全的。
来自isDbLockedByCurrentThread 文档:
这个方法的名字来自一个活跃的时间 与数据库的连接意味着线程持有一个实际的 锁定数据库。如今,已经没有真正的“数据库 锁”,尽管如果线程无法获取数据库,它们可能会阻塞 连接以执行特定操作。
isDbLockedByOtherThreads 自 API 级别 16 起已弃用。
【讨论】:
关于问题:
我的数据库管理器是单例的,在初始化时会立即打开与数据库的连接。
我们应该划分'opening DB','opening a connection'。 SQLiteOpenHelper.getWritableDatabase() 给出一个打开的数据库。 但是我们不必控制连接,因为它是在内部完成的。
让数据库一直打开是安全的,这样当有人打电话给我的班级使用数据库时,它已经打开了吗?
是的,是的。如果事务正确关闭,连接不会挂起。请注意,如果 GC 完成,您的数据库也会自动关闭。
或者我应该在每次访问之前和之后打开和关闭数据库。
关闭 SQLiteDatabase 实例除了关闭连接之外并没有什么大不了的,但如果此时有一些连接,这对开发人员来说很糟糕。此外,在 SQLiteDatabase.close() 之后,SQLiteOpenHelper.getWritableDatabase() 将返回一个新实例。
一直开着有什么坏处吗?
不,没有。另请注意,在不相关的时刻关闭数据库和线程,例如在 Activity.onStop() 中可能会关闭活动连接并使数据处于不一致状态。
【讨论】:
Android 8.1 有一个SQLiteOpenHelper.setIdleConnectionTimeout(long) 方法:
设置 SQLite 连接的最大毫秒数 在关闭并从池中移除之前允许处于空闲状态。
【讨论】:
从性能的角度来看,最佳方法是在应用程序级别保留一个 SQLiteOpenHelper 实例。打开数据库可能很昂贵并且是一个阻塞操作,因此不应该在主线程和/或活动生命周期方法中完成。
setIdleConnectionTimeout() 方法(在 Android 8.1 中引入)可用于在不使用数据库时释放 RAM。如果设置了空闲超时,数据库连接将在一段时间不活动后关闭,即当数据库未被访问时。执行新查询时,将对应用透明地重新打开连接。
除此之外,应用程序可以在进入后台或检测到内存压力时调用releaseMemory(),例如在onTrimMemory()
【讨论】:
您也可以使用 ContentProvider。它会为你做这些事情。
【讨论】:
创建您自己的应用程序上下文,然后从那里打开和关闭数据库。 该对象还具有可用于关闭连接的 OnTerminate() 方法。 我还没有尝试过,但它似乎是一个更好的方法。
@binnyb:我不喜欢使用 finalize() 来关闭连接。 可能可行,但据我了解,在 Java finalize() 方法中编写代码是个坏主意。
【讨论】:
onTerminate 不会在生产环境中被调用 - developer.android.com/reference/android/app/…