Hilt Series: Architecture Components com Dagger Hilt — ViewModel

Aprenda como injetar o ViewModel utilizando o Dagger Hilt no seu projeto

Ramon Ribeiro Rabello
Android Dev BR
5 min readAug 4, 2020

--

Photo by Jed Wood on Unsplash

Esse é o segundo artigo da Hilt Series, que é composta de várias partes:

Parte 1: Introdução ao Dagger Hilt

Parte 2: Architecture Components com Dagger Hilt — ViewModel

Parte 3: Migrando do Dagger para o Hilt (em breve)

Parte 4: Testes com Hilt (em breve)

Nesta segunda parte, será demonstrado um breve histórico sobre Architecture Components, comparativo de como injetar ViewModel com Dagger e como o Hilt pode ser facilmente integrado ao Architecture Components. Esse artigo irá focar apenas no ViewModel, por questão de escopo. A integração com outros componentes, como o Room, serão demonstrados no próximo artigo.

Relembrando: Architecture Components

Durante o I/O 2017, foi anunciado o Architecture Components, um conjunto de bibliotecas e guidelines criada pela Google para definição da arquitetura de projetos Android mais modernos, que englobava desde persistência, paginação, etc. Dentre os componentes lançados, o ViewModel foi uma das grandes sensações, pois, juntamente com o LiveData e Lifecycles, trouxe a tão aclamada solução para umas das maiores dores no desenvolvimento Android: o gerenciamento do ciclo de vida e retenção do estado após mudança de configuração, mesmo que inicialmente de forma incompleta.

Injetando ViewModel com Dagger

Logo que Architecture Components foi lançado, uma das dúvidas era como injetar o ViewModel em um projeto gerenciado e modularizado usando Dagger. Infelizmente, a injeção de um ViewModel com o Dagger ainda é disparadamente a que mais exige boilerplate, tanto que foi necessário um artigo dedicado para exemplificar todas as possíveis abordagens — e nenhuma delas é trivial.

Diante dessas várias abordagens, a que ficou mais popular foi a injeção por meio de multibindings, uma feature do Dagger 2 que permite realizar o binding de uma coleção de objetos, mesmo que estejam sendo providos em módulos diferentes. Para isso, a anotação @IntoMap era utilizada juntamente com @Binds para prover os ViewModels de acordo com uma chave, geralmente representada por uma anotação customizada, como @ViewModelKey.

Além disso, era necessário fornecer uma ViewModelProvider.Factory que seria responsável para realizar a construção dos ViewModels, como mostra o código a seguir.

Apesar da complexidade do código acima, o que essa factory faz é disponibilizar um ViewModel a partir de viewModelsMap, de acordo com o tipo da classe informada para a anotação @ViewModelKey.

Depois disso, será preciso criar outro Module que irá prover uma ViewModelFactory, da seguinte maneira:

Agora, basta vincular a ViewModelFactoryModule ao componente de escopo de aplicação, comumente denominado de ApplicationComponent nos projetos com Dagger:

Por fim, basta adicionar @Inject à declaração da ViewModelProvider.Factory:

Ou de forma bem concisa por meio da extension function viewModels(), disponível na biblioteca activity-ktx ou fragment-ktx:

E se esses passos pudessem ser simplificados? È aí que o Dagger Hilt entra em cena para deixar as coisas muito mais simples e produtivas, como vamos ver a seguir.

Injetando ViewModel com Dagger Hilt

Basicamente, o Hilt simplifica a injeção do ViewModel em apenas 2 passos. Mas antes, precisamos adicionar no arquivo build.gradle da aplicação a bibliteca de integração do Hilt com Lifecycle e ViewModel e depois fazer o sync do Gradle para baixar as dependências:

Depois, adicione a anotação @ViewModelInject no construtor do ViewModel:

No código acima, ao adicionar a anotação @ViewModelInject, o Hilt irá automaticamente realizar a injeção do ViewModel em qualquer Android Entry Point, como Activity ou Fragment. A anotação @Assisted é opcional e serve para indicar uma “injeção assistida”, isto é, o SavedStateHandle será injetado em tempo de execução utilizando uma AssistedFactory, porém esse parâmetro não fará parte do grafo de dependência.

Por fim, declare o ViewModel dentro de um @AndroidEntryPoint:

Qual a mágica que o Hilt faz para injetar o ViewModel?

Você notou que para realizar a injeção do ViewModel com o Hilt, não foi necessário criar mais nenhum Module ou Factory ou Annotation? Mas qual a mágica por trás de tudo isso?

Quando o Hilt identifica a anotação @ViewModelInject no construtor de um ViewModel, ele automaticamente gera todas as factories e modules necessários para realizar a injeção de dependência. O código abaixo é da classe LoggingViewModel_HiltModule gerada pelo Hilt para prover um ViewModelAssistedFactory. Ela fica localizada na pasta app/build/generated/source/kapt/debug.

Observe a utilização das anotações @Binds, @IntoMap e @StringKey, algo semelhante como o ViewModelBindingModule foi definido usando Dagger 2. Um outro ponto importante para ressaltar é a utilização da anotação @InstallIn(ActivityRetainedComponent.class).

Como foi apresentado na parte 1 desta série, o Hilt define uma hierarquia de componentes por padrão, onde cada componente possui um escopo exclusivo. O componente ActivityRetainedComponent é utilizado quando é necessário injetar um comportamento que deva perdurar, mesmo se houver mudança de configuração, que no caso, é característica do ViewModel.

Outro código disponibilizado pelo Hilt interessante de mencionar é o HiltViewModelFactory, que foi simplificado por questão do escopo deste artigo.

Essa Factory já contém toda a complexidade de fornecimento de um ViewModel parametrizado com um SavedStateHandle, para fins de persistir o estado mesmo depois de mudança de configuração.

Conclusão

Esse artigo apresentou um breve histórico sobre Architecture Components, mostrou a complexidade em torno da injeção de ViewModel utilizando Dagger e como que o Hilt simplifica esse processo todo por meio da utilização da anotação @ViewModelInject para geração automática dos modules e factories necessárias para realizar a injeção de dependência do ViewModel. Na parte 3 desta série, será apresentado passo-a-passo como migrar do Dagger 2 para Hilt. Todo o código utilizado neste artigo, está disponível neste repositório no GitHub, no branch arch-viewmodel-inject.

Links

--

--