Utilizando WorkManager com Hilt - Injeção de dependência na classe Worker

WorkManager

WorkManager é uma API para programação de tarefas assíncronas que nos permite programar tarefas para serem executadas mesmo quando o app é fechado.

Hilt

Hilt é uma biblioteca de injeção de dependências para android criada com base na biblioteca Dagger.

Nesse artigo eu vou demonstrar como utilizar injeção de dependências na classe Worker da API WorkManager com Hilt

Adicionando as dependências do WorkManager

build.gradle(Module)

dependencies {
implementation "androidx.work:work-runtime-ktx:2.5.0"
androidTestImplementation "androidx.work:work-testing:2.5.0"
testImplementation "androidx.work:work-testing:2.5.0"
}
Definindo um trabalho usando a classe Worker sem utilizar injeção de dependência.
class SomeWork(appContext: Context, workerParams: WorkerParameters):
       Worker(appContext, workerParams) {
   override fun doWork(): Result {

    val user: User = User("John Doe", 32)

    print(user.toString())

       return Result.success()
   }
}

No exemplo acima a classe SomeWork que estende Worker instancia um objeto do tipo User e imprime o resultado no terminal. Nos exemplos a seguir veremos como passar User via parâmetro com hilt

Criar uma WorkRequest e enviar para o sistema
//Criando request
val doSomeWorkRequest: WorkRequest =
PeriodicWorkRequestBuilder<SomeWork>(15,TimeUnit.Minutes)
.build()

//Enviando para o sistema
WorkManager.getInstance(context)
                .enqueueUniquePeriodicWork(
                    "AppNameSomeWork",
                    ExistingPeriodicWorkPolicy.KEEP,
                    doSomeWorkRequest
                )

No exemplo acima foi criada uma requisição que irá se repetir a cada 15 minutos que é o tempo mínimo permitido pelo sistema. A cada 15 minutos será executada a linha de código print(user.toString()) dentro no método doWork() na classe SomeWork

Adicionando Hilt no projeto

build.gradle(Project)

buildscript {
    //...
    dependencies {
        //...
        classpath 'com.google.dagger:hilt-android-gradle-plugin:2.37'
    }
}

build.gradle(Module)

//...
plugins {
    id 'kotlin-kapt'
    id 'dagger.hilt.android.plugin'
}

android {
    //...
//O Hilt usa recursos do Java 8, ativar o Java 8
compileOptions {
    sourceCompatibility JavaVersion.VERSION_1_8
    targetCompatibility JavaVersion.VERSION_1_8
  }

 defaultConfig {
    //...
annotationProcessorOptions {
//O importante aqui é substituir = por += caso seu 
//projeto não esteja assim
                arguments += [... ] 
            }
}
}

kapt {
    correctErrorTypes true
}

dependencies {
    //Dagger - Hilt
    implementation 'com.google.dagger:hilt-android:2.37'
    kapt 'com.google.dagger:hilt-compiler:2.37'
    kapt 'androidx.hilt:hilt-compiler:1.0.0'
    implementation 'androidx.hilt:hilt-work:1.0.0'
    implementation 'androidx.hilt:hilt-navigation-compose:1.0.0-alpha03'
}

Adicione as dependências e verifique as configurações.

Anotando a class do projeto que estende Application()

AppNameApplication.kt

@HiltAndroidApp
class AppNameApplication : Application() {
override fun onCreate() {
        super.onCreate()
//...
    }
}

Essas anotações preparam as classes para receber a injeção de dependência pelo Hilt.

O hilt também é compatível com as seguintes classes:
Activity, Fragment, View, Service e BroadcastReceiver.
.Injetar dependências via hilt numa classe que não é compatível demanda um trabalho a mais.. talvez um próximo artigo. Mas o nosso foco aqui é a classe Worker então vamos lá.

Criando um modulo do Hilt

AppModule.kt

@InstallIn(SingletonComponent::class)
@Module
object AppModule {

 @Singleton
 @Provides
fun provideUser(): User{
return User("John Doe", 32)
}

}

Agora podemos injetar a instancia de User. Reparem nas anotações elas são importantes para entender os detalhes consultem a documentação do Hilt
hilt-android

Preparando a classe SomeWork para receber a injeção de dependência.

@HiltWorker
class SomeWork @AssistedInject constructor(
@Assisted appContext: Context, 
@Assisted workerParams: WorkerParameters, 
val user: User):
       Worker(appContext, workerParams) {
   override fun doWork(): Result {
    print(user.toString())
       return Result.success()
   }
}

Agora já estamos usando a instancia de User que vem via injeção do Hilt na classe SomeWork mas ainda precisamos fazer mais algumas coisas para tudo funcionar. O sistema cria uma instancia de WorkManager com uma configuração padrão, precisamos criar uma nova configuração para tudo funcionar corretamente.

Informar no AndroidManifest.xml que usaremos uma configuração customizada para WorkManager

AndroidManifest.xml

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

Feito isso vamos criar a configuração customizada.

Criando nova configuração para WorkManager

AppNameApplication.kt

@HiltAndroidApp
class AppNameApplication : Application() {

@Inject lateinit var workerFactory: HiltWorkerFactory

override fun getWorkManagerConfiguration(): Configuration {
        return Configuration.Builder()
            .setWorkerFactory(workerFactory)
            .build()
    }
}

Agora sim está tudo pronto para funcionar, você tem uma classe Worker funcionando com injeção de dependências utilizando o Hilt. Parece muito trabalho mas num projeto onde temos vários workers facilita muito o desenvolvimento, Podemos injetar nosso Repository por exemplo e ainda tem a facilidade nos testes.

Detalhe importante

Até o momento as bibliotecas do Hilt não estavam funcionando bem com a biblioteca do kotlin versão 1.5.20 então usem á versão 1.5.10 que não gerou problemas.

Referências:

38