在kotlin使用房间作为单身人士

我试图使用房间作为单身人士,所以我不必调用Room.databaseBuilder() – 这是昂贵的不止一次。

 @Database(entities = arrayOf( Price::class, StationOrder::class, TicketPrice::class, Train::class, TrainCategory::class ), version = 2) @TypeConverters(Converters::class) abstract class AppDatabase : RoomDatabase() { abstract fun dao(): TrainDao companion object { fun createDatabase(context: Context): AppDatabase = Room.databaseBuilder(context, AppDatabase::class.java, "trains.db").build() } } 

注意:

  1. 因为Room需要使用abstract class所以不能使用Object。
  2. 单例必须是线程安全的,因为多个线程可以同时访问它。
  3. 必须能够把Context作为一个参数。

我已经看过所有类似的StackOverflow问题,并没有满足我的要求

在Kotlin中有论点的单身人士 不是线程安全的

Kotlin – 在Android中转换Singleton DatabaseController的最佳方法 是线程安全的

Kotlin线程保存与参数 使用对象的 本地懒惰单例

我找到了一个解决方案,这是未来我和任何一个可能有同样问题的答案。


经过一番调查,我发现我有两个选择。

  1. 使用双重检查锁定
  2. 使用按需初始化持有人惯用语

所以我考虑实施其中的一个,但是这在kotlin中不太正确的代码:p


所以经过更多的研究,我偶然发现了这个伟大的文章 ,它提供了一个优秀的解决方案,使用双重检查锁定,但在一个合适的方式。

我的代码变成这样:

 companion object : SingletonHolder<AppDatabase, Context>({ Room.databaseBuilder(it, AppDatabase::class.java, "train.db").build() }) 

从文章:

可重用的Kotlin实现:

我们可以封装逻辑来在SingletonHolder类中用参数来懒散地创建和初始化一个单SingletonHolder 。 为了使这个逻辑线程安全,我们需要实现一个同步算法,最有效的一个 – 也是最难得到的 – 是双重检查锁定算法。

 open class SingletonHolder<T, A>(creator: (A) -> T) { private var creator: ((A) -> T)? = creator @Volatile private var instance: T? = null fun getInstance(arg: A): T { val i = instance if (i != null) { return i } return synchronized(this) { val i2 = instance if (i2 != null) { i2 } else { val created = creator!!(arg) instance = created creator = null created } } } } 

额外:如果你想单身有两个参数

 open class SingletonHolder2<out T, in A, in B>(creator: (A, B) -> T) { private var creator: ((A, B) -> T)? = creator @Volatile private var instance: T? = null fun getInstance(arg0: A, arg1: B): T { val i = instance if (i != null) return i return synchronized(this) { val i2 = instance if (i2 != null) { i2 } else { val created = creator!!(arg0, arg1) instance = created creator = null created } } } } 

你可以使用Kotlin标准库

  ( LazyThreadSafetyMode.SYNCHRONIZED ,initializer:() - > T): Lazy <T> 
 companion object { private lateinit var context: Context private val database: AppDatabase by lazy(LazyThreadSafetyMode.SYNCHRONIZED) { Room.databaseBuilder(context, AppDatabase::class.java, "trains.db").build() } fun getDatabase(context: Context): AppDatabase { this.context = context.applicationContext return database } } 

就个人而言,我通常会在应用程序中添加ApplicationContext相关的单例,例如

 <!-- AndroidManifest.xml --> <manifest> <application android:name="MyApplication"> ... 
 class MyApplication : Application() { val database: AppDatabase by lazy { Room.databaseBuilder(this, AppDatabase::class.java, "train.db").build() } } 

你甚至可以定义一个扩展方法,以便作为context.database访问。

 val Context.database get() = generateSequence(applicationContext) { (it as? ContextWrapper)?.baseContext }.filterIsInstance<MyApplication>().first().database