Reputation: 1920
I want to inject repositories and others in the workmanager. Since workmanager is and Android Component i cannot just inject stuff in the construcotr but i have to make a Factory class. So i found an example on Medium and i followed it. I have the dependencies
// Dagger 2
def dagger_version = '2.24'
implementation "com.google.dagger:dagger-android:$dagger_version"
implementation "com.google.dagger:dagger-android:$dagger_version"
implementation "com.google.dagger:dagger-android-support:$dagger_version"
annotationProcessor "com.google.dagger:dagger-android-processor:$dagger_version"
kapt "com.google.dagger:dagger-compiler:$dagger_version"
kapt "com.google.dagger:dagger-android-processor:$dagger_version"
// Assisted Injection
def dagger_assist_version = '0.3.3'
implementation "com.squareup.inject:assisted-inject-annotations-dagger2:$dagger_assist_version"
kapt "com.squareup.inject:assisted-inject-processor-dagger2:$dagger_assist_version"
Then i have the WorkManager Module where i bind my WorkerClass Factory
@Module
interface WorkManagerModule {
@Binds
@IntoMap
@WorkManagerKey(FileSplitter::class)
fun bindImageSplitterWorker(factory: FileSplitter.Factory): ChildWorkerFactory
}
WorkManagerComponent
@Component(modules = [AppAssistedInjectModule::class, WorkManagerModule::class])
interface WorkManagerComponent {
fun factory(): AppWorkerFactory
}
And the Factories
class AppWorkerFactory @Inject constructor(
private val workerFactories: Map<Class<out CoroutineWorker>,
@JvmSuppressWildcards Provider<ChildWorkerFactory>>
) : WorkerFactory() {
override fun createWorker(
appContext: Context,
workerClassName: String,
workerParameters: WorkerParameters
): CoroutineWorker? {
val foundEntry =
workerFactories.entries.find { Class.forName(workerClassName).isAssignableFrom(it.key) }
val factoryProvider = foundEntry?.value
?: throw IllegalArgumentException("unknown worker class name: $workerClassName")
return factoryProvider.get().create(appContext, workerParameters)
}
}
interface ChildWorkerFactory {
fun create(appContext: Context, params: WorkerParameters): CoroutineWorker
}
And the Application class where i want to build the DaggerComponent
val factory: AppWorkerFactory = DaggerWorkManagerComponent.create().factory()
WorkManager.initialize(this, Configuration.Builder().setWorkerFactory(factory).build())
So my WorkManager class is like
class FileSplitter @AssistedInject constructor(
@Assisted private val appContext: Context,
@Assisted private val params: WorkerParameters,
@Assisted private val messagesRepository: MessagesRepository
) : CoroutineWorker(appContext, params) {
override val coroutineContext = Dispatchers.IO
override suspend fun doWork(): Result = coroutineScope {
... CODE...
Result.success()
}
@AssistedInject.Factory
interface Factory : ChildWorkerFactory
companion object {
private const val TAG = "ImageSplitterWorker"
}
}
On build Project i get multiple errors. But the first two errors seems to be the real problem. I cannot understand what it maybe going wrong. Here they are.
...di/modules/AppAssistedInjectModule.java:7: error: cannot find symbol
@dagger.Module(includes = {AssistedInject_AppAssistedInjectModule.class})
^
symbol: class AssistedInject_AppAssistedInjectModule
..di//modules/AppAssistedInjectModule.java:8: error: [ComponentProcessor:MiscError] dagger.internal.codegen.ComponentProcessor was unable to process this class because not all of its dependencies could be resolved. Check for compilation errors or a circular dependency with generated code.
public abstract class AppAssistedInjectModule {
Upvotes: 0
Views: 1673
Reputation: 1920
The solution is the code below (each of the piece is a seperate file to my project)
Gradle imports
// Dagger 2
def dagger_version = '2.27'
implementation "com.google.dagger:dagger-android:$dagger_version"
implementation "com.google.dagger:dagger-android:$dagger_version"
implementation "com.google.dagger:dagger-android-support:$dagger_version"
kapt "com.google.dagger:dagger-compiler:$dagger_version"
kapt "com.google.dagger:dagger-android-processor:$dagger_version"
// Assisted Injection
def dagger_assist_version = '0.5.2'
compileOnly "com.squareup.inject:assisted-inject-annotations-dagger2:$dagger_assist_version"
kapt "com.squareup.inject:assisted-inject-processor-dagger2:$dagger_assist_version"
The Worker class
class FileSplitterWorker @AssistedInject constructor(
@Assisted private val appContext: Context,
@Assisted private val params: WorkerParameters,
private val messagesRepository: MessagesRepository
) : CoroutineWorker(appContext, params) {
override suspend fun doWork(): Result {
// Do Work with messageRepository
return Result.success()
}
@AssistedInject.Factory
interface Factory : ChildWorkerFactory
}
Create this interface
interface ChildWorkerFactory {
fun create(appContext: Context, params: WorkerParameters): CoroutineWorker
}
And a factory class for Workers
class AppWorkerFactory @Inject constructor(
private val workerFactories: Map<Class<out CoroutineWorker>, @JvmSuppressWildcards Provider<ChildWorkerFactory>>
) : WorkerFactory() {
override fun createWorker(
appContext: Context,
workerClassName: String,
workerParameters: WorkerParameters
): CoroutineWorker? {
val foundEntry =
workerFactories.entries.find { Class.forName(workerClassName).isAssignableFrom(it.key) }
val factoryProvider = foundEntry?.value
?: throw IllegalArgumentException("unknown worker class name: $workerClassName")
return factoryProvider.get().create(appContext, workerParameters)
}
}
The AppComponent for the Dagger should be like
@Singleton
@Component(
modules = [
AndroidInjectionModule::class,
AndroidSupportInjectionModule::class,
AppAssistedInjectModule::class,
WorkManagerModule::class,
etc etc...
]
)
interface AppComponent {
fun inject(application: MyApplication)
fun workerFactory(): AppWorkerFactory
@Component.Factory
interface Factory {
fun withContext(@BindsInstance application: MyApplication): AppComponent
}
}
@Module(includes = [AssistedInject_AppAssistedInjectModule::class])
@AssistedModule
interface AppAssistedInjectModule
The WorkerModule should be and Interface
@Module(includes = [RepositoryModule::class, etc etc])
interface WorkManagerModule {
@Binds
@IntoMap
@WorkManagerKey(FileSplitterWorker::class)
fun bindFileSplitterWorker(factory: FileSplitterWorker.Factory): ChildWorkerFactory
}
And the Application class
override fun onCreate() {
super.onCreate()
instance = this
val daggerAppComponent = DaggerAppComponent.factory().withContext(this)
daggerAppComponent.inject(this)
val factory: AppWorkerFactory = daggerAppComponent.workerFactory()
WorkManager.initialize(this, Configuration.Builder().setWorkerFactory(factory).build())
}
companion object {
private lateinit var instance: MyApplication
}
And in the end you should declare the workmanager to the manifest in the application tag
<provider
android:name="androidx.work.impl.WorkManagerInitializer"
android:authorities="${applicationId}.workmanager-init"
android:exported="false"
tools:node="remove" />
Upvotes: 0
Reputation: 1920
Well....seems like Dagger2 Injection to workmanager is not able to be done yet. There is an open issue in Github for that. Anyone that knows please inform!!
Upvotes: 0