Consumindo API REST no Android com Retrofit em Kotlin — Parte 1

Alex Felipe
CollabCode
Published in
10 min readOct 18, 2017

No artigo onde criamos o Ceep, implementamos uma lista básica utilizando o Recycler View e usamos uma lista de objetos fixa na App para apresentar cada um dos itens.

Como sabemos, atualmente é muito comum desenvolvermos Apps que funcionam tanto offline como online, por exemplo, consumindo APIs REST.

Sendo mais breve, neste artigo veremos como realizamos requisições HTTP na CRUD API que criamos com o Spring Boot. Para esse primeiro contato, o nosso objetivo será pegar todas as notas do servidor! 😊

Quer aprender mais sobre Kotlin tanto no mundo mobile como no back-end? Então confira este agregador de conteúdo onde listo todos os conteúdos que escrevi de Kotlin e os que serão publicados mais pra frente 😉

Decidindo a API para realizar as requisições HTTP

Um dos grandes passos para começarmos com a implementação é justamente decidir a API que iremos utilizar para realizar as requisições.

A princípio, poderíamos utilizar a API do Java HttpURLConnection para estabelecer a comunicação HTTP com a API REST. Entretanto, ela exige muita configuração manual que acaba deixando essa tarefa um tanto quanto não produtiva.

Visando uma implementação mais objetiva e muito comum na comunidade Android, vamos utilizar uma lib que vai facilitar esse processo de realização de requisições, ou seja, o Retrofit.

Além da fama na comunidade Android, existem diversas razões para considerarmos o uso do Retrofit ao invés do modo nativo, dentre elas temos:

  • Serialização de objetos: não precisamos fazer essa tarefa manualmente
  • Configuração: configuramos apenas o necessário para estabelecer a comunicação
  • Usabilidade: parece até engraçado, mas veremos como é bem mais fácil realizar requisições com o Retrofit

Lembrando que o Retrofit não é restrito para uso no Android, você pode utilizá-lo em server-side seja Java ou Kotlin 😃

Preparado para começar? Let’s go!

Gato teclando no modo hardcore

Preparando o ambiente

O nosso primeiro passo para utilizar o Retrofit é colocando a dependência dele no nosso projeto:

dependencies {
// restante das dependências
implementation 'com.squareup.retrofit2:retrofit:2.3.0'
}

Pronto, já podemos utilizá-lo!

Criando a classe de configuração

Agora vamos criar a classe que vai ficar responsável em realizar a configuração de inicialização do Retrofit pra gente, portanto, criaremos a classe RetrofitInitializer:

Classe para inicializar o Retrofit

Existem outras convenções para criar esta classe, como por exemplo, RetrofitConfig, WebClient ou até mesmo via injeção de dependência… Fique à vontade em nomear ou configurar da maneira que achar mais adequado

Começando o processo de configuração do Retrofit

Em seguida, vamos criar a função init() que vai ficar responsável em inicializar o Retrofit:

Criando função para inicializar o Retrofit

Dentro dela, precisamos criar uma instância de um Builder do Retrofit que se trata de uma classe com a responsabilidade de construir um objeto do tipo Retrofit pra gente:

Instanciando o Retrofit.Builder

Definindo uma URL base

Com o Builder em mãos, uma das primeiras configurações que consideramos, é a indicação de uma URL base por meio da função baseUrl():

Veja que adicionei o endereço http://192.168.0.23:8080. Ele indica exatamente o meu IP dentro da rede onde estou, ou seja, dessa forma estarei indicando que vou acessar o localhost na porta 8080. Neste momento você pode estar pensando:

“Se pretende utilizar o localhost, porque não deixou o endereço http://localhost:8080, assim como acessamos a API?”

Peculiaridade ao definir a URL local da API em Apps Android

De fato essa é uma dúvida bem comum e a resposta pra ela é fácil. Se acessarmos o localhost da App, mesmo que seja o emulador, estaremos acessando o endereço do celular!

Em outras palavras, o localhost tanto do celular como do emulador, são diferentes do computador!

Isso significa que ao acessarmos o IP do computador diretamente, mesmo que rode no celular físico, sempre estaremos acessando diretamente a API. Claro, desde que todos os dispositivos estejam na mesma rede.

