How to inject Worker class? - android

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

How to inject Repository in ordinary classes using hilt

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

Workmanager doesn't start when I use HILT

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.

WorkManager set up with KOIN

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

Application dependency to ViewModel with HILT

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.

Dagger 2 with WorkManager without Dagger-Android

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

Categories

Resources