Geonode logo
Geonode Team

Geonode Team

Atualizado: 7 de outubro de 2026

Publicado: 2 de setembro de 2026

OkHttp em Java: Guia de Introdução

O OkHttp é o cliente HTTP que a maioria dos projetos JVM e Android acaba por utilizar, e apresenta duas decisões de conceção que podem confundir os principiantes: o cliente foi concebido para ser partilhado e os corpos das respostas têm de ser fechados. Se acertar nestes pontos, tudo o resto é simples. Se errar, irá perder ligações até que algo deixe de funcionar. Este guia aborda os conceitos básicos, os interceptores, os tempos de espera e os proxies — além de uma nota sobre a localização atual do projeto, que sofreu alterações.

O nosso ponto de vista: somos a Geonode e comercializamos proxies, e a configuração do proxy do OkHttp é realmente invulgar — a autenticação do proxy é feita através de um proxyAuthenticator, e não de um cabeçalho, e se isso for feito incorretamente, gera um erro 407 que parece ser um problema de credenciais, quando na verdade é um problema de configuração. Essa secção encontra-se perto do final. Tudo o que vem antes disso funciona sem qualquer proxy e, se estiver a aprender a biblioteca, comece por testá-la com a sua própria ligação.

Onde se encontra agora o OkHttp

Vale a pena referir logo de início, porque os links antigos já não funcionam.

O repositório do OkHttp foi transferido. github.com/square/okhttp

redireciona agora para github.com/lysine-dev/okhttp

, e o site de documentação que se encontrava em square.github.io/okhttp

apresenta um erro 404 — o site atual está em lysine.dev/okhttp

. O projeto é mantido ativamente e está licenciado sob a licença Apache-2.0, conforme verificado em setembro de 2026.

As coordenadas do Maven não se alteraram. Continua publicado como com.squareup.okhttp3:okhttp

:

implementation("com.squareup.okhttp3:okhttp:5.5.0")

Existe também uma lista de materiais para manter os artefactos relacionados atualizados:

implementation(platform("com.squareup.okhttp3:okhttp-bom:5.5.0"))

Requisitos: Android 5.0+ (nível de API 21+) e Java 8+. O OkHttp depende do Okio para E/S e da biblioteca padrão do Kotlin — ambas descritas pelo projeto como «pequenas bibliotecas com forte compatibilidade retroativa». No Android, utiliza o AndroidX Startup e, se desativar o inicializador no manifesto, a sua aplicação deve chamar OkHttp.initialize(applicationContext)

em Application.onCreate

.

O antigo ramo 3.12.x

suporta Android 2.3+ e Java 7, e o projeto é claro ao afirmar que essas plataformas «não suportam TLS 1.2 e não devem ser utilizadas».

A sua primeira solicitação

OkHttpClient client = new OkHttpClient();

String run(String url) throws IOException {
  Request request = new Request.Builder()
      .url(url)
      .build();

  try (Response response = client.newCall(request).execute()) {
    return response.body().string();
  }
}

Há três aspetos importantes nesse exemplo de sete linhas.

**try

-with-resources não é opcional.** Response

implementa Closeable

, e uma resposta não encerrada mantém a sua ligação. Se houver fugas suficientes, o conjunto de ligações esgota-se, altura em que as solicitações ficam pendentes em vez de falharem — um sintoma que se assemelha a um problema de rede, mas que não o é.

**body().string()

pode ser chamado uma vez.** Consome o fluxo. Chamá-lo duas vezes provoca um erro, e chamá-lo depois de a resposta ter sido fechada também provoca um erro. Se precisar do corpo mais do que uma vez, guarde a cadeia de caracteres.

**execute()

é síncrono.** Para trabalho assíncrono, enqueue()

aceita um Callback

e é executado no conjunto de threads do despachante do OkHttp.

Um POST tem a mesma estrutura com um corpo:

public static final MediaType JSON = MediaType.get("application/json");

String post(String url, String json) throws IOException {
  RequestBody body = RequestBody.create(json, JSON);
  Request request = new Request.Builder()
      .url(url)
      .post(body)
      .build();
  try (Response response = client.newCall(request).execute()) {
    return response.body().string();
  }
}

Partilhar o Cliente

A decisão de design em que as pessoas mais frequentemente erram.

OkHttpClient

mantém um conjunto de ligações e um conjunto de threads. Criar um por cada pedido desperdiça todas as ligações que poderias ter reutilizado e cria threads que depois abandonas. Funciona, é lento e, sob carga, esgota os recursos.