Caso tenha ficado em dúvida sobre o que seja uma URL base, de forma objetiva, é a URL que vai ficar fixa em todas as requisições que forem feitas.

Isso significa que da forma como configuramos, sempre que for realizada uma requisição, a URL vai sempre começar com o valor http://192.168.0.23:8080.

Configuração do conversor de objetos

Agora que realizamos a primeira configuração, o próximo passo é indicar o conversor de objetos que será utilizado, para isso chamamos a função addConverterFactory():

Chamando a função addConverterFactory

Entretanto, como a ideia do Retrofit é automatizar esse processo, a própria documentação nos sugere alguns conversores já prontos:

  • Gson: com.squareup.retrofit2:converter-gson
  • Jackson: com.squareup.retrofit2:converter-jackson
  • Moshi: com.squareup.retrofit2:converter-moshi
  • Protobuf: com.squareup.retrofit2:converter-protobuf
  • Wire: com.squareup.retrofit2:converter-wire
  • Simple XML: com.squareup.retrofit2:converter-simplexml

Nenhum dos conversores vem por padrão com o Retrofit, ou seja, precisamos adicioná-los via dependência também. Fique à vontade em usar a lib que preferir.

Caso esteja com dúvida na escolha de um conversor por questões de performance ou até mesmo personalização, recomendo que faça uma pesquisa para entender as diferenças entre eles, ou então, você pode implementar o seu próprio conversor seguindo as instruções da documentação.

Adicionando o conversor do GSON

Dentre as possibilidades, vou utilizar o GSON por ser uma lib comum na comunidade Android. Para adicioná-lo, basta apenas inserir a dependência:

dependencies {
// demais dependências
implementation 'com.squareup.retrofit2:converter-gson:2.3.0'
}

Após finalizar o processo de build, precisamos apenas enviar como parâmetro do addConverterFactory() a chamada estática da função create() da classe GSONConverterFactory:

Inserindo o converter do GSON

Pronto, configuramos o conversor, bem fácil, né? Por mais incrível que pareça, já realizamos as configurações necessárias para usar o Retrofit!

Construíndo o objeto do Retrofit

Depois de finalizar a configuração do Builder, podemos construir o objeto da classe Retrofit com a função build():

Construíndo o objeto do Retrofit

Agora que temos um objeto do Retrofit, podemos seguir para o próximo passo que é criar um Service que vai representar a requisição HTTP.

Implementando o Service do Retrofit

Para implementarmos um Service do Retrofit, criamos interfaces indicando o que o Service vai representar, como por exemplo, qual recurso ele vai consumir ou lidar.

Já que o nosso recurso é uma nota, podemos criar a interface NoteService:

Service para representar as notas

Dentro dela, precisamos indicar quais requisições serão representadas, ou melhor, os endpoints.

No nosso caso o nosso objetivo inicial é realizar uma requisição para pegar todas as notas do servidor, sendo assim, inicialmente criamos a assinatura de função list():

Função para representar todas as notas

Em seguida, veja que ainda não estão claras algumas informações da requisição HTTP, como por exemplo, se é um GET ou POST, ou então, qual URL será chamada…

Como sabemos, o nosso endpoint está retornando todas as notas para a requisição GET na URL “/notas” e para indicarmos essa nossa intenção, adicionamos a annotation @GET enviando o parâmetro "notes":

Representando uma requisição do tipo GET para a URL “/notes”

Pronto, configuramos o nosso Service, ou seja, já podemos utilizá-lo!

Começando com o processo da requisição

Agora que temos um objeto do Retrofit e um Service, podemos realizar a chamada dentro da Activity. Portanto, começaremos pela instância da classe e RetrofitInitializer:

Criando a instância da classe RetrofitInitializer dentro do onCreate

Em seguida, precisamos do nosso Service para que seja possível a realização da requisição.

Entretanto, para conseguirmos um objeto do tipo Service que seja vinculado à configuração que realizamos no Retrofit, precisamos ter acesso ao objeto retrofit que criamos na RetrofitInitializer.

Criando a instância do Service

Em outras palavras, precisamos criar uma função dentro do RetrofitInitializer que vai nos devolver uma instância do NoteService:

Criando a função para devolver um NoteService

