MerryAnnotation – Seu primeiro processador de anotações no Kotlin Android

Navendra Jha Blocked Unblock Seguir Seguindo 24 de dezembro de 2018

A temporada de férias nos dá um bom tempo de inatividade para relaxar e refletir sobre o ano passado e para nos prepararmos para o Ano Novo. Para mim, este ano tinha muitas coisas na loja, comecei o Android Development, me apaixonei por Kotlin, participei do meu primeiro AndroidDevSummit no Computer History Museum, que também foi a comemoração de 10 anos do Android e, mais importante, conheci muitas pessoas incríveis e fez novos amigos.
Por muito tempo, eu queria desenvolver um processador de anotações personalizado como seu presente em todos os lugares no android. Eu finalmente desenvolvi uma, aprendi muitas coisas no processo e estou animado para compartilhar meus aprendizados.

Foto de Tyler Delgado em Unsplash

O Android é preenchido com anotações, seja um simples @Orride para uma classe ou um @BindView do ButterKnife. A anotação junto com a geração de código facilita nossa vida. Ele ajuda no princípio DRY (Não Repita-se) e nos ajuda a desenvolver uma aplicação melhor de maneira fácil e rápida. Neste artigo, vamos aprender um processador básico de anotações escrito em Kotlin para uma aplicação Android. Também estaremos gerando o código sem usar nenhuma biblioteca personalizada como o KotlinPoet ou o JavaPoet.
Vamos começar…

Entendendo como funciona o processamento de anotações

Anotações são metadados. Podemos adicionar metadados de anotações a qualquer classe, método ou parâmetro adicionando @ na frente deles.

  • O compilador Java faz uma pesquisa exaustiva e encontra todos os elementos no projeto anotados por nossas anotações e os passa para o Processador de Anotações para processá-los.
  • O processador de anotação obtém todos os elementos com anotações, seus tipos e o ambiente do processo.
  • O processador de anotação processa os elementos recebidos e, no nosso caso, geramos os arquivos de classe necessários.

Referências para uma explicação mais detalhada:
Processamento de anotações em um mundo Kotlin por Henri Zac Sweers
Processamento de Anotações Boilerplate Destruction by Jake Wharton

MerryAnnotation – Visão geral

Na MerryAnnotation, estaremos gerando uma saudação de "Feliz Natal", anotando uma classe Greeter.

Nossa MainActivity será semelhante ao seguinte:

Como você pode ver, criamos uma " classe Santa" na linha 13 e a anotamos com nossa anotação @GreetingGenerator personalizada. Em seguida, estamos usando o método greeting () da classe “Generated_Santa” na linha 19 e definindo nosso texto textView com a saudação retornada.
A “classe Generated_Santa” é gerada automaticamente no tempo de execução com um método greeting () que retorna uma string. Este é o nosso objetivo final!

Link do Github

Se você quiser pular diretamente para o código-fonte completo, acesse aqui.
Caso contrário, siga todo o blog para obter uma implementação detalhada passo a passo de um processador de anotação personalizado usando o Kotlin.

Criando Anotação e Módulo CodeGen

Vamos trabalhar no formato multi-módulo. Siga as etapas abaixo para configurar um projeto de vários módulos no Android Studio.

  • Crie um novo projeto no Android Studio e verifique o suporte ao Kotlin.
  • Crie um novo módulo de Arquivo> Novo> Novo Módulo> Biblioteca Java.
  • Nomeie a biblioteca Java como “anotação” . Neste módulo, incluiremos todas as nossas anotações personalizadas neste módulo.
  • Repita as etapas acima para criar um novo módulo e nomeie-o como "codegen". Vamos incluir toda a nossa lógica de geração de código aqui.
  • Nesta fase, temos quatro arquivos build.gradle – app, annotation, codegen e project.
  • Modifique-os como mostrado abaixo

build.gradle (módulo: app)

 aplique o plugin: 'kotlin-kapt' 
 dependências { 
...
 projeto de implementação (': anotação') 
projeto kapt (': codegen')
}

build.gradle (módulo: anotação)

 aplicar plugin: 'kotlin' 

dependências {
implementação fileTree (dir: 'libs', include: ['* .jar'])
implementação "org.jetbrains.kotlin: kotlin-stdlib-jdk7: $ kotlin_version"
}

sourceCompatibility = "7"
targetCompatibility = "7"

repositórios {
mavenCentral ()
}
compileKotlin {
kotlinOptions {
jvmTarget = "1.8"
}
}
compileTestKotlin {
kotlinOptions {
jvmTarget = "1.8"
}
}

build.gradle (módulo: codegen)

 aplicar plugin: 'kotlin' 
aplique o plugin: 'kotlin-kapt'

kapt {
generateStubs = true
}

sourceSets {
a Principal {
java {
srcDir "$ {buildDir.absolutePath} / tmp / kapt / main / kotlinGenerated /"
}
}
}


dependências {
projeto kapt (": annotation")
compileOnly project (': annotation')
implementação fileTree (include: ['* .jar'], dir: 'libs')
implementação "org.jetbrains.kotlin: kotlin-stdlib-jdk7: $ kotlin_version"

// gerador de configuração para provedores de serviços
implementação "com.google.auto.service: auto-service: 1.0-rc4"
kapt "com.google.auto.service: auto-service: 1.0-rc4"

}

