Singleton设计模式以获得RoomDatabase实例



我正在使用RoomDatabase,在设置了我的DAO、数据库和实体类后,我需要找到一种方法来获得RoomDatabase实例,但在文档中,我看到了:

如果你的应用程序在一个进程中运行,你应该遵循singleton实例化AppDatabase对象时的设计模式。每个RoomDatabase实例相当昂贵,而且您很少需要访问到单个进程中的多个实例。

因此,从Udacity课程中,我看到有多种方法可以做到这一点——第一种:

private lateinit var INSTANCE: MainDBForObjects
fun getDatabase(context: Context): MainDBForObjects{
if (!::INSTANCE.isInitialized){
INSTANCE = Room.databaseBuilder(
context,
MainDBForObjects::class.java, "database"
).fallbackToDestructiveMigration()
.build()
}
return INSTANCE
}

另一个是来自数据库抽象类的伴随对象:

companion object {
@Volatile
private var INSTANCE: SleepDatabase? = null
fun getInstance(context: Context): SleepDatabase {
synchronized(this) {
var instance = INSTANCE
if (instance == null) {
instance = Room.databaseBuilder(
context.applicationContext,
SleepDatabase::class.java,
"sleep_history_database"
)

.fallbackToDestructiveMigration()
.build()

INSTANCE = instance
}
return instance
}
}
}

那么这两个之间有什么重要的区别吗?这一个比另一个好吗?

添加了一个通过双重检查锁定创建Singleton对象的实现。

这将确保只有在需要时(如果数据库对象未初始化(才能获取锁,因为同步通常是昂贵的

这里的双重检查指的是synchronized块中的再次空检查,这有助于我们在thread A第一次获取锁来初始化对象,而其他线程现在也在等待(thread B, C)的情况下,只要thread A初始化对象并释放锁,所有等待的线程都会立即获得更新的值。

使用局部变量的需要是确保部分初始化的对象对线程不可见,从而导致不一致的状态(与语言语义有关,此处有更多信息-->https://en.wikipedia.org/wiki/Double-checked_locking#Usage_in_Java)

companion object {
@Volatile
private var sInstance: SleepDatabase? = null
@JvmStatic
fun getInstance(context: Context): SleepDatabase {
val localInstance = sInstance
if (localInstance != null) {
return localInstance
}
return synchronized(this) {
var instance = sInstance
if (instance == null) {
instance = Room.databaseBuilder(
context.applicationContext,
SleepDatabase::class.java,
"sleep_history_database"
)
.fallbackToDestructiveMigration()
.build()
sInstance = instance
}
instance
}
}
}

最新更新