I am trying to use WorkManager to populate the Room Database while creating the database. I am using dagger to initialize the database and its Dao's. While seeding the database, it gives the following error.
Could not instantiate *.*.*.SeedDatabaseWorker
java.lang.NoSuchMethodException: <init> [class android.content.Context, class androidx.work.WorkerParameters]
at java.lang.Class.getConstructor0(Class.java:2204)
at java.lang.Class.getDeclaredConstructor(Class.java:2050)
at androidx.work.WorkerFactory.createWorkerWithDefaultFallback(WorkerFactory.java:91)
at androidx.work.impl.WorkerWrapper.runWorker(WorkerWrapper.java:233)
at androidx.work.impl.WorkerWrapper.run(WorkerWrapper.java:127)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)
at java.lang.Thread.run(Thread.java:761)
2019-03-16 16:22:29.135 18035-18051/*.* E/WM-WorkerWrapper: Could not create Worker *.*.*.SeedDatabaseWorker
Here's how i have setup the application.
AppModule.Kt
@Provides
@Singleton
fun provideAppDatabase(context: Context): AppDatabase {
return Room.databaseBuilder(
context,
AppDatabase::class.java,
"garden.db"
)
.addCallback(object : RoomDatabase.Callback() {
override fun onCreate(db: SupportSQLiteDatabase) {
super.onCreate(db)
val request = OneTimeWorkRequestBuilder<SeedDatabaseWorker>().build()
WorkManager.getInstance().enqueue(request)
}
})
.build()
}
@Provides
@Singleton
fun providePlantDao(db: AppDatabase): PlantDao =
db.plantDao()
PlantDao
@Dao
interface PlantDao {
@Query("SELECT * FROM plants ORDER BY name")
fun getPlants(): LiveData<List<Plant>>
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertAll(plants: List<Plant>)
}
AppDatabase.kt
@Database(
entities = [
Plant::class
],
version = 1,
exportSchema = false
)
abstract class AppDatabase : RoomDatabase() {
abstract fun plantDao(): PlantDao
}
SeedDatabaseWorker.kt
class SeedDatabaseWorker @Inject constructor(
context: Context,
workerParameters: WorkerParameters
): CoroutineWorker(context, workerParameters) {
private val TAG by lazy { SeedDatabaseWorker::class.java.simpleName }
@Inject
lateinit var database: AppDatabase
override val coroutineContext = Dispatchers.IO
override suspend fun doWork(): Result = coroutineScope {
try {
applicationContext.assets.open("plants.json").use { inputStream ->
JsonReader(inputStream.reader()).use { jsonReader ->
val plantType = object : TypeToken<List<Plant>>() {}.type
val plantList: List<Plant> = Gson().fromJson(jsonReader, plantType)
database.plantDao().insertAll(plantList)
Result.success()
}
}
} catch (ex: Exception) {
Log.e(TAG, "Error seeding database", ex)
Result.failure()
}
}
}
Can anyone help me to populate database using WorkManager? Note: I am trying this google sample
For now, Dagger 2 does not officially support injection with androidx.worker.Worker
, this issue is still open, check here. One solution is proposed by this blog. Basically you could inject any object in the constructor like:
class HelloWorldWorker @AssistedInject constructor(
@Assisted private val appContext: Context,
@Assisted private val params: WorkerParameters,
private val foo: Foo
) : Worker(appContext, params) {
private val TAG = "HelloWorldWorker"
override fun doWork(): Result {
Log.d(TAG, "Hello world!")
Log.d(TAG, "Injected foo: $foo")
return Result.success()
}
@AssistedInject.Factory
interface Factory : ChildWorkerFactory
}
Here the object Foo
is injected.
I have same problem in my project, inspired by the blog, I solve the problem. You could check the my solution to see how to pre-populate data with WorkManager.