在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() } }
注意:
- 因为Room需要使用
abstract class
所以不能使用Object。 - 单例必须是线程安全的,因为多个线程可以同时访问它。
- 必须能够把
Context
作为一个参数。
我已经看过所有类似的StackOverflow问题,并没有满足我的要求
在Kotlin中有论点的单身人士 不是线程安全的
Kotlin – 在Android中转换Singleton DatabaseController的最佳方法 是线程安全的
Kotlin线程保存与参数 使用对象的 本地懒惰单例
我找到了一个解决方案,这是未来我和任何一个可能有同样问题的答案。
经过一番调查,我发现我有两个选择。
- 使用双重检查锁定
- 使用按需初始化持有人惯用语
所以我考虑实施其中的一个,但是这在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