通过 Dagger 2 提供 RoomDatabase 时实现 .addCallback() 的正确方法是啥?
Posted
技术标签:
【中文标题】通过 Dagger 2 提供 RoomDatabase 时实现 .addCallback() 的正确方法是啥?【英文标题】:What is the proper way to implement .addCallback() when providing RoomDatabase via Dagger 2?通过 Dagger 2 提供 RoomDatabase 时实现 .addCallback() 的正确方法是什么? 【发布时间】:2018-11-04 08:38:12 【问题描述】:我正在使用 Dagger 2 在我的应用中根据需要创建和共享我的 RoomDatabase。
我正在尝试实现addCallback()
,以便我可以覆盖数据库的onCreate()
函数并使用它来插入我的初始数据库值。这就是我遇到问题的地方。
我觉得我必须忽略一些显而易见的事情,但我想不出一种优雅的方法。
RoomDatabase 类:
@Database(
entities = [Station::class],
version = 1,
exportSchema = false
)
abstract class TrainDB : RoomDatabase()
abstract fun stationDao() : StationDao
DAO:
@Dao
abstract class StationDao
@Insert(onConflict = OnConflictStrategy.REPLACE)
abstract fun insert(stations: Station)
@Query("SELECT * FROM station_table")
abstract fun getAll() : LiveData<List<Station>>
匕首模块:
@Module
class DataModule
@Singleton
@Provides
fun provideDb(app: Application): TrainDB
var trainDB: TrainDB? = null
trainDB = Room
.databaseBuilder(app, TrainDB::class.java, "train.db")
.allowMainThreadQueries()
.fallbackToDestructiveMigration()
.addCallback(object : RoomDatabase.Callback()
override fun onCreate(db: SupportSQLiteDatabase)
super.onCreate(db)
/*
WHAT GOES HERE?
*/
)
.build()
return trainDB
@Singleton
@Provides
fun providesStationDao(db: TrainDB) : StationDao = db.stationDao()
我希望能够在 onCreate()
回调中访问我的 DAO。很明显这应该是可能的,因为 Google 正在将 Room 和 Dagger 放在一起,这可能是一个非常常见的用例。
我尝试将 DAO 作为 provideDB()
的构造函数参数,但这会产生循环依赖
我尝试将我的 RoomDatabase 初始化为伴随对象。然后,我可以调用可以访问 DAO 的 getInstance()
方法,而不是在我的 provideDB()
方法中使用 Room.builder
格式。但是这样我在递归调用getWriteableDatabase()
时遇到了错误。
我知道我可以使用 db.execSQL()
之类的东西,但是当我使用 Room 时这样做似乎太可惜了。
我错过了更好的方法吗?我正在使用 Kotlin,但欢迎使用 Java 示例。 :)
【问题讨论】:
这个答案看起来不错:Prepopulate Room Database with Dagger 2 in Kotlin 在我看来,使用协程是一个更好的解决方案。一个很好的例子raywenderlich.com/… 【参考方案1】:我是这样管理的:
@Module
class DataModule
lateinit var trainDB: TrainDB
@Singleton
@Provides
fun provideDb(app: Application): TrainDB
trainDB = Room
.databaseBuilder(app, TrainDB::class.java, "train.db")
.allowMainThreadQueries()
.fallbackToDestructiveMigration()
.addCallback(object : RoomDatabase.Callback()
override fun onCreate(db: SupportSQLiteDatabase)
super.onCreate(db)
/*
trainDB.stationDao().insert(...)
*/
)
.build()
return trainDB
@Singleton
@Provides
fun providesStationDao(db: TrainDB) : StationDao = db.stationDao()
但请记住,您需要从数据库中进行假读取以启动数据库并调用 onCreate()。当 db 尚未创建时,不要在第一次交互时写入 db,因为它会产生竞争条件,并且您的 on create 写入不会生效。
【讨论】:
+1 表示“您需要从数据库进行虚假读取以启动 db 并调用 onCreate()。当 db 没有时,不要在第一次交互时写入 db被创建是因为它会创建一个竞争条件,而你的 on create 写入不会生效。” onCreate() 在第一次安装应用程序时只会被调用一次。如果您希望再次调用它,您必须从您正在使用的模拟器或手机中卸载该应用程序并重新安装。无需假读。 @Jeffrey,是的,安装应用程序后,它不会到达这一行,除非您询问 DI 您需要此依赖项(假设是假读取)然后 DI 联系到这一行并尝试创建数据库的一个实例,因为没有其他实例,它在创建一次时进行,之后,您可以访问 Dao 进行读取和写入 在创建文档中说会在创建所有表后调用,但在我的情况下,它会在没有表时调用,而且数据库版本为 0?!【参考方案2】:您可以创建最终的单元素数组。
@AppScope
@Provides
public AppDatabase appDatabase(@ApplicationContext Context appContext, AppExecutors executors)
final AppDatabase[] databases = new AppDatabase[1];
databases[0] = Room.databaseBuilder(appContext, AppDatabase.class, AppDatabase.DATABASE_NAME)
.fallbackToDestructiveMigration()
.addCallback(new RoomDatabase.Callback()
@Override
public void onCreate(@NonNull SupportSQLiteDatabase db)
super.onCreate(db);
executors.diskIO().execute(() ->
databases[0].populateDatabaseOnCreate();
);
).build();
return databases[0];
【讨论】:
【参考方案3】:你可以在java中做到这一点
AppDatabase appDatabase = null;
AppDatabase finalAppDatabase = appDatabase;
appDatabase = Room.databaseBuilder(MyApplication.getApplication(),
AppDatabase.class, Constants.DATABASE_NAME).
addCallback(new RoomDatabase.Callback()
@Override
public void onCreate(@NonNull SupportSQLiteDatabase db)
super.onCreate(db);
//check for null
finalAppDatabase.yourDao();
).
build();
return appDatabase;
【讨论】:
我尝试了与此代码等效的 Kotlin。我还将我的文件转换为 Java,所以我可以直接使用它。在这两种情况下,我都会在 finalAppDatabase 上得到一个空引用。【参考方案4】:我们可以通过惰性注入来防止循环依赖。
例如
@Singleton
@Provides
fun provideDb(
app: Application,
trainDBLazy: Lazy<TrainDB> // import dagger.Lazy
): TrainDB
return Room
.databaseBuilder(app, TrainDB::class.java, "train.db")
.allowMainThreadQueries()
.fallbackToDestructiveMigration()
.addCallback(object : RoomDatabase.Callback()
override fun onCreate(db: SupportSQLiteDatabase)
super.onCreate(db)
var trainDB = trainDBLazy.get()
)
.build()
【讨论】:
以上是关于通过 Dagger 2 提供 RoomDatabase 时实现 .addCallback() 的正确方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章
Dagger 2 错误:依赖“不能在没有 @Inject 构造函数的情况下提供”,而它实际上是用 @Inject 注释的
kotlin + Dagger2 :没有@Provides-annotated 方法就无法提供
如何使用 Dagger 2 向 MyFirebaseMessagingService 提供数据库,以便我可以在 android 本地存储 fcm 消息