Prepopulate Room database with Worker Hilt

81 Views Asked by At

I'm trying to prepopulate a Room database using a Worker injected by Hilt in the RoomDatabase.Callback(), but when I open the app and the first call requires data, it happens that returns an error and after to retry the prepopulate data appear. It seems the worker insert in the DB is called after the first read of the DB.

This the hilt database module:

@Provides
@Singleton
fun provideRoomDb(
    @ApplicationContext context: Context, databaseCallback: RoomDatabase.Callback
): BeerAppDatabase = Room
    .databaseBuilder(context, BeerAppDatabase::class.java, beerAppDatabaseName)
    .fallbackToDestructiveMigration()
    .addCallback(databaseCallback)
    .build()

@Provides
@Singleton
fun provideBeerDao(beerAppDatabase: BeerAppDatabase): BeerDao = beerAppDatabase.beerDao()

@Provides
@Singleton
fun provideDatabaseCallback(
    workManager: WorkManager
) = object : RoomDatabase.Callback() {
    override fun onCreate(db: SupportSQLiteDatabase) {
        super.onCreate(db)
        workManager.enqueueUniqueWork(
            beerDatabaseWorkerName,
            ExistingWorkPolicy.REPLACE,
            OneTimeWorkRequestBuilder<DatabaseWorker>().build()
        )
    }
}

@Provides
@Singleton
fun provideWorkManager(@ApplicationContext context: Context) = WorkManager.getInstance(context)

The database:

@Database(
    entities = [BeerDatabaseModel::class],
    version = 1,
    exportSchema = false
)
internal abstract class BeerAppDatabase : RoomDatabase() {
    abstract fun beerDao(): BeerDao
}

The Dao:

@Dao
internal interface BeerDao {
    @Query("SELECT * FROM beers WHERE (:beerName IS NULL OR name LIKE :beerName) ORDER BY id ASC LIMIT :limitPerPage OFFSET :offset")
    suspend fun getBeers(beerName: String?, offset: Int, limitPerPage: Int): List<BeerDatabaseModel>

    @Query("SELECT * FROM beers WHERE id LIKE :beerId")
    suspend fun getBeerById(beerId: Int): BeerDatabaseModel

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun insertBeers(beers: List<BeerDatabaseModel>)
}

and the worker:

@HiltWorker
internal class DatabaseWorker @AssistedInject constructor(
    @Assisted private val context: Context,
    @Assisted workerParams: WorkerParameters,
    private val beerDao: Lazy<BeerDao>
) : CoroutineWorker(context, workerParams) {

    companion object {
        const val prePopulateDatabaseFile = "pre-populate-database.json"
    }

    @OptIn(ExperimentalSerializationApi::class)
    override suspend fun doWork(): Result = coroutineScope {
        withContext(Dispatchers.IO) {
            runCatching {
                context.assets.open(prePopulateDatabaseFile).use { inputStream ->
                    beerDao.get().insertBeers(Json.decodeFromStream(inputStream))
                }
                Result.success()
            }.getOrElse {
                Result.failure()
            }
        }
    }
}

Also I added in the manifest:

<provider
    android:name="androidx.startup.InitializationProvider"
    android:authorities="${applicationId}.androidx-startup"
    tools:node="remove">
</provider>

and in the App:

@Inject
lateinit var workerFactory: HiltWorkerFactory

override fun getWorkManagerConfiguration() =
    Configuration.Builder()
        .setWorkerFactory(workerFactory)
        .build()

Do you have any idea how to solve this issue? Thank you!

0

There are 0 best solutions below