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
Related
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
Hy
I recently migarted my projekt from dagger 2 to Hilt. Everything went well, but when I modified my WorkManager class, since my worker hasn't done anything.
In logcat I found this error message: WM-WorkerFactory: Could not instantiate hu.crm.crmapp.workmanager.SynchronizationWorker
java.lang.NoSuchMethodException: hu.crm.crmapp.workmanager.SynchronizationWorker. [class android.content.Context, class androidx.work.WorkerParameters]
First of all, I checked all of things, that I found in stackoverflow, so I deleted thw workmanager provider from manifest.
The Sync,and PrefManager dependies I also provided, but I don't copy that bunch of code here.
My Woker class:
#HiltWorker
class SynchronizationWorker #AssistedInject constructor(
private val sync: Sync,
private val prefManager: PrefManager,
#Assisted private val context: Context,
#Assisted workerParams: WorkerParameters
) : Worker(context, workerParams) {
private val countDownLatch = CountDownLatch(1)
override fun doWork(): Result {
val notificationHelper = NotificationHelper(context)
var workResult: Result = Result.success()
//doThings
}
My Application class:
#HiltAndroidApp
class CrmApp : Application(), Configuration.Provider {
#Inject
lateinit var workerFactory: HiltWorkerFactory
#Inject
lateinit var errorLogDao: ErrorLogDao
override fun attachBaseContext(base: Context?) {
super.attachBaseContext(base)
MultiDex.install(this)
}
override fun onCreate() {
super.onCreate()
BuildTypeInitializations.init(this)
}
override fun getWorkManagerConfiguration(): Configuration {
return Configuration.Builder()
.setWorkerFactory(workerFactory)
.build()
}
}
And there is the call of Worker class
val constraint =
Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED).build()
val synchronizationWorker =
OneTimeWorkRequest.Builder(SynchronizationWorker::class.java)
.setConstraints(constraint)
.setBackoffCriteria(
BackoffPolicy.LINEAR,
OneTimeWorkRequest.MIN_BACKOFF_MILLIS,
TimeUnit.MILLISECONDS
)
.build()
WorkManager.getInstance(requireContext()).enqueue(synchronizationWorker)
Thanks for the help.
I encountered the same problem and error when I wanted to inject constructor parameters in the Workmanager with the Dagger-Hilt. Follow these steps to inject constructor parameters in the Workmanager with Hilt:
Step 1: Remove the default initializer from the AndroidManifest.xml:
<application>
<provider
android:name="androidx.work.impl.WorkManagerInitializer"
android:authorities="${applicationId}.workmanager-init"
tools:node="remove" />
</application>
(As you've stated, you have already done this part)
Step 2: In your Application class insert this code:
#HiltAndroidApp
class ImageSearchApplication : Application(), Configuration.Provider{
#Inject lateinit var workerFactory: MyWorkerFactory
override fun getWorkManagerConfiguration() =
Configuration.Builder()
.setMinimumLoggingLevel(android.util.Log.DEBUG)
.setWorkerFactory(workerFactory)
.build()
}
Step 3: Now create this class called MyWorkerFactory like this:
class MyWorkerFactory #Inject constructor (private val repository: UnsplashRepository) : WorkerFactory() {
override fun createWorker(
appContext: Context,
workerClassName: String,
workerParameters: WorkerParameters
): ListenableWorker? {
// This only handles a single Worker, please don’t do this!!
// See below for a better way using DelegatingWorkerFactory
return MyWorker(appContext, workerParameters, repository)
}
}
That's it! Note that, pass and inject all the parameters you need in the Worker class. I needed only my repository so I defined and injected it.
I'm trying to set up work manager to do some work and I'm having trouble initializing it.
Im using KOIN workmaanger dsl
implementation "org.koin:koin-androidx-workmanager:2.2.0-rc-4"
and my worker class looks like this
class NotificationsScheduler(
private val dispatchers: AppCoroutineDispatchers,
private val getTaskUseCase: GetTaskUseCase,
private val context: Context,
private val workerParameters: WorkerParameters
) : Worker(context, workerParameters) {
override fun doWork(): Result {
...
}
What I've done so far is disabled default initializer
<provider
android:name="androidx.work.impl.WorkManagerInitializer"
android:authorities="${applicationId}.workmanager-init"
tools:node="remove" />
My worker module is set up like this
val workerModule = module {
worker { NotificationsScheduler(get(), get(), get(), get()) }
}
and it is added in list used in startKoin DSL. I've also used workManagerFactory() DSL to set up factory.
startKoin {
androidContext(this#MyApplication)
workManagerFactory()
modules(koinModules)
}
What I'm having trouble with, is that it crashes when app start with exception:
Caused by: org.koin.core.error.NoBeanDefFoundException: No definition found for class:'androidx.work.WorkerParameters'. Check your definitions!
Just take NotificationsScheduler class implements KoinComponent and inject the AppCoroutineDispatchers and GetTaskUseCase instances by inject() like this:
class NotificationsScheduler(context: Context, parameters: WorkerParameters) : CoroutineWorker(context, parameters), KoinComponent {
private val dispatchers: AppCoroutineDispatchers by inject()
private val getTaskUseCase: GetTaskUseCase by inject()
}
In worker module:
val workerModule = module {
worker { OneTimeWorkRequestBuilder<AlarmNotificationHandleWorker>().run{
WorkManager.getInstance(androidContext())
.enqueueUniqueWork(UUID.randomUUID().toString()
,ExistingWorkPolicy.APPEND, this)
}
}
}
Make sure you had provided the GetTaskUseCase and AppCoroutineDispatchers instances
Updated: Koin 2.2.0 release:
implementation "org.koin:koin-androidx-workmanager:2.2.0"
Update your Worker class
class NotificationsScheduler(private val dispatchers: AppCoroutineDispatchers,private val getTaskUseCase: GetTaskUseCase,context: Context, parameters: WorkerParameters) : CoroutineWorker(context, parameters), KoinComponent {
}
And here you are:
val workerModule = module {
worker { NotificationsScheduler(get(),get(),androidContext(),get()) }
}
Thanks #p72b
I was wondering how can I pass application dependency to ViewModel using Hilt?
I was trying with AndroidViewModel, but I couldn't make it. Can someone help me? Some short sample could will mean a lot to me.
This is my ViewModel:
class MainViewModel #ViewModelInject constructor(
private val application: Application,
private val repository: Repository,
#Assisted private val savedStateHandle: SavedStateHandle
) : ViewModel() {
This is my hilt module
#Module
#InstallIn(ApplicationComponent::class)
object DatabaseModule {
#Singleton
#Provides
fun provideDatabase(
#ApplicationContext context: Context
) = Room.databaseBuilder(
context,
MyDatabase::class.java,
"my_database"
).build()
#Singleton
#Provides
fun provideDao(database: MyDatabase) = database.myDao()
#Singleton
#Provides
fun provideRepository(myDao: MyDao) = Repository(myDao)
#Singleton
#Provides
fun provideApplicationContext() = MyApplication()
}
Everything else is fine, and I got the error message:
Caused by: java.lang.RuntimeException: Cannot create an instance of
class com.example.example.viewmodel.MainViewModel
Caused by: java.lang.InstantiationException:
java.lang.Class<com.example.example.viewmodel.MainViewModel> has
no zero argument constructor
You can see full source https://github.com/Kotlin-Android-Open-Source/MVI-Coroutines-Flow/tree/dagger_hilt
Repository:
#Singleton
class UserRepositoryImpl #Inject constructor(
private val userApiService: UserApiService,
private val dispatchers: CoroutineDispatchers,
...
) : UserRepository { ... }
Usecases:
class AddUserUseCase #Inject constructor(private val userRepository: UserRepository) {
suspend operator fun invoke(user: User) = userRepository.add(user)
}
class RemoveUserUseCase #Inject constructor(private val userRepository: UserRepository) {
suspend operator fun invoke(user: User) = userRepository.remove(user)
}
class RefreshGetUsersUseCase #Inject constructor(private val userRepository: UserRepository) {
suspend operator fun invoke() = userRepository.refresh()
}
...
ViewModel:
class MainVM #ViewModelInject constructor(
private val getUsersUseCase: GetUsersUseCase,
private val refreshGetUsers: RefreshGetUsersUseCase,
private val removeUser: RemoveUserUseCase,
) : ViewModel() { ... }
Activity:
#AndroidEntryPoint
class MainActivity : AppCompatActivity(), View {
private val mainVM by viewModels<MainVM>()
...
}
Edited:
To inject application context:
First, remove this definition, because Hilt already provides application context:
#Singleton
#Provides
fun provideApplicationContext() = MyApplication()
Second, Use #ApplicationContext annotation on your context parameter.
class MainViewModel #ViewModelInject constructor(
#ApplicationContext private val context: Context,
private val repository: Repository,
#Assisted private val savedStateHandle: SavedStateHandle
) : ViewModel() {
Use #ApplicationContext Context context as a parameter in the constructor.
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