Исправление double-checked locking инициализации
Едва ли не каждый первый учебный пример по Room который можно найти в интернете содержит какую либо вариацию этого кода:
@Database(entities = [Item::class], version = 1, exportSchema = false)
abstract class AppDatabase : RoomDatabase() {
abstract fun itemDao(): ItemDao
companion object {
@Volatile
private var INSTANCE: AppDatabase? = null
fun getDatabase(context: Context): AppDatabase {
return INSTANCE ?: synchronized(this) {
Room.databaseBuilder(
context,
AppDatabase::class.java,
"database.db"
).fallbackToDestructiveMigration().build().also {
INSTANCE = it
}
}
}
}
}
И у этого решения есть проблема - здесь для инициализации базы данных используется double-checked locking (все то что внутри companion object) который невозможно безопасно реализовать на Java/Kotlin даже с Volatile. Это решение просто не работает.
Как правильно потокобезопасно (и желательно эффективно) инициализировать базу данных в данном примере?
Ответы (1 шт):
Похоже что наиболее правильным решением в данном случае будет использование делегата by lazy который гарантирует потокобезопасность и однократность инициализации "из коробки" плюс он является ленивым. А саму ссылку на базу хранить в классе Application, который является синглтоном уровня приложения и доступен на протяжении всего жизненного цикла приложения и из любого компонента.
class BaseApplication : Application() {
val appDatabase:AppDatabase by lazy {
Room.databaseBuilder(
applicationContext,
AppDatabase::class.java,
"database.db"
).fallbackToDestructiveMigration().build()
}
}
Получение ссылки на базу:
val application = [email protected] //из активити
val application = [email protected]?.applicationContext //из фрагмента
val database = (application as? BaseApplication)?.appDatabase