Cria um único cliente para a tua aplicação e partilha-o. É seguro para threads por definição.

Quando precisar de configurações diferentes para uma parte do seu código, não crie um segundo cliente do zero — clone o já existente para que os conjuntos sejam partilhados:

OkHttpClient shortTimeout = client.newBuilder()
    .readTimeout(5, TimeUnit.SECONDS)
    .build();

newBuilder()

produz um cliente que partilha o conjunto de ligações e o distribuidor com o seu pai, que é exatamente o que pretende.

Para o encerramento, especialmente em processos de curta duração, liberte os recursos explicitamente:

client.dispatcher().executorService().shutdown();
client.connectionPool().evictAll();

Sem isto, uma JVM pode ficar bloqueada durante o período de manutenção da ligação do conjunto antes de terminar, o que é algo complicado de depurar numa ferramenta CLI.

O que o OkHttp acrescenta à sua solicitação

A biblioteca reescreve as solicitações, e saber o que ela acrescenta evita uma série de confusões.

A documentação é explícita: «O OkHttp pode adicionar cabeçalhos que não constam na solicitação original, incluindo Content-Length, Transfer-Encoding, User-Agent, Host, Connection e Content-Type. Adicionará um cabeçalho Accept-Encoding para compressão transparente da resposta, a menos que esse cabeçalho já esteja presente. Se houver cookies, o OkHttp adicionará um cabeçalho Cookie com os mesmos.»

Duas consequências.

A compressão transparente é automática e revertida automaticamente. O OkHttp solicita a compressão, descomprime a resposta e, em seguida, «eliminará os cabeçalhos de resposta correspondentes Content-Encoding e Content-Length, uma vez que estes não se aplicam ao corpo da resposta descomprimida». Portanto, a ausência de Content-Length numa resposta é normal e não um erro — a menos que defina Accept-Encoding manualmente, caso em que a descompressão fica a seu cargo.

Os pedidos condicionais ocorrem automaticamente quando o armazenamento em cache está ativado. O OkHttp adiciona If-Modified-Since e If-None-Match para revalidar entradas de cache desatualizadas.

Por predefinição, também segue redirecionamentos e, num desafio de autorização, «solicitará ao Authenticator (se estiver configurado) que satisfaça o desafio», tentando novamente com as credenciais fornecidas.

A biblioteca descreve-se como «baseada em princípios e evita ser excessivamente configurável, especialmente quando essa configuração se destina a contornar um servidor com erros, testar cenários inválidos ou que contradigam a RFC relevante». Enumera as suas próprias limitações com honestidade — «não permite GET com corpo» e a cache «não é uma interface com implementações alternativas». Se precisar de enviar pedidos deliberadamente inválidos, esta não é a biblioteca adequada para o efeito, e isso é uma escolha de conceção e não uma omissão.

Interceptores

O principal ponto de extensão e a característica que irá definir a forma como utiliza a biblioteca.

class LoggingInterceptor implements Interceptor {
  @Override public Response intercept(Interceptor.Chain chain) throws IOException {
    Request request = chain.request();
    long t1 = System.nanoTime();
    logger.info(String.format("Sending request %s on %s%n%s",
        request.url(), chain.connection(), request.headers()));

    Response response = chain.proceed(request);

    long t2 = System.nanoTime();
    logger.info(String.format("Received response for %s in %.1fms%n%s",
        response.request().url(), (t2 - t1) / 1e6d, response.headers()));
    return response;
  }
}

A documentação salienta que «uma chamada a chain.proceed(request) é uma parte essencial da implementação de cada interceptor. Este método, aparentemente simples, é onde todo o trabalho HTTP ocorre.» E um aviso que vale a pena ter em conta: «se chain.proceed(request) for chamado mais do que uma vez, os corpos das respostas anteriores têm de ser fechados.»

Existem dois tipos, e é importante escolher corretamente. Registe-se com addInterceptor() ou addNetworkInterceptor(). A documentação explica a diferença com precisão.

Interceptores de aplicação:

  • «Não é preciso preocupar-se com respostas intermédias, como redirecionamentos e novas tentativas.»
  • «São sempre invocados uma vez, mesmo que a resposta HTTP seja servida a partir da cache.»
  • «Respeitam a intenção original da aplicação. Não se preocupam com cabeçalhos injetados pelo OkHttp, como If-None-Match.»
  • «Podem interromper o fluxo e não chamar Chain.proceed().»
  • «Podem tentar novamente e efetuar várias chamadas a Chain.proceed().»
  • «Pode ajustar os tempos limite das chamadas utilizando withConnectTimeout, withReadTimeout, withWriteTimeout.»

