Como criar suas próprias bibliotecas

Bibliotecas estão presentes na vida do desenvolvedor de várias formas: organizar código, poupar escrita, testes, clients HTTP… e por ai vai! Uma prática muito comum é reutilizarmos o nosso código de projetos antigos em projetos novos.

E quem nunca deu aquele Ctrl + C e Ctrl +V? ¯\_(ツ)_/¯

O problema de copiar o código de projetos antigos é que, na maioria das vezes, acabamos criando algo novo, ou até mesmo corrigindo um bug, e esquecemos que precisamos realizar a manutenção dos nossos códigos antigos.

Uma boa prática é abstrairmos esse código da aplicação. Criando uma biblioteca!

Primeiros passos

Vamos criar uma lib (abreviação de ‘library’) bem simples que apenas exibe informações no LogCat — Sim, eu sei que essa lib é inútil, mas vamos nos focar no conceito e deixar a sua aplicação (ou utilidade) de lado por enquanto.

Todo o código fonte usado nesse tutorial está disponível aqui.

Antes de tudo, crie um módulo novo e selecione a opção Android Library.

Até esse ponto, a criação de uma lib não é muito diferente de um app, mas uma observação importante é com o Module name: ela estará no compile do gradle.


Agora que já criamos o nosso módulo, podemos trabalhar em cima dele para desenvolvermos nossa lib.

A estrutura é a mesma de uma aplicação android, com androidTest para UI, se existirem, main, onde escreveremos a nossa lib e test, onde podem ser feitos os testes unitários.


Quando eu digo que a estrutura é a mesma, não é brincadeira:

~/git/Logger/logger/build/outputs/aar/logger-release.aar
├── AndroidManifest.xml
├── R.txt
├── aidl
├── assets
├── classes.jar
├── jni
├── libs
└── res
└── values
└── values.xml

Vale lembrar que todas as permissões, activities, services etc declaradas no AndroidManifest serão colocadas junto às das aplicações que estarão usando sua lib, graças ao Manifest Merge. Como o foco do artigo não é falar sobre a estrutura seguida nos arquivos .aar, não vamos nos aprofundar no assunto. Mas podemos encontrar mais informações aqui.


Logger

Como exemplo, nossa classe de log será algo bem simples:

Por enquanto, vamos referenciar a lib direto no app/build.gradle

Agora podemos fazer a chamada dentro da nossa aplicação:

print simples da imagem

Distribuição

Sem dúvidas esse é um dos pontos mais importantes (de qualquer projeto) e muito pouco pensado no desenvolvimento. Talvez para nós, devs android, seja uma coisa bem óbvia, a Google Play. Mas se tratando de uma biblioteca, nós podemos fazer a distribuição de N formas: disponibilizar o .aar publicamente, apenas o .jar, maven etc

Para esse exemplo, vamos usar uma ferramenta de distribuição chamada JFrog. Nela nós podemos fazer upload de várias ferramentas, SDKs, bibliotecas e mais com npm, nuget, maven, docker e gradle!

Como esse projeto é open source, podemos fazer o upload com a conta free.

Por default, a ferramenta já nos disponibiliza um repositório maven (para onde enviaremos a biblioteca).

Todo compile do gradle possui uma regra na sua estrutura, como essa:

Precisamos adicionar os plugins para deploy:

Vamos no logger/build.gradle e adicionar os plugins também:

Mapeamos os repositórios git…

Estamos quase lá! Para finalizarmos, precisamos da nossa apikey do Bintray. Podemos obtê-la facilmente acessando as configurações da conta no próprio site da Bintray e copiar na aba 'API KEY'. Após obter a sua chave, coloque-a no arquivo local.properties

Gradle sincronizado com sucesso? Então estamos prontos para publicar!

Agora só será necessário rodar o comando no terminal

./gradlew bintrayUpload
// gradle compilando aqui
BUILD SUCCESSFUL

BUILD SUCCESSFUL

Mas Jão, BUILD SUCCESSFUL? Só isso? Então tá no ar??
— Aham!

Para finalizar, vamos remover a referência local que temos da lib na aplicação e apontar para a que está no JFrog.

/build.gradle

E no app/build.gradle

Sync now e está feito!

Extra

Quando desenvolvemos uma biblioteca, temos que ter alguns pontos em mente:

  1. USE e ABUSE de annotations — Annotations ajudam o desenvolvedor em tempo de edição e criação de algo mais concreto, com @NonNull e @Deprecated, por exemplo.
  2. Não quebre as interfaces! — É de extrema importância respeitarmos as interfaces que o cliente está utilizando com nossa biblioteca, então evite coisas como: alterações de parâmetros, métodos que retornam objetos diferentes após updates etc.
  3. Utilize a menor quantidade de dependências possíveis! — Isso influencia e muito na aplicação do seu cliente e no DEX.

Conclusão

Vimos como uma simples prática pode melhorar e muito a qualidade da distribuição, escalabilidade e manutenção do nosso código.

Caso você tenha alguma dúvida sobre o assunto, sugestão ou queira falar sobre, sinta-se livre para falar comigo no twitter que eu to sempre por lá!

May the force be with you — Yoda