Primeiro episódio sobre Hilt da série MAD Skills Esta é a série de artigos do MAD Skills sobre Hilt. Neste artigo, veremos por que a injeção de dependências (DI, na sigla em inglês) é importante para os apps e o Hilt, a solução recomendada do Jetpack para DI no Android.
Se preferir ver este conteúdo em vídeo, acesse este link:
Ao seguir os princípios de injeção de dependências nos apps Android, você estabelece as bases para uma boa arquitetura de apps. Isso contribui para a reutilização de código e a facilidade de refatoração e testes. Saiba mais sobre os benefícios da DI aqui .
Ao criar instâncias de classes nos projetos, você pode exercitar o gráfico de dependências manualmente atendendo às dependências diretas e transitivas exigidas pela classe.
Mas fazer isso manualmente sempre envolve algum código boilerplate e pode estar sujeito a erros. Veja, por exemplo, um dos ViewModels que temos no iosched , o app Google I/O de código aberto. Você tem ideia da quantidade de código necessária para criar um FeedViewModel com as dependências diretas e transitivas?
Essa uma tarefa difícil e repetitiva, e podemos facilmente errar nas dependências. Por meio de uma biblioteca de injeção de dependências, podemos ter os benefícios da DI sem precisar fornecer as dependências manualmente, já que a biblioteca gera todo o código necessário para você. É aí que entra o Hilt.
Hilt O Hilt é uma biblioteca de injeção de dependências desenvolvida pelo Google que ajuda a obter o máximo das práticas recomendadas de DI nos apps ao fazer o trabalho mais difícil e gerar todo o boilerplate que você precisaria escrever em outra situação.
Por meio de anotações, o Hilt gera esse código para você no tempo de compilação, tornando-o muito rápido no tempo de execução. Isso é feito com o poder do Dagger , a biblioteca de DI do JVM na qual o Hilt é baseado.
O Hilt é a solução de DI recomendada do Jetpack para apps Android e vem com ferramentas e suporte a outras bibliotecas do Jetpack. Início rápido Todos os apps que usam o Hilt devem conter uma classe Application que é anotada com @HiltAndroidApp, pois isso dispara a geração de código do Hilt no tempo de compilação. E, para que o Hilt possa injetar dependências em uma atividade, ela precisa ser anotada com @AndroidEntryPoint.
Para injetar uma dependência, anote com @Inject as variáveis que você deseja que o Hilt injete. Todas as variáveis injetadas pelo Hilt são disponibilizadas com a chamada a super.onCreate.
Neste exemplo, vamos injetar um MusicPlayer em PlayActivity. Mas como o Hilt sabe de que forma deve fornecer instâncias do tipo MusicPlayer? Bem, ele não sabe, por enquanto. Precisamos dizer ao Hilt como fazer isso… usando anotações, é claro!
Anotar o construtor de uma classe com @Inject diz ao Hilt como criar instâncias dessa classe.
Isso é tudo de que precisamos para injetar uma dependência em uma Activity. Essa parte foi fácil! Começamos com um exemplo simples, já que o MusicPlayer não depende de nenhum outro tipo. No entanto, se tivéssemos outras dependências passadas como parâmetros, o Hilt cuidaria disso e atenderia a essas dependências fornecendo uma instância do MusicPlayer.
Esse foi um exemplo muito simples e ingênuo, na verdade. Mas se você tivesse que fazer manualmente o que fizemos até agora, como faria?
Como fazer isso manualmente Ao fazer a injeção de dependências manualmente, você pode ter classes de contêiner de dependências que são responsáveis por fornecer tipos e gerenciar o ciclo de vida das instâncias fornecidas. Para simplificar um pouco, é isso o que o Hilt faz nos bastidores!
Quando você anota a Activity com @AndroidEntryPoint, um contêiner de dependências é criado, gerenciado e associado automaticamente a PlayActivity. Vamos chamar nossa implementação manual de PlayActivityContainer. Ao anotar MusicPlayer com @Inject, basicamente dizemos ao contêiner como fornecer instâncias do tipo MusicPlayer.
E, na Activity, precisaríamos criar uma instância do contêiner e preencher as dependências da atividade com ela. Isso também é feito pelo Hilt quando a atividade é anotada com @AndroidEntryPoint.
Resumo de anotações Até agora, vimos que quando @Inject é usado para anotar o construtor de uma classe, isso diz ao Hilt como fornecer instâncias dessa classe. E quando anota uma variável em uma classe @AndroidEntryPoint anotada, o Hilt injeta uma instância desse tipo na classe.
@AndroidEntryPoint , que pode anotar a maioria das classes de framework do Android, e não apenas atividades, cria uma instância de um contêiner de dependências para essa classe e preenche todas as variáveis @Inject anotadas.
@HiltAndroidApp anota a classe Application e, além de disparar a geração de código do Hilt, também cria um contêiner de dependências associado à classe Application.
Módulos do Hilt Agora que já sabemos o básico sobre o Hilt, vamos complicar o exemplo. O MusicPlayer agora toma uma dependência de seu construtor, MusicDatabase.
Portanto, precisamos dizer ao Hilt como fornecer instâncias de MusicDatabase. Quando o tipo é uma interface ou você não possui a classe porque ela vem de uma biblioteca, por exemplo, não é possível anotar o construtor com @Inject.
Vamos imaginar que estamos usando o Room como biblioteca de persistência no app. De volta à nossa implementação manual de PlayActivityContainer, ao fornecer MusicDatabase com o Room, essa seria uma classe abstrata na qual gostaríamos de executar algum código para fornecer a dependência. Depois, para fornecer uma instância de MusicPlayer, precisamos chamar o método que fornece ou satisfaz a dependência de MusicDatabase.
Não precisamos nos preocupar com dependências transitivas no Hilt, já que ele conecta todas as dependências transitivas automaticamente. No entanto, precisamos dizer a ele como fornecer instâncias do tipo MusicDatabase. Para isso, usamos os módulos do Hilt.
Um módulo do Hilt é uma classe anotada com @Module. Nessa classe, podemos ter funções que dizem ao Hilt como fornecer instâncias de determinados tipos. Essas informações conhecidas pelo Hilt também são chamadas de vinculações no jargão do Hilt.
A função anotada com @Provides diz ao Hilt como fornecer instâncias do tipo MusicDatabase. O corpo contém o bloco de código que o Hilt precisa executar, e isso é exatamente igual ao que tínhamos na implementação manual.
O tipo de retorno, MusicDatabase, informa ao Hilt o tipo que essa função fornece. E os parâmetros da função indicam ao Hilt as dependências do tipo correspondente. Nesse caso, ApplicationContext, já disponível no Hilt. Esse código informa ao Hilt como fornecer instâncias do tipo MusicDatabase ou, em outras palavras, temos uma vinculação para MusicDatabase.
Os módulos Hilt também são anotados com @InstallIn, que indica em quais contêineres de dependências ou componentes essas informações estão disponíveis. Mas o que é um componente? Vamos explicar em mais detalhes.
Componentes do Hilt Um componente é uma classe gerada pelo Hilt que é responsável por fornecer instâncias de tipos, como o contêiner que estamos programando manualmente. No tempo de compilação, o Hilt percorre o gráfico de dependências do aplicativo e gera um código para fornecer as dependências transitivas a todos os tipos.
Um componente é uma classe gerada pelo Hilt que é responsável por fornecer instâncias de tipos O Hilt gera um componente, ou contêiner de dependências, para a maioria das classes de framework do Android. As informações, ou vinculações, de cada componente se propagam pela hierarquia de componentes.
Hierarquia de componentes do Hilt Se a vinculação MusicDatabase estiver disponível no SingletonComponent, que corresponde à classe Application, ela também estará disponível no restante dos componentes.
Esses componentes são gerados automaticamente pelo Hilt no tempo de compilação e são criados, gerenciados e associados à classe de framework do Android correspondente quando você anota essas classes com @AndroidEntryPoint.
A anotação @InstallIn para módulos é útil para controlar onde essas vinculações estão disponíveis e que outras vinculações podem ser utilizadas.
Definição do escopo De volta ao nosso código PlayActivityContainer criado manualmente, não sei se você percebeu, mas sempre que a dependência MusicDatabase é necessária, estamos criando outra instância dela.
Isso não é o ideal, pois podemos querer reutilizar a mesma instância de MusicDatabase em todo o app. Em vez de uma função, poderíamos compartilhar a mesma instância tendo tudo isso em uma variável.
Basicamente, estamos definindo o escopo do tipo MusicDatabase para esse contêiner, já que sempre fornecemos a mesma instância como dependência. Como fazer isso com o Hilt? Bem, aqui não há mistério… com outra anotação!
Ao usar a anotação @Singleton no método @Provides, dizemos ao Hilt para sempre compartilhar a mesma instância desse tipo naquele componente.
@Singleton é uma anotação de escopo. E cada componente do Hilt tem uma anotação de escopo associada.
Anotações de escopo para cada componente do Hilt Se você quiser definir o escopo de um tipo para o ActivityComponent, use a anotação ActivityScoped. Essas anotações podem ser usadas em módulos, mas também podem anotar classes cujo construtor é anotado com @Inject.
Vinculações Existem dois tipos de vinculações:
As vinculações que NÃO têm uma anotação de escopo são chamadas de vinculações sem escopo , como MusicPlayer, e essas vinculações estarão disponíveis para todos os componentes se não estiverem instaladas em um módulo. As vinculações com escopo que têm uma anotação de escopo, como MusicDatabase, ou as vinculações sem escopo que são instaladas em um módulo ficam disponíveis no componente correspondente e nos componentes abaixo dele na hierarquia. Extensões do Jetpack O Hilt oferece integrações com as bibliotecas do Jetpack mais populares: ViewModel, Navigation, Compose e WorkManager. Além de ViewModel, cada integração requer uma biblioteca diferente para adição ao projeto. Confira a documentação para obter mais informações sobre isso. Você se lembra do código FeedViewModel do iosched que vimos no início desta postagem do blog? Quer ver como ele fica com o suporte do Hilt?
Além de anotar o construtor com @Inject, para dizer ao Hilt como fornecer instâncias desse ViewModel, precisamos anotar a classe com @HiltViewModel.
Pronto! Você não precisa criar manualmente um provedor ViewModel para isso. O Hilt se encarrega da tarefa.
Saiba mais! O Hilt é baseado em outra biblioteca popular de injeção de dependências: o Dagger ! O Dagger será muito mencionado nos próximos episódios! Se você já usa o Dagger, saiba que ele e o Hilt podem trabalhar juntos. Leia mais sobre as APIs de migração no guia .
Para saber mais sobre o Hilt, temos uma folha de referência com as anotações mais populares, o que elas fazem e como utilizá-las. Além dos documentos sobre o Hilt, também temos codelabs para aprender com uma experiência mais prática.
E assim terminamos este episódio. Mas a história não acaba aqui. Há mais episódios por vir na série MAD Skills, então siga a publicação do Android Developers no Medium para ver quando eles serão postados.
2 comentários :
Onyx-Circle
Onyx Circle AG brings talents and companies together. Our broad network within the industries makes us a strong partner
when it comes to tracking down and connecting with experts.Digitalization
Digitization is permanently changing all areas of our economy and the relevant labor markets. Methods of networked planning, implementation
and management through the use of software dominate in the areas of construction and real estate management as well as in the industrial
sectors through the increased use of automation.
I used the RxHacker mobile app and found a cheaper Pharmacy Discount in my town. I saved $108 on my last prescription
Postar um comentário