Para criarmos a instância do NoteService vinculada à configuração que fizemos do Retrofit, precisamos ter acesso ao objeto retrofit que está dentro da função init().

Sendo mais objetivo, faz todo o sentido transformarmos o retrofit em uma property que vai ser acessível por todos os membros da classe, pois, se amanhã surgir mais Services, não teremos problemas em criá-los:

“Existem outras técnicas comuns durante a criação do objeto retrofit, entretanto, por ser um artigo mais introdutório, não entrarei nessa discussão. Caso tiver dúvidas, fique à vontade em perguntar.

Agora, podemos criar a instância do NoteService por meio da função create() do retrofit enviando a referência de classe da interface:

Criando um NoteService a partir do objeto do NoteService

Repara que estamos apenas devolvendo o retorno da função create(), ou seja, quando temos esse tipo de comportamento no Kotlin, podemos resumir mais ainda o código!

Single-Expression function

A técnica para a redução destes casos, trata-se de uma chamada de função em uma única linha, tal feature é conhecida como Single-Expression function:

Transformando uma função em Single-Expression function

Inclusive, nesse tipo implementação, o retorno explícito se torna opcional:

Omitindo o tipo da Single-Expression function

Bem mais simples, concorda?

Existe uma outra técnica que nos permite “reduzir” mais ainda essa chamada transformando em property e modificando o get(). Não entrarei no assunto, mas caso queira saber como fica, é só avisar 😄

Chamando a função do Service

Pronto, temos o nosso Service disponível, basta apenas chamarmos a função list() do NoteService na Activity:

Chamando a função list do NoteService

Por mais que chamemos a função list() que representa a requisição HTTP, o Retrofit utiliza uma entidade responsável pelas chamadas de requisições, conhecida como Call.

Representando a chamada das requisições

Sendo assim, ao invés de vazio (Unit) vamos retornar uma Call na função list():

Declarando uma Call no NoteService

Porém, a Call precisa saber com qual tipo de informação ela vai lidar.

“Como assim?”

Lembra que mencionei que o Retrofit já faz a serialização dos objetos de maneira automática? Então, é na Call que indicamos o objeto que será serializado durante a requisição, ou seja, uma lista de objetos do tipo Note.

Indicando o objeto que vai ser serializado durante a requisição

Para indicarmos essa informação, podemos enviar o parâmetro List<Note> via generics:

Agora, na Activity, podemos pegar a nossa Call:

Retornando a call na função list

Com a call em mãos, podemos realizar a requisição utilizando duas funções:

  • execute(): faz uma chamada síncrona
  • enqueue(): faz uma chamada assíncrona

Por se tratar de um processo que pode demorar (envolve questões de comunicação com o meio externo via rede), o próprio Android não permite que requisições síncronas sejam realizadas na UI Thread, isto é, precisamos realizar uma requisição assíncrona com a função enqueue():

Entretanto, a chamada da função enqueue() exige uma implementação da interface Callback. Neste momento você pode estar pensando:

“O que é um callback?”

Basicamente, callbacks são chamadas de volta de uma requisição, em outras palavras, quando uma requisição finaliza, o callback é chamado para tomarmos uma ação.

“Bacana, mas como posso implementar um callback?”

Uma alternativa seria criar uma classe e implementar a interface Callback, porém, como sabemos, no Java temos a capacidade de criar classes anônimas para implementar interfaces.

Implementando interfaces com classes anônimas

No Kotlin, conseguimos o mesmo resultado a partir da feature Object Expressions:

Implementando o callback com o object expressions

Repara que adicionamos o parâmetro object: Callback<List<Note>?> para indicar qual interface estamos implementando, e então, dentro do escopo do Object Expression, foram declaradas duas funções da interface Callback:

  • onResponse(): quando a comunicação com o servidor é realizada e ele nos devolve uma resposta
  • onFailure(): quando a comunicação com o servidor não acontece ou quando ele apresenta uma resposta de falha

De modo resumido, quando as requisições derem certo, o onResponse() será chamado e, quando falharem, o onFailure() será acionado. Simples, né?

Sendo assim, vamos implementar as funções do callback para tomar uma ação.

Implementando as funções do callback