Interceptores de rede:

  • «Capaz de operar em respostas intermédias, como redirecionamentos e novas tentativas.»
  • «Não é invocado para respostas em cache que contornam a rede.»
  • «Observe os dados tal como serão transmitidos pela rede.»
  • «Acesso ao Connection que transporta o pedido.»

A regra prática: utilize um interceptor de aplicação para tudo o que diga respeito à intenção do seu pedido — adicionar um cabeçalho de autorização, um agente de utilizador, registo ao nível da aplicação. Utilize um interceptor de rede para tudo o que diga respeito ao que realmente atravessa a rede — inspecionar corpos comprimidos, verificar cada salto de redirecionamento, examinar a ligação.

O erro mais comum é registar um interceptor de autenticação como um interceptor de rede, o que faz com que este seja acionado uma vez por cada salto de redirecionamento e possa revelar as suas credenciais a um anfitrião que não pretendia.

Tempos limite

O OkHttp tem valores predefinidos razoáveis e quatro configurações distintas; saber qual delas foi acionada indica onde está o problema.

OkHttpClient client = new OkHttpClient.Builder()
    .connectTimeout(10, TimeUnit.SECONDS)
    .readTimeout(30, TimeUnit.SECONDS)
    .writeTimeout(30, TimeUnit.SECONDS)
    .callTimeout(60, TimeUnit.SECONDS)
    .build();

**connectTimeout

** abrange o estabelecimento da ligação TCP e TLS. **readTimeout

** aplica-se entre leituras individuais, não à resposta na totalidade — por isso, um download lento, mas que avança, nunca o aciona. **writeTimeout

** faz o mesmo para os uploads. **callTimeout

** limita toda a chamada, incluindo redirecionamentos, novas tentativas e transferência do corpo da mensagem. Por predefinição, é zero, o que significa que não há limite global.

É este último que deve ser definido. Sem ele, uma chamada que continua a receber dados aos poucos pode decorrer indefinidamente, porque o tempo limite de leitura é reiniciado a cada byte recebido. callTimeout

é o limite máximo que faz com que uma tarefa termine, e é o equivalente a --max-time

no curl — uma distinção que abordámos em definir um tempo limite com o curl.

As substituições por chamada estão disponíveis através de ``withReadTimeout`

` e métodos semelhantes de um interceptor de aplicação, sendo esta a forma de conceder mais margem a um ponto final lento sem flexibilizar os valores predefinidos para tudo.

Proxies

É aqui que o OkHttp difere da maioria dos clientes e onde as pessoas perdem tempo.

Proxy proxy = new Proxy(Proxy.Type.HTTP,
    new InetSocketAddress("proxy.example.com", 9000));

OkHttpClient client = new OkHttpClient.Builder()
    .proxy(proxy)
    .build();

Para SOCKS, utilize Proxy.Type.SOCKS com o mesmo formato.

A autenticação é a parte que surpreende as pessoas. Não é necessário definir um cabeçalho Proxy-Authorization. Deve fornecer um proxyAuthenticator, que o OkHttp invoca quando o proxy emite um desafio 407:

Authenticator proxyAuth = (route, response) -> {
  if (response.request().header("Proxy-Authorization") != null) {
    return null;   // already tried these credentials; give up
  }
  String credential = Credentials.basic("user", "pass");
  return response.request().newBuilder()
      .header("Proxy-Authorization", credential)
      .build();
};

OkHttpClient client = new OkHttpClient.Builder()
    .proxy(proxy)
    .proxyAuthenticator(proxyAuth)
    .build();

O retorno nulo é essencial. Sem ele, uma palavra-passe errada gera um ciclo infinito de tentativas em vez de uma falha — o OkHttp consulta o autenticador, recebe a mesma credencial incorreta, recebe outro 407 e volta a perguntar. Verificar se já enviou um cabeçalho Proxy-Authorization é a forma de quebrar o ciclo.

Três notas adicionais.

Um 407 não é um 401. O proxy recusou o acesso e nunca chegou ao destino. Um 401 significa que o proxy funcionou e que o destino solicita credenciais, o que é uma situação de authenticator() completamente diferente.

