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

hum*_*zed 8 singleton android kotlin android-room

我正在尝试使用Room作为单身人士,所以我不必再调用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()
    }
}
Run Code Online (Sandbox Code Playgroud)

注意:

  1. 无法使用Object,因为Room需要使用abstract class.
  2. 单例必须是线程安全的,因为多个线程可能同时访问它.
  3. 必须能够Context作为一个论点.

我查看了所有类似的StackOverflow问题,但没有一个满足我的要求

在Kotlin中使用参数的Singleton 不是线程安全的

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

Kotlin线程使用参数 使用对象保存本机惰性单例

hum*_*zed 6

我找到了解决方案,所以这是将来我和任何可能遇到相同问题的人的答案。


经过研究,我发现我有两个选择。

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

所以我考虑实现其中之一,但这在kotlin中感觉不对,因为样板代码太多了:p


因此,在进行了更多研究之后,我偶然发现了这篇出色的文章,该文章提供了一种出色的解决方案,该解决方案使用有效的方式使用了双重检查锁定。

我的代码变成这样:

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

从文章:

可重用的Kotlin实现:

我们可以封装逻辑以懒散地创建和初始化带有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
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

额外: 如果您要让Singleton有两个参数

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
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 请小心地将应用程序上下文传递给 Room,就像我文章中的示例一样。 (2认同)

Jan*_*ski 5

在这种特殊情况下我会诉诸使用匕首2,或其他一些依赖注入库像孝允或牙签。所有这三个库都允许以单例形式提供依赖关系。

这是Dagger 2模块的代码:

@Module
class AppModule constructor(private val context: Context) {
    @Provides
    @Singleton
    fun providesDatabase(): AppDatabase {
        return Room.databaseBuilder(
                context,
                AppDatabase::class.java,
                "train.db")
                .build()
    }
}
Run Code Online (Sandbox Code Playgroud)

AppComponent:

@Singleton
@Component(modules = arrayOf(
        AppModule::class
))
interface AppComponent {
    fun inject(viewModel: YourViewModel)
    fun inject(repository: YourRepository)
}
Run Code Online (Sandbox Code Playgroud)

提供注入的应用程序类:

class App : Application() {
    companion object {
        private lateinit var appComponent: AppComponent
        val component: AppComponent get() = appComponent
    }

    override fun onCreate() {
        super.onCreate()
        initializeDagger()
    }

    private fun initializeDagger() {
        component = DaggerAppComponent.builder()
                .appModule(AppModule(this))
                .build()
    }
}
Run Code Online (Sandbox Code Playgroud)

然后将数据库作为单例注入到您需要的任何位置(例如,在应用程序的存储库中):

@Inject lateinit var appDatabase: AppDatabase

init {
    App.component.inject(this)
}
Run Code Online (Sandbox Code Playgroud)

  • 你为什么使用 m 前缀?它已经过时了。 (2认同)

Chi*_*ndo 5

使用@Volatile来保证线程安全。

public abstract class AppDatabase : RoomDatabase() {

   abstract fun trainDao(): trainDao

   companion object {
        @Volatile
        private var INSTANCE: AppDatabase? = null

        fun getDatabase(context: Context): Db = INSTANCE ?: synchronized(this){
            val instance = Room.databaseBuilder(
            context.applicationContext,
            AppDatabase ::class.java,
            "train-db"
          ).build()
          INSTANCE = instance
          instance
        }
   }
}
Run Code Online (Sandbox Code Playgroud)

摘自: https: //developer.android.com/codelabs/android-room-with-a-view-kotlin#7


归档时间:

查看次数:

4941 次

最近记录:

7 年,5 月 前