A primeira delas será aonResponse(), dentro dela, vamos pegar o parâmetro response (que representa a resposta HTTP) e pedir o objeto devolvido pelo corpo da requisição por meio da função body():

Pegando o objeto do corpo da requisição

Veja que neste momento, o Kotlin indica que existe a possibilidade do response ser null, ou seja, podemos aplicar a estratégia do let que vimos lá na implementação do RecyclerView:

Safe Call com let no response

Neste momento retornamos o objeto notes que representa a nossa lista de notas. E tudo indica que temos uma lista de notas segura, certo? Só que não…

Minion no modo “What?!!”

Cuidados com a resposta do callback

Pois é, veja o que acontece quando colocamos o tipo da variável notes no modo explícito:

Deixando o objeto notes com o tipo explícito

Temos uma lista que pode ser null! Portanto, podemos modificar essa chamada utilizando uma chain na Safe Call:

Realizando uma chain de Safe Call junto com o let

E agora temos uma lista segura!!! 😄

Computando o RecyclerView com a resposta do callback

Agora que temos a lista de notas, precisamos apenas modificar a forma como estamos criando a lista do RecyclerView para que receba as notas recebidas pelo onResponse().

Para que o código fique mais legível, vamos extrair a função configureList() que vai receber uma lista de notas e vai manter todo o código que configura o RecyclerView:

Função para manter toda a configuração do RecyclerView

Veja que ao invés de receber a função notes() (que nos devolvia notas criadas dentro da App), agora estamos recebendo as notas por meio do parâmetro notes, portanto, podemos remover a função notes(). E então, basta apenas chamar a função dentro do let:

Chamando a função que configura a lista dentro do onResponse

Pronto, implementamos o onResponse(), agora basta apenas implementar o onFailure(), pois se ocorrer alguma exception durante a requisição, ele que vai ficar responsável em tomar uma ação.

Para este exemplo, vamos imprimir a mensagem do parâmetro t: Throwable em log:

Imprimindo a mensagem de erro do Throwable

Se preferir tomar mais ações, como por exemplo, avisar o usuário com um Toast, fique à vontade.

Adicionando permissões para acessar a internet

Já que a requisição exige uma conexão via rede, precisamos pedir a permissão para o Android. Para isso, inserimos o seguinte código no AndroidManifest.xml:

Adicionando a permissão para acessar a rede do celular

Com toda implementação feita, podemos executar a nossa App:

App Ceep com as notas recebidas da API

Opa! Agora sim em! Nossa App está se comunicando com a nossa API e buscou todas as notas! 😉

Panda com uma dancinha marota haha

Código fonte

Caso tenha dúvida em relação à implementação, fique à vontade em consultar o código fonte que deixei no repositório do GitHub.

Claro, aproveite e pergunte nos comentários também!

Para saber mais

Conseguimos realizar a requisição com o Retrofit, entretanto, ainda existem pontos pelos quais precisamos ficar atentos, como por exemplo:

  • Requisição dentro da Activity: como sabemos, não faz parte da responsabilidade da Activity realizar uma requisição
  • Implementação do Callback: atualmente escrevemos muito código para implementar o callback, algo que pode prejudicar a leitura do código

Pensando justamente nesses pontos, logo mais veremos técnicas que lidam com esse tipo de cenário.

Além disso, fizemos apenas um GET no servidor, sendo que temos a capacidade de realizar um CRUD!

Não se preocupe! Implementaremos a funcionalidade de inserção das nossas notas, como também, veremos uma técnica que permite desacoplar entidades no Android como é caso do callback dentro da Activity.

Em outras palavras, deixo a segunda parte do artigo de Retrofit com Kotlin:

Conclusão

Neste artigo aprendemos a realizar requisições HTTP com o Retrofit em uma App Android utilizando o Kotlin. E como foi visto, o Retrofit facilita e tanto a nossa vida neste tipo de tarefa.

Também vimos que existem diversas peculiaridades, como por exemplo, adicionar um conversor, implementar um Service, chamada síncrona assíncrona e a utilização de um callback.

Por fim, vimos que nesse tipo de tarefa temos que pedir as permissões de acesso a rede para o Android.

E aí, o que achou do Retrofit? Compartilhe comigo sua experiência durante a implementação ou, se tiver alguma sugestão, é muito bem vinda também 😄

--

--