Что такое OkHttp и зачем он нужен?
OkHttp — это мощная и эффективная HTTP-библиотека для JVM (Java Virtual Machine), Android и Kotlin/Native, разработанная компанией Square. Она предоставляет удобный API для выполнения HTTP-запросов и обработки ответов. OkHttp является де-факто стандартом для работы с сетью в экосистеме Android и активно используется в серверных Kotlin-приложениях.
Зачем использовать OkHttp вместо стандартных средств Java (HttpURLConnection)?
- Простота и удобство: Чистый, текучий (fluent) API, который легко читать и писать.
- Производительность: Встроенная поддержка HTTP/2, автоматическое сжатие ответов (gzip), пул соединений (connection pooling) и кэширование.
- Надёжность: Автоматические повторные запросы при сбоях сети, устойчивость к ошибкам DNS и TLS.
- Гибкость: Легко настраиваемые перехватчики (interceptors) для логирования, аутентификации, модификации запросов и ответов.
- Тестируемость: Возможность подменять сетевой слой с помощью MockWebServer.
OkHttp идеально подходит для Android-приложений, REST API-клиентов, микросервисов и любых других проектов на Kotlin, где требуется надёжная и быстрая работа с HTTP.
Установка
OkHttp распространяется через Maven Central. Для добавления в проект используйте систему сборки Gradle (наиболее распространённая для Kotlin).
Для Android / Kotlin/JVM проекта (build.gradle.kts):
dependencies {
implementation("com.squareup.okhttp3:okhttp:4.12.0")
// Для логирования (опционально)
implementation("com.squareup.okhttp3:logging-interceptor:4.12.0")
}
Для Android / Kotlin/JVM проекта (build.gradle):
dependencies {
implementation 'com.squareup.okhttp3:okhttp:4.12.0'
implementation 'com.squareup.okhttp3:logging-interceptor:4.12.0'
}
Для Maven (pom.xml):
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.12.0</version>
</dependency>
Важно: Убедитесь, что версия OkHttp совместима с вашей версией Kotlin. Актуальную версию всегда можно найти на MVNRepository.
Быстрый старт
Самый простой пример — выполнить GET-запрос и получить тело ответа в виде строки.
import okhttp3.OkHttpClient
import okhttp3.Request
import java.io.IOException
fun main() {
// 1. Создаём клиент (обычно один на всё приложение)
val client = OkHttpClient()
// 2. Строим запрос
val request = Request.Builder()
.url("https://api.github.com/users/octocat")
.build()
// 3. Выполняем запрос синхронно (блокирует текущий поток)
client.newCall(request).execute().use { response ->
if (!response.isSuccessful) {
throw IOException("Unexpected code $response")
}
// 4. Получаем тело ответа
println(response.body?.string())
}
}
Объяснение:
OkHttpClient()— создаёт экземпляр клиента. Он потокобезопасен, поэтому рекомендуется создать один экземпляр и переиспользовать его.Request.Builder()— использует шаблон "Строитель" (Builder) для конфигурации запроса.client.newCall(request)— создаёт новый вызов (Call), который можно выполнить синхронно (execute()) или асинхронно (enqueue()).response.use { ... }— гарантирует, что ресурсы ответа (тело, соединение) будут закрыты после использования.response.body?.string()— читает тело ответа как строку. Важно: Тело можно прочитать только один раз!
Основные методы и функции
Рассмотрим ключевые методы OkHttp с подробными примерами.
1. OkHttpClient.Builder.build()
Что делает: Создаёт настроенный экземпляр OkHttpClient. Позволяет задать таймауты, перехватчики, кэш, прокси и другие параметры.
Параметры: Методы билдера (не все):
connectTimeout(Long, TimeUnit)— таймаут подключения.readTimeout(Long, TimeUnit)— таймаут чтения данных.writeTimeout(Long, TimeUnit)— таймаут записи данных.addInterceptor(Interceptor)— добавляет перехватчик приложения.addNetworkInterceptor(Interceptor)— добавляет сетевой перехватчик.cache(Cache)— устанавливает кэш ответов.proxy(Proxy)— настраивает прокси-сервер.
Пример:
val client = OkHttpClient.Builder()
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS)
.addInterceptor(HttpLoggingInterceptor().apply {
level = HttpLoggingInterceptor.Level.BODY
})
.build()
2. Request.Builder.url(String)
Что делает: Устанавливает URL для запроса. Может принимать строку или объект HttpUrl.
Параметры:
url: String— URL в виде строки.url: HttpUrl— предварительно разобранный URL (позволяет безопасно добавлять параметры).
Пример:
// Строковый URL
val request1 = Request.Builder()
.url("https://api.example.com/data")
.build()
// Безопасный HttpUrl
val url = HttpUrl.Builder()
.scheme("https")
.host("api.example.com")
.addPathSegment("data")
.addQueryParameter("page", "1")
.build()
val request2 = Request.Builder()
.url(url)
.build()
3. Request.Builder.header(String, String) и addHeader(String, String)
Что делает: Добавляет HTTP-заголовки к запросу. header() заменяет существующий заголовок, addHeader() добавляет новый, не удаляя старые.
Параметры:
name: String— имя заголовка.value: String— значение заголовка.
Пример:
val request = Request.Builder()
.url("https://api.example.com/secure")
.header("Authorization", "Bearer token123")
.addHeader("Accept", "application/json")
.addHeader("User-Agent", "MyKotlinApp/1.0")
.build()
4. Request.Builder.method(String, RequestBody?)
Что делает: Устанавливает HTTP-метод (GET, POST, PUT, DELETE, PATCH и т.д.) и тело запроса. Для методов без тела (GET, DELETE) тело должно быть null.
Параметры:
method: String— HTTP метод.body: RequestBody?— тело запроса (может быть null).
Пример:
// POST с JSON телом
val json = MediaType.parse("application/json; charset=utf-8")
val body = RequestBody.create(json, """{"key": "value"}""")
val request = Request.Builder()
.url("https://api.example.com/data")
.method("POST", body)
.build()
// GET запрос (тело не требуется)
val getRequest = Request.Builder()
.url("https://api.example.com/data")
.method("GET", null)
.build()
5. Call.execute()
Что делает: Выполняет запрос синхронно. Блокирует текущий поток до получения ответа. Возвращает Response.
Параметры: Нет.
Возвращает: Response.
Исключения: IOException при сетевых ошибках.
Пример:
val client = OkHttpClient()
val request = Request.Builder().url("https://httpbin.org/get").build()
try {
val response = client.newCall(request).execute()
if (response.isSuccessful) {
println(response.body?.string())
}
} catch (e: IOException) {
e.printStackTrace()
}
6. Call.enqueue(Callback)
Что делает: Выполняет запрос асинхронно в фоновом потоке. Не блокирует вызывающий поток. Результат приходит в колбэк.
Параметры:
callback: Callback— объект с двумя методами:onFailure(Call, IOException)иonResponse(Call, Response).
Пример:
val client = OkHttpClient()
val request = Request.Builder().url("https://httpbin.org/delay/2").build()
client.newCall(request).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
println("Ошибка: ${e.message}")
}
override fun onResponse(call: Call, response: Response) {
response.use {
if (response.isSuccessful) {
println("Успех: ${response.body?.string()}")
}
}
}
})
// Не блокирует главный поток
println("Запрос отправлен асинхронно")
7. Response.isSuccessful
Что делает: Проверяет, является ли код ответа успешным (в диапазоне 200-299). Удобная альтернатива проверке response.code() in 200..299.
Параметры: Нет (свойство).
Возвращает: Boolean.
Пример:
val response = client.newCall(request).execute()
if (response.isSuccessful) {
// Обработка успешного ответа
} else {
println("Ошибка: ${response.code()} - ${response.message()}")
}
8. Response.body() и ResponseBody.string()
Что делает: response.body() возвращает объект ResponseBody, содержащий тело ответа. ResponseBody.string() читает тело как строку. Важно: Тело можно прочитать только один раз, после чего оно будет закрыто.
Параметры: Нет.
Возвращает: ResponseBody? (может быть null для ответов без тела, например 204 No Content).
Пример:
val response = client.newCall(request).execute()
val body = response.body
// Чтение как строка
val bodyString = body?.string()
println(bodyString)
// Чтение как байты
val bytes = body?.bytes()
// Чтение как поток
val inputStream = body?.byteStream()
inputStream?.use { stream ->
// Чтение потока
}
9. ResponseBody.contentType()
Что делает: Возвращает MediaType