proxySelector() permite-lhe escolher um proxy por pedido, em vez de por cliente, o que é a forma de encaminhar diferentes hosts de forma distinta sem ter de criar vários clientes.

Verifique se a alteração surtiu efeito. Solicite um serviço que devolva o seu endereço, com e sem o proxy configurado. Uma configuração incorreta neste caso passa despercebida — os pedidos são bem-sucedidos e seguem diretamente — e confirmar o endereço de saída é a única forma de ter a certeza. Esse padrão de «sucesso silencioso» é aquele sobre o qual escrevemos em por que é importante testar proxies.

Tratar as respostas de forma adequada

Alguns padrões que fazem a diferença entre código que funciona e código que funciona sob carga.

**Verifique se há um erro de rede (isSuccessful()

), e não apenas a ausência de uma exceção.** O OkHttp lança um erro de rede (IOException

) em caso de falhas de rede, e não em caso de códigos de erro HTTP. Um 404 ou um 500 surge como um erro de rede comum (Response

):

try (Response response = client.newCall(request).execute()) {
  if (!response.isSuccessful()) {
    String body = response.body() != null ? response.body().string() : "";
    throw new IOException("HTTP " + response.code() + " from " + request.url()
                          + ": " + body.substring(0, Math.min(200, body.length())));
  }
  return response.body().string();
}

Incluir a primeira parte do corpo do erro transforma um código de estado opaco numa mensagem que identifica o problema — a maioria das APIs explica-se no corpo de um 400.

Não armazene respostas grandes numa String. body().string()

lê tudo para a memória. Para um download grande, utilize streaming:

try (Response response = client.newCall(request).execute();
     BufferedSource source = response.body().source();
     BufferedSink sink = Okio.buffer(Okio.sink(new File("out.bin")))) {
  sink.writeAll(source);
}

As chamadas assíncronas continuam a necessitar que o corpo seja fechado, e o callback é executado numa thread em segundo plano:

client.newCall(request).enqueue(new Callback() {
  @Override public void onFailure(Call call, IOException e) {
    logger.warn("request failed", e);
  }
  @Override public void onResponse(Call call, Response response) throws IOException {
    try (ResponseBody body = response.body()) {
      handle(body.string());
    }
  }
});

Note-se que onFailure

é disparado apenas em caso de problemas de rede. Um erro HTTP 500 surge em onResponse

, o que surpreende quem espera que a nomenclatura signifique exatamente o que parece.

As tentativas de repetição requerem cuidado. O OkHttp repete automaticamente algumas falhas ao nível da ligação, controladas por retryOnConnectionFailure()

, que está ativado por predefinição. Não repete tentativas em caso de códigos de erro HTTP, e nem deve fazê-lo — repetir uma chamada POST que possa ter sido bem-sucedida pode duplicar uma gravação. Quando adicionar a sua própria lógica de repetição de tentativas, faça-o num interceptor da aplicação, limite o número de tentativas, deixe um intervalo entre elas e restrinja-a a métodos idempotentes, a menos que a API ofereça uma chave de idempotência.

O que mais lhe oferece

Resumidamente, uma vez que estas são as razões para o escolher.

HTTP/2, em que «o suporte permite que todos os pedidos para o mesmo anfitrião partilhem um socket». Pool de ligações, que «reduz a latência das solicitações (caso o HTTP/2 não esteja disponível)». GZIP transparente. Armazenamento em cache de respostas, que «evita completamente a rede para solicitações repetidas».

Resiliência. «Recupera silenciosamente de problemas comuns de ligação» e, quando um serviço tem vários endereços, «tenta endereços alternativos se a primeira ligação falhar» — o que, segundo o projeto, «é necessário para IPv4+IPv6 e serviços alojados em centros de dados redundantes».

TLS moderno, incluindo TLS 1.3, ALPN e fixação de certificados, utilizando a implementação da plataforma. Na JVM, também suporta o Conscrypt, que integra o BoringSSL com o Java, utilizado automaticamente se for o primeiro fornecedor de segurança.

Conformidade com normas. O projeto enumera as especificações que segue: RFC 9110 para semântica HTTP, RFC 9111 para armazenamento em cache, RFC 9112 para HTTP/1.1, RFC 9113 para HTTP/2, RFC 6455 para WebSockets e a especificação WHATWG para eventos enviados pelo servidor. Quando uma especificação é ambígua, «segue os agentes de utilizador modernos, tais como navegadores populares ou bibliotecas HTTP comuns».

