Consumindo REST API no Android com o Ktor
No curso Jetpack Compose: Comunicação com REST API, aprendemos como fazer requisições a uma API a partir de um aplicação Android. Para fazer isso, utilizamos o Retrofit - uma lib famosa na comunidade Android -, porém, vimos que temos uma possibilidade de implementação semelhante com o Ktor - uma lib desenvolvida em Kotlin e baseada em Coroutines!
Pensando em demonstrar a implementação com o Ktor, vou mostrar para você como implementar o desafio proposto no curso de REST API que pede para carregar o endereço automaticamente a partir do CEP consumindo a API do viacep, bora lá?
Passo 1: Instalando o Ktor no projeto Android
Como primeiro passo, adicione as dependências do Ktor no Android no app/build.gradle
:
dependencies {
//other dependencies
def ktor_version = '2.2.4'
implementation "io.ktor:ktor-client-android:$ktor_version"
implementation "io.ktor:ktor-client-logging:$ktor_version"
implementation "io.ktor:ktor-client-content-negotiation:$ktor_version"
implementation "io.ktor:ktor-serialization-kotlinx-json:$ktor_version"
implementation "org.slf4j:slf4j-android:1.7.36"
}
Agora vamos entender para que serve cada uma das dependências:
io.ktor:ktor-client-android
→ Engine de cliente HTTP para Android;io.ktor:ktor-client-logging
→ Permite adicionar o logging das requisições feitas pelo cliente HTTP;io.ktor:ktor-client-content-negotiation
→ Responsável em configurar qual o tipo de conteúdo o cliente vai lidar, por exemplo, JSON ou XML;io.ktor:ktor-serialization-kotlinx-json
→ Conversor de objetos análogo ao Gson, Moshi etc;org.slf4j:slf4j-android
→ Implementação de logger utilizado para realizar o logging.
Após adicionar as dependências, configure as necessárias etapas a mais para a serialização da lib io.ktor:ktor-serialization-kotlinx-json
funcionar corretamente:
- Adicione o plugin no
app/build.gradle
:
plugins {
//other plugins
id 'kotlinx-serialization'
}
- Adicione repositório e dependência no classpath no
build.gradle
:
buildscript {
repositories { mavenCentral() }
dependencies {
classpath "org.jetbrains.kotlin:kotlin-serialization:1.8.10"
}
}
Lembre-se de adicionar o
buildscript
no topo do arquivo.
Com a instalação inicial realizada, sincronize o projeto e você terá acesso a tudo que precisa para configurar o Ktor.
Passo 2: Configurando o cliente HTTP do Ktor
Para configurar o cliente do Ktor, faça seguinte código:
HttpClient(Android) {
install(Logging) {
level = LogLevel.ALL
}
install(ContentNegotiation) {
json(Json {
ignoreUnknownKeys = true
})
}
}
Agora vamos entender o que esse código faz:
HttpClient
→ Referência de cliente HTTP do ktor para realizar as requisições;Android
→ Engine que determina qual cliente HTTP será utilizado por baixo dos panos, por exemplo,HttpsUrlConnection
,OkHttp
etc. Essa implementação de Engine tem o foco em dar suporte para versões do Android mais antigas. Para saber mais detalhes, recomendo que consulte a documentação para conhecer as possibilidades;
install()
→ Instala plugins específicos ao Ktor;Logging
→ Plugin para logging do cliente HTTP;level
→ Determina o nível de logging do plugin - nesse caso,LogLevel.ALL
seria um log mais completo;
ContentNegotiation
→ Plugin para configurar o content negotiation da requisição;json()
→ Registra que o tipo de conteúdo da requisição ContentType é para json;Json { }
→ Cria a instância deJson
do serializador do Kotlin e permite configurar via lambda, como por exemplo oignoreUnknownKeys
que ignora campos desconhecidos durante o parsing.
Essa configuração é útil em situações nas quais o objeto espera apenas alguns campos do JSON de resposta; se essa configuração não for feita, uma exception é lançada durante o parsing caso o objeto não tenha todos os campos esperados.
Independente da REST API que você vai consumir, essa será a configuração base genérica.
Agora que sabemos como adicionar o Ktor em qualquer projeto Android, vamos ao projeto de exemplo que usaremos no artigo.
Passo 3: Conhecendo o projeto de exemplo do artigo
Para a demonstração, vamos considerar o código com Retrofit do desafio de buscar o endereço automaticamente pelo CEP.
Em resumo, vamos modificar o código final do desafio e utilizar o Ktor. Assim, faça o seguinte:
- Acesse o código fonte a partir da branch desafio-endereco;
- Rode o aplicativo e verifique se ele apresenta o seguinte comportamento:
Se tudo estiver rodando conforme o gif acima, prossiga para o próximo passo, pois vamos mexer neste projeto e fazer a implementação de requisição com o Ktor!
Passo 4: Implementando a requisição com o Ktor
Implementar a requisição do Ktor é similar ao Retrofit; a grande diferença é que, no Retrofit, implementamos uma interface que geralmente tem o padrão com sufixo Service
:
interface AddressService {
@GET("{cep}/json")
suspend fun findAddress(
@Path("cep") cep: String
): AddressResponse
}
Já no Ktor, podemos utilizar um padrão de nossa preferência, por exemplo, AddressRestApi
:
package br.com.alura.anyflix.network.restapi
import br.com.alura.anyflix.network.responses.AddressResponse
import io.ktor.client.HttpClient
import io.ktor.client.call.body
import io.ktor.client.request.get
import javax.inject.Inject
import javax.inject.Singleton
private const val BASE_URL = "https://viacep.com.br/ws"
@Singleton
class AddressRestApi @Inject constructor(
private val client: HttpClient
) {
suspend fun findAddress(cep: String): AddressResponse {
return client.get("$BASE_URL/$cep/json").body()
}
}
Observe que, no Ktor, podemos utilizar uma classe em vez de uma interface (padrão do Retrofit), e também, é nesse código do Ktor em que fazemos as configurações de URL base e end-points. Agora, vamos entender o que foi feito:
get()
→ Faz a requisição GET a partir da URL via coroutine;body()
→ Retorna o corpo da requisição de acordo com o tipo esperado, nesse caso,AddressResponse
.
Caso não tenha uma referência que representa o corpo, pode retornar como
String
.
A configuração de injeção de dependência é feita com o Hilt, por isso temos o @Singleton
e o @Inject
. Inclusive, a instância do HttpClient
é oferecida pela configuração do módulo RestApiModule
:
@Module
@InstallIn(SingletonComponent::class)
object RestApiModule {
//rest of the code
@Provides
@Singleton
fun provideHttpClient(): HttpClient {
return HttpClient(Android) {
install(Logging) {
level = LogLevel.ALL
}
install(ContentNegotiation) {
json(Json {
ignoreUnknownKeys = true
})
}
}
}
}
Pronto, temos tudo para testar o Ktor e vamos implementar o código que integra o Ktor com o App.
Passo 5: Implementando o Ktor no repositório
Para fazer o primeiro teste, ajuste o repositório para utilizar o AddressRestApi
em vez do AddressService
:
class AddressRepository @Inject constructor(
// private val service: AddressService,
private val restApi: AddressRestApi
) {
suspend fun findAddress(cep: String): Address? {
return try {
// service.findAddress(cep).toAddress()
restApi.findAddress(cep).toAddress()
} catch (e: HttpException) {
Log.e(TAG, "findAddress: ", e)
null
} catch (e: ConnectException) {
Log.e(TAG, "findAddress: ", e)
null
}
}
}
Pronto! Isso é o suficiente para utilizar o Ktor! O que você achou?
Para saber mais
Dica: Além de realizar requisições HTTP, o Ktor é uma ferramenta capaz de implementar aplicações servidoras, como é o caso de REST APIs. Se você tem interesse em aprender esse tipo de implementação, sugiro que confira o artigo Utilizando o Ktor para criar um CRUD e REST API com Kotlin, aqui da Alura.
Conclusão
Neste artigo aprendemos:
- Instalar o cliente do Ktor em um projeto Android
- Configurar o cliente HTTP do Ktor junto com o Hilt
- Implementar requisição GET com o Ktor
- Integrar a requisição do Ktor com o repositório do projeto
Caso deseje verificar o código final junto com o projeto de exemplo, você pode consultar a branch ktor no repositório do GitHub, ou então, acessar o commit com as mudanças.
Aproveite esse momento para colocar em prática o que aprendeu e compartilhe suas impressões com a gente nas redes sociais ou Discord. 😉
Bons estudos e até mais!