sourceCompatibility = "7"
targetCompatibility = "7"

Vamos entender o que estamos fazendo aqui. Em nosso módulo de aplicativo, adicionamos dependências para o módulo de anotação e o uso do kapt (processador de anotações Kotlin) para o módulo codegen , que é nosso processador de anotações personalizado.
Para o módulo de anotação e codegen build.gradle acabamos de transformá-lo da biblioteca Java para a biblioteca Kotlin. Na Codegen, também adicionamos dependências de AutoServiço para vincular serviços.

Criando nossa Anotação Personalizada no Módulo de Anotação

Crie um novo arquivo Kotlin no módulo de anotação e nomeie-o como GreetingGenerator. O Kotlin tem um ótimo suporte para classes de anotação e usaremos a classe de anotações do Kotlin para criar nossa anotação personalizada da seguinte forma:

Aqui nós criamos uma nova classe de anotação GreetingGenerator com atributos conforme necessário. Os atributos adicionais são descritos abaixo. (Desafortunadamente copiado do Kotlin Docs. Pode ser encontrado aqui .)

Noções Básicas de Anotação Kotlin do Kotlin Docs

Criando nosso processador de anotação personalizado no módulo CodeGen

Crie um novo arquivo Kotlin no módulo codegen e nomeie-o como FileGenerator. Crie uma nova classe FileGenerator e estenda-a da classe AnnotationProcesor, que nos fornecerá dados de ambiente que serão úteis para gerar código.

Vamos ver o que estamos fazendo aqui.

método process ()

  • Este é um dos métodos mais importantes da classe, aqui escrevemos nossa lógica de processamento. Substituir este método. Recebemos RoundEnvironment no método que usaremos para obter todos os elementos anotados com uma anotação GreetingGenerator, conforme mostrado na seguinte linha:
 roundEnvironment 
? .getElementsAnnotatedWith (GreetingGenerator :: class.java)
  • Isso nos dá uma lista de elementos anotados com nossa anotação. Então, para cada elemento, obteremos o nome da classe, o nome do pacote e, em seguida, generateClass, que é nossa função personalizada para gerar código durante o tempo de compilação, conforme mostrado nas seguintes linhas:
 ?.para cada { 
val className = it.simpleName.toString ()
val pack = processingEnv.elementUtils
.getPackageOf (it) .toString ()
generateClass (className, pacote)
}

Método generateClass

  • Neste método, escrevemos a lógica para gerar nossa classe. Primeiro, obtemos fileName e usamos nossa classe Custom KotlinClassBuilder para gerar o conteúdo necessário do arquivo, conforme mostrado:
 val fileName = "Gerado_ $ className" 
val fileContent = KotlinClassBuilder (fileName, pacote) .getContent ()
  • Em seguida, obtemos o diretório de destino e criamos um novo arquivo nesse diretório com o fileContent obtido anteriormente, conforme mostrado:
 val kaptKotlinGeneratedDir = processingEnv 
.options [KAPT_KOTLIN_GENERATED_OPTION_NAME]
 val file = Arquivo (kaptKotlinGeneratedDir, "$ fileName.kt") file.writeText (fileContent) 

O KAPT_KOTLIN_GENERATED_OPTION_NAME é definido no objeto do componente como “kapt.kotlin.generated”, que é o diretório do qual o kapt compilará todos os arquivos junto com as fontes principais.

Aqui, poderíamos ter gerado nosso conteúdo de arquivo usando qualquer biblioteca de gerador de código como o KotlinPoet, mas para fins de aprendizado, vamos construir nosso CodeGenerator simples também.

KotlinClassBuilder – Nosso CodeGenerator personalizado

Crie uma classe KotlinClassBuilder e adicione o método getContent () que retorna a string fileContent requerida, conforme mostrado:

Aqui nós adicionamos className, package e greeting como variáveis construtoras e as usamos para gerar nosso conteúdo de arquivo. Como sabemos, precisamos gerar a seguinte classe final:

 package <package_name> 
 class <class_name> { 
 divertido saudação () = "<saudação>" 
 } 

Neste KotlinClassBuilder, usamos o modelo String e geramos a string acima passando o nome do pacote, o nome da classe e a saudação apropriados.

Fuga

Finalmente, clique em build e confira app> build> generated> kaptKotlin> debug, ele conterá nossa classe gerada – Generated_Hello () conforme necessário.

Agora aperte run e você será saudado com uma atividade com um textView dizendo “Merry Christmas !!”.

Mais leituras e boas referências

@Eliminate ("Boilerplate")
Todo mundo sabe que a programação Java envolve um bom código clichê, mas também tem ótimos recursos para… academy.realm.io
O Guia de 10 Passos para o Processamento de Anotações no Android Studio – stable | kernel
Ao trabalhar em uma biblioteca de testes no meu tempo livre, achei que as anotações seriam úteis para criar um gráfico… stablekernel.com
Processamento de Anotações 101
Nesta entrada de blog, gostaria de explicar como escrever um processador de anotações. Então aqui está o meu tutorial. Primeiro eu sou… hannesdorfmann.com

Finalmente material de leitura mais importante

Anotações – Linguagem de Programação Kotlin
Quando você está anotando uma propriedade ou um parâmetro de construtor primário, existem vários elementos Java que são… kotlinlang.org

Muito obrigado
Codificação Feliz e Feliz Natal !!