Perguntas frequentes

O OkHttp ainda é mantido?

Sim. O repositório mudou de square/okhttp para lysine-dev/okhttp e o site da documentação está agora em lysine.dev/okhttp, mas o projeto continua a ser desenvolvido ativamente e está licenciado sob a licença Apache-2.0. As coordenadas do Maven continuam a ser com.squareup.okhttp3:okhttp.

Devo criar um novo OkHttpClient para cada pedido?

Não. O cliente mantém um conjunto de ligações e um conjunto de threads, e é seguro para threads por definição. Crie um para a sua aplicação e partilhe-o. Quando precisar de configurações diferentes, utilize newBuilder() no cliente existente para que os conjuntos sejam partilhados.

Por que tenho de fechar a resposta?

Porque Response mantém uma ligação até ser fechada, e as respostas que ficam em aberto esgotam o conjunto de ligações. O sintoma é que as solicitações ficam pendentes em vez de falharem, o que é difícil de diagnosticar. Utilize sempre try-with-resources.

Qual é a diferença entre um interceptor de aplicação e um interceptor de rede?

Um interceptor de aplicação vê o seu pedido original e é invocado exatamente uma vez, mesmo para respostas em cache, podendo interromper o processo ou tentar novamente. Um interceptor de rede vê cada troca de rede individual, incluindo redirecionamentos e novas tentativas, tem acesso à ligação e é totalmente ignorado no caso de respostas em cache.

Como configuro um proxy no OkHttp?

Passe um objeto java.net.Proxy para OkHttpClient.Builder.proxy(). Para autenticação, defina um proxyAuthenticator em vez de um cabeçalho — e devolva null quando um cabeçalho Proxy-Authorization já estiver presente, caso contrário, credenciais incorretas podem provocar um ciclo infinito de tentativas.

Que tempos de espera devo definir?

O «connectTimeout» (tempo de espera de leitura) em cerca de 10 segundos, o «readTimeout» (tempo de espera de resposta) e o «writeTimeout» (tempo de espera de conexão) em cerca de 30, e — mais importante ainda — o «callTimeout» (tempo de espera de chamada), cujo valor predefinido é zero e que é a única configuração que limita toda a chamada. Sem ele, uma resposta que chega lentamente nunca atinge o tempo de espera, porque o tempo de espera de leitura é reiniciado a cada byte.

O OkHttp lida com GZIP automaticamente?

Sim, se não definir manualmente Accept-Encoding. Ele solicita a compressão, descomprime a resposta e descarta Content-Encoding e Content-Length, uma vez que estes já não descrevem o corpo descomprimido. Se definir o cabeçalho manualmente, fica também responsável pela descompressão.

Que versão do Java é necessária para o OkHttp?

Java 8 ou posterior e Android 5.0 (nível de API 21) ou posterior. Depende do Okio e da biblioteca padrão do Kotlin. O antigo ramo 3.12.x suporta Java 7 e Android 2.3, mas não suporta TLS 1.2 e não deve ser utilizado.

Conclusão

O OkHttp é uma API compacta baseada numa implementação bem concebida, e há dois hábitos que abrangem a maior parte da sua utilização correta: partilhar um único cliente em toda a aplicação e encerrar todas as respostas com ``try-with-resources. Ambas as falhas ocorrem de forma silenciosa e acabam por se manifestar como pedidos que ficam bloqueados, em vez de erros que se possam ler.

É nos interceptores que irá dedicar a maior parte do seu tempo, e vale a pena aprender bem a distinção entre «aplicação» e «rede», em vez de aprender por tentativa e erro. Os interceptores de aplicação detetam a sua intenção e são executados uma única vez; os interceptores de rede detetam cada salto e são ignorados no caso de respostas em cache. Registar um interceptor de autorização na camada de rede é o erro clássico, e este é acionado em cada redirecionamento.

Defina um callTimeout. O valor predefinido é zero; é a única configuração que delimita uma chamada na íntegra, e a sua ausência é a razão pela qual uma tarefa que deveria demorar segundos, ocasionalmente, continua a executar-se até que outra coisa a interrompa.

E se estiver a seguir documentação mais antiga, verifique os links. O projeto mudou-se para lysine.dev/okhttp e o site hospedado pela Square já não existe — embora as coordenadas do artefacto permaneçam inalteradas, pelo que o seu ficheiro de compilação não necessita de quaisquer alterações.