MyRepository
class MyRepository #Inject constructor(
private val myDao: IMyDao
){
...
}
MyModule
#InstallIn(SingletonComponent::class)
#Module
class MyModule {
#Provides
fun provideMyRepository(MyDao: IMyDao): MyRepository{
return MyRepository(MyDao)
}
}
Use in Worker
class MyWorker(appContext: Context, workerParams: WorkerParameters) :
Worker(appContext, workerParams) {
private val myRepository: MyRepository =
EntryPointAccessors.fromApplication(appContext, MyRepository::class.java)
}
start worker in Application class.
but got error: java.lang.ClassCastException: Cannot cast com.freedom.android.DaggerMyApplication_HiltComponents_SingletonC$SingletonCImpl to com.freedom.android.MyRepository
I read the relevant documentation.But I don't want to use the interface, I just want the class to be provided.
Please tell me what is the problem.
In this case you need to use hilt worker
#HiltWorker class WokerName #AssistedInject constructor(
#Assisted appContext: Context,
#Assisted params: WorkerParameters,
myRepository: MyRepository): CoroutineWorker(appContext, params)
Go through all the links below you will get all the gradel dependency, how to use them etc..
https://developer.android.com/training/dependency-injection/hilt-jetpack#kotlin
https://developer.android.com/reference/androidx/hilt/work/HiltWorker
https://developer.android.com/guide/background/persistent/configuration/custom-configuration
https://developer.android.com/topic/libraries/app-startup
Related
I have a repository which I am scoping to a ViewModel via Hilt.
I also inject this repository in a Worker which is singleton scoped. The worker is Singleton scoped as this is the typical case I have seen in WorkManager examples.
This leads to a Missing Binding hilt error when building the app. I believe this is because when I make my repository ViewModelScoped it is no longer available for my workers which are Singleton scoped.
My WorkManager is only needed when I need to do the work.
Is it correct to have Workmanager and its workers Singleton scoped? Do i need to change the scope of the workers in order to avoid the repository missing binding error?
Repository Injection
#Module
#InstallIn(ViewModelComponent::class)
abstract class BindFirestore {
#Binds
#ViewModelScoped
abstract fun bindFirestoreRepository(firestoreRepository: FirestoreRepository): IFirestoreRepository
}
#Singleton
class FirestoreRepository #Inject constructor(
private val firestoreDatasource: IFirestoreDatasource,
) : IFirestoreRepository { ... }
WorkManager Injection
#Module
#InstallIn(SingletonComponent::class)
object WorkManagerModule {
#Singleton
#Provides
fun provideWorkManagerConfiguration(
addWorkerFactories: AddWorkerFactories
): Configuration {
return Configuration.Builder()
.setMinimumLoggingLevel(android.util.Log.DEBUG)
.setWorkerFactory(addWorkerFactories)
.build()
}
}
#Singleton
class AddWorkerFactories #Inject constructor(
firestore: IFirestoreRepository,
) : DelegatingWorkerFactory() {
init {
addFactory(
MyFactoryWorker(
firestore,
))
}
}
class MyFactoryWorker(
val firestore: IFirestoreRepository,
): WorkerFactory() {
override fun createWorker(
appContext: Context,
workerClassName: String,
workerParameters: WorkerParameters,
): ListenableWorker? {
return when (workerClassName) {
WorkerGetData::class.java.name -> WorkerGetData(
appContext,
workerParameters,
firestore,
)
else -> null
}
}
}
#HiltWorker
class WorkerGetData #AssistedInject constructor(
#Assisted val context: Context,
#Assisted workerParameters: WorkerParameters,
val firestore: IFirestoreRepository,
): CoroutineWorker(context, workerParameters) { ... }
Before, I use Code A to pass Context to ViewModel.
Now I hope to use Hilt as dependency injection to pass Context,
I have read the article , and Code B is from the article.
1: Is the Code B correct way to pass Context into ViewModel?
2: In my mind, in order to use Hilt in Android Studio project, I have added such as the Code C in project, do I need to use fun provideApplicationContext() = MyApplication() in Code B?
Code A
class HomeViewModel(private val mApplication: Application, val mRepository: DBRepository) : AndroidViewModel(mApplication) {
...
}
Code B
class MainViewModel #ViewModelInject constructor(
#ApplicationContext private val context: Context,
private val repository: Repository,
#Assisted private val savedStateHandle: SavedStateHandle
) : ViewModel() {
...
}
#Singleton
#Provides
fun provideApplicationContext() = MyApplication()
Code C
#HiltAndroidApp
class MyApplication : Application() {
}
This is how I injected applicationContext in the viewmodel and it worked fine.
Base Application
#HiltAndroidApp
class BaseApplication: Application()
App Module
#Module
#InstallIn(SingletonComponent::class)
object AppModule {
#Singleton
#Provides
fun provideApplication(#ApplicationContext app: Context): BaseApplication{
return app as BaseApplication
}
View Model
#HiltViewModel
class PendingListViewModel
#Inject
constructor(private val application: BaseApplication)
Usage In the ViewModel
AppCompatResources.getDrawable(application.applicationContext, R.drawable.marker_circle)
I am using Hilt. In my ViewModel, I have EventLogger class and a repository class, which are throwing me an error while being built, so build is unsuccessful. The code is as follows,
#HiltViewModel
class ConversionViewModel #Inject constructor(
private val preConversionUseCase : PreConversionUseCase,
private val conversionUseCase: ConversionUseCase,
private val conversionSmsUseCase: ConversionSmsUseCase,
private val currencyRepository: MyBankRepository,
private val logger: EventLogger
) : ViewModel() { ... }
class EvenLogger #Inject constructor(
#ActivityContext val context: Context
){ ... }
class MyBankRepository #Inject constructor(
#ActivityContext private val module: ApiModule
): ApiRequest() {
val api = module.provideRetrofit()
}
I am getting the following error:
I think you can't inject the ActivityContext into an ApiModule. You need to add an Context-type param to the constructor of your MyBankRepository, I suppose:
class MyBankRepository #Inject constructor (
#ActivityContext
val context: Context,
private val module: ApiModule
) : ApiRequest() { ...
But this does unfortunately not work for ViewModels as described here:
https://github.com/google/dagger/issues/2698#issuecomment-862516150
The Repository and the EventLogger are both children of a ViewModel.
So it looks like you have to chance your architecture a little bit. You shouldn't implement too much logic into an ViewModel.
So first of all I created my worker class called UpdaterWorker.
After that I overrided getWorkManagerConfiguration inside my application class.
And I created a WorkerFactory class.
Is that all I have to do, or did I miss something?
How can I create an one time request?
When I try to inject updater worker to main activity, I get this error: Dagger does not support injecting #AssistedInject type, .workmanager.UpdaterWorker. Did you mean to inject its assisted factory type instead?
**Updater worker**
#HiltWorker
class UpdaterWorker #AssistedInject constructor(
private val api: ReservationApi,
private val updaterUrl: String,
private val prefManager: PrefManager,
#Assisted private val context: Context,
#Assisted workerParams: WorkerParameters
) : Worker(context, workerParams) {
...
}
** Worker Factory**
class WorkerFactory #Inject constructor(
private val api: ReservationApi,
#param:Named("update_url") private val updaterUrl: String,
private val prefManager: PrefManager,
) : WorkerFactory() {
override fun createWorker(
appContext: Context,
workerClassName: String,
workerParameters: WorkerParameters
): ListenableWorker? {
return when (workerClassName) {
WorkerEnums.UpdaterWorker.name -> {
UpdaterWorker(api, updaterUrl, prefManager, appContext, workerParameters)
}
else ->
throw Resources.NotFoundException("Worker not found")
}
}
}
** My application class**
override fun getWorkManagerConfiguration(): Configuration {
return Configuration.Builder()
.setWorkerFactory(workerFactory)
.build()
}
**Main Activity**
#Inject
lateinit var updaterWorker: UpdaterWorker
A WorkerFactory is not needed and furthermore, you don't need to (and can't) inject the Worker into any other class. Here is the right approach:
Worker
#HiltWorker
class UpdaterWorker #AssistedInject constructor(
private val api: ReservationApi,
private val updaterUrl: String,
private val prefManager: PrefManager,
#Assisted private val context: Context,
#Assisted workerParams: WorkerParameters
) : Worker(context, workerParams) {
...
}
Usage of Worker (e.g here in viewmodel)
class MyTestViewModelForWorker(#ApplicationContext private val context: Context) : ViewModel() {
private val myTestWork = OneTimeWorkRequestBuilder<UpdateWorker>
.build()
fun startManager() {
WorkManager.getInstance(context).enqueue(myTestWork)
}
}
Then, it is important to override the default workerfactory with the hiltfactory in you app class
Overriding default WorkerFactory
#HiltAndroidApp
class App : Application(), Configuration.Provider {
#Inject lateinit var workerFactory: HiltWorkerFactory
override fun getWorkManagerConfiguration(): Configuration =
Configuration.Builder().setWorkerFactory(workerFactory).build()
}
And finally, you need to remove the default workmanagerinitalizator inside your manifest:
AppManifest
<provider
android:name="androidx.work.impl.WorkManagerInitializer"
android:authorities="${applicationId}.workmanager-init"
tools:node="remove" />
Next time, please read the documentation of hilt, because everything I wrote here is perfectly explained there and second of all, the approach I am showing here will change with the release of androidx.work:work-runtime-ktx:2.6.0
I am trying to use Dagger2 standard library with WorkManager. But in the examples on the internet they are using with dagger-android which I do not want. So, can you please suggest some solutions for this, so that I can use Dagger2, NOT dagger-android with WorkManager.
Thank you for your answer in advance!
This example using Kotlin.
class YourWorker(ctx: Context, params: WorkerParameters) : Worker(ctx, params) {
#Inject
lateinit var yourModel: YourModel
init {
getServiceComponent().inject(this) // or you create your component here and inject worker to it
}
}
#ServiceScope
#Component(
dependencies = {ApplicationComponent.class}
)
public interface ServiceComponent {
void inject(YourWorker worker);
}
You can inject a worker using Hilt.
Use #WorkerInject in the constructor of your worker. You must also annotate the Context and WorkerParameters dependencies with #Assisted:
class ExampleWorker #WorkerInject constructor(
#Assisted appContext: Context,
#Assisted workerParams: WorkerParameters,
workerDependency: WorkerDependency
) : Worker(appContext, workerParams) { ... }
And setup WorkManager configuration in your Application class:
#HiltAndroidApp
class ExampleApplication : Application(), Configuration.Provider {
#Inject lateinit var workerFactory: HiltWorkerFactory
override fun getWorkManagerConfiguration() =
Configuration.Builder()
.setWorkerFactory(workerFactory)
.build()
}
Reference: https://developer.android.com/training/dependency-injection/hilt-jetpack#workmanager