HelloCW
HelloCW

Reputation: 2255

How can I use dependency injection in CoroutineWorker with hilt?

I'm learning dependency injection, the following Code A and Code B are from the project https://github.com/android/sunflower

The author has defined a dependency injection PlantDao in Code A, but a PlantDao object is created with code database.plantDao() manually in Code B.

Why doesn't the author use dependency injection with the object PlantDao in Code B? How can I use dependency injection with the object PlantDao in Code B?

Code A

@InstallIn(SingletonComponent::class)
@Module
class DatabaseModule {

    @Singleton
    @Provides
    fun provideAppDatabase(@ApplicationContext context: Context): AppDatabase {
        return AppDatabase.getInstance(context)
    }

    @Provides
    fun providePlantDao(appDatabase: AppDatabase): PlantDao {
        return appDatabase.plantDao()
    }

   ...
}

Code B

class SeedDatabaseWorker(
        context: Context,
        workerParams: WorkerParameters
) : CoroutineWorker(context, workerParams) {
    override suspend fun doWork(): Result = withContext(Dispatchers.IO) {
        try {
            val filename = inputData.getString(KEY_FILENAME)
            if (filename != null) {
                applicationContext.assets.open(filename).use { inputStream ->
                    JsonReader(inputStream.reader()).use { jsonReader ->
                        ...
                        val database = AppDatabase.getInstance(applicationContext)
                        database.plantDao().insertAll(plantList)

                        ...
            } else {
               ...
            }
        } catch (ex: Exception) {
           ...
        }
    }

    ..
}

Added Content

To Andrew: Thanks!

In this question, you told me that @InstallIn(SingletonComponent::class) will be available to the entire application, you can see Image 1.

The author has defined a dependency injection object of PlantDao in Code A and install it as SingletonComponent::class.

So I think that the object of PlantDao will be available to the entire application,why can't I use the dependency injection object of PlantDao directly in Code B?

Image 1 enter image description here

Code D

class SeedDatabaseWorker @Inject  constructor(
    database: AppDatabase,
    context: Context,
    workerParams: WorkerParameters
) : CoroutineWorker(context, workerParams) {
    override suspend fun doWork(): Result = withContext(Dispatchers.IO) {
        try {
            val filename = inputData.getString(KEY_FILENAME)
            if (filename != null) {
                applicationContext.assets.open(filename).use { inputStream ->
                    JsonReader(inputStream.reader()).use { jsonReader ->
                        ...                  
                        database.plantDao().insertAll(plantList)
                        ...
                    } else {   
                ..
        }

Upvotes: 0

Views: 2072

Answers (1)

Andrew
Andrew

Reputation: 4712

You have to annotate your worker with @HiltWorker, your context and params with @Assisted, your constructor with @AssistedInject and then you can constructor inject your dao.

WorkerCode

@HiltWorker
class SeedDatabaseWorker @AssistedInject constructor(
        @Assisted context: Context,
        @Assisted workerParams: WorkerParameters,
        private val database: AppDatabase
) : CoroutineWorker(context, workerParams) {
    override suspend fun doWork(): Result = withContext(Dispatchers.IO) {
        try {
            val filename = inputData.getString(KEY_FILENAME)
            if (filename != null) {
                applicationContext.assets.open(filename).use { inputStream ->
                    JsonReader(inputStream.reader()).use { jsonReader ->
                        ...
                        database.plantDao().insertAll(plantList)

                        ...
            } else {
               ...
            }
        } catch (ex: Exception) {
           ...
        }
    }

    ..
}

Furthermore, you have to change the default WorkerFactory to a hiltWorkerFactory and remove the default initializer:

AppCode

@HiltAndroidApp
class ExampleApplication : Application(), Configuration.Provider {

  @Inject lateinit var workerFactory: HiltWorkerFactory

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

AndroidManifest.xml

<provider
    android:name="androidx.work.impl.WorkManagerInitializer"
    android:authorities="${applicationId}.workmanager-init"
    tools:node="remove" />

Needed Dependencies

implementation 'androidx.hilt:hilt-work:1.0.0'
kapt 'androidx.hilt:hilt-compiler:1.0.0'
implementation 'androidx.work:work-runtime-ktx:2.5.0'

Be aware that some processes change when later updating androidx.work to 2.6. You can read more here

Edit

You can use your dependency injection plantdao directly in code b. That's what private val database: AppDatabase inside your constructor means. In the first step, you provided your plantdao to hilt and told it how to create an instance of plantdao. In the next step (code b), you retain an instance of your plantdao via constructor injecting it. You have to first provide it to hilt (via a module) and then you can retain it (via constructor injection).

Upvotes: 6

Related Questions