OkHttp #

一、OkHttp概述 #

OkHttp是Square公司开源的高性能HTTP客户端,是Android开发中最常用的网络库之一。

1.1 OkHttp特点 #

  • 支持HTTP/2
  • 连接池减少延迟
  • 透明的GZIP压缩
  • 响应缓存
  • 拦截器机制
  • 支持WebSocket

1.2 添加依赖 #

kotlin
dependencies {
    implementation("com.squareup.okhttp3:okhttp:4.12.0")
}

二、基本使用 #

2.1 创建OkHttpClient #

kotlin
// 简单创建
val client = OkHttpClient()

// 自定义配置
val client = OkHttpClient.Builder()
    .connectTimeout(30, TimeUnit.SECONDS)
    .readTimeout(30, TimeUnit.SECONDS)
    .writeTimeout(30, TimeUnit.SECONDS)
    .retryOnConnectionFailure(true)
    .build()

2.2 GET请求 #

kotlin
suspend fun get(url: String): String {
    val request = Request.Builder()
        .url(url)
        .get()
        .build()
    
    client.newCall(request).execute().use { response ->
        if (!response.isSuccessful) {
            throw IOException("Unexpected code $response")
        }
        return response.body?.string() ?: ""
    }
}

// 异步请求
fun getAsync(url: String, callback: Callback) {
    val request = Request.Builder()
        .url(url)
        .get()
        .build()
    
    client.newCall(request).enqueue(callback)
}

2.3 POST请求 #

kotlin
// JSON请求
suspend fun postJson(url: String, json: String): String {
    val mediaType = "application/json; charset=utf-8".toMediaType()
    val body = json.toRequestBody(mediaType)
    
    val request = Request.Builder()
        .url(url)
        .post(body)
        .build()
    
    client.newCall(request).execute().use { response ->
        return response.body?.string() ?: ""
    }
}

// 表单请求
suspend fun postForm(url: String, params: Map<String, String>): String {
    val formBody = FormBody.Builder().apply {
        params.forEach { (key, value) ->
            add(key, value)
        }
    }.build()
    
    val request = Request.Builder()
        .url(url)
        .post(formBody)
        .build()
    
    client.newCall(request).execute().use { response ->
        return response.body?.string() ?: ""
    }
}

// Multipart上传文件
suspend fun uploadFile(url: String, file: File): String {
    val mediaType = "image/jpeg".toMediaType()
    val requestBody = MultipartBody.Builder()
        .setType(MultipartBody.FORM)
        .addFormDataPart("file", file.name, file.asRequestBody(mediaType))
        .addFormDataPart("description", "File upload")
        .build()
    
    val request = Request.Builder()
        .url(url)
        .post(requestBody)
        .build()
    
    client.newCall(request).execute().use { response ->
        return response.body?.string() ?: ""
    }
}

2.4 添加请求头 #

kotlin
val request = Request.Builder()
    .url(url)
    .addHeader("Authorization", "Bearer $token")
    .addHeader("Content-Type", "application/json")
    .header("User-Agent", "MyApp/1.0")  // 覆盖同名header
    .build()

三、拦截器 #

3.1 日志拦截器 #

kotlin
val loggingInterceptor = HttpLoggingInterceptor().apply {
    level = HttpLoggingInterceptor.Level.BODY
}

val client = OkHttpClient.Builder()
    .addInterceptor(loggingInterceptor)
    .build()

3.2 自定义拦截器 #

kotlin
class AuthInterceptor(private val token: String) : Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        val request = chain.request().newBuilder()
            .addHeader("Authorization", "Bearer $token")
            .build()
        return chain.proceed(request)
    }
}

val client = OkHttpClient.Builder()
    .addInterceptor(AuthInterceptor("your_token"))
    .build()

3.3 响应拦截器 #

kotlin
class ResponseInterceptor : Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        val response = chain.proceed(chain.request())
        
        // 检查token是否过期
        if (response.code == 401) {
            // 刷新token并重试
            val newToken = refreshToken()
            val newRequest = chain.request().newBuilder()
                .header("Authorization", "Bearer $newToken")
                .build()
            return chain.proceed(newRequest)
        }
        
        return response
    }
}

3.4 缓存拦截器 #

kotlin
class CacheInterceptor : Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        val request = chain.request()
        
        val newRequest = request.newBuilder()
            .header("Cache-Control", "public, max-age=60")
            .build()
        
        return chain.proceed(newRequest)
    }
}

3.5 网络拦截器 vs 应用拦截器 #

kotlin
val client = OkHttpClient.Builder()
    .addInterceptor(loggingInterceptor)        // 应用拦截器
    .addNetworkInterceptor(networkInterceptor) // 网络拦截器
    .build()
特性 应用拦截器 网络拦截器
调用次数 一次 可能多次(重定向、重试)
缓存 不触发缓存 触发缓存
网络状态 不关心 可检查网络状态

四、缓存 #

4.1 配置缓存 #

kotlin
val cacheSize = 10 * 1024 * 1024L  // 10MB
val cacheDir = File(context.cacheDir, "http_cache")
val cache = Cache(cacheDir, cacheSize)

val client = OkHttpClient.Builder()
    .cache(cache)
    .build()

4.2 缓存策略 #

kotlin
// 强制使用缓存
val request = Request.Builder()
    .url(url)
    .cacheControl(CacheControl.FORCE_CACHE)
    .build()

// 强制使用网络
val request = Request.Builder()
    .url(url)
    .cacheControl(CacheControl.FORCE_NETWORK)
    .build()

// 自定义缓存策略
val cacheControl = CacheControl.Builder()
    .maxAge(10, TimeUnit.MINUTES)
    .build()

val request = Request.Builder()
    .url(url)
    .cacheControl(cacheControl)
    .build()

五、Cookie管理 #

5.1 持久化Cookie #

kotlin
val cookieJar = PersistentCookieJar(SetCookieCache(), SharedPrefsCookiePersistor(context))

val client = OkHttpClient.Builder()
    .cookieJar(cookieJar)
    .build()

5.2 自定义CookieJar #

kotlin
class MyCookieJar : CookieJar {
    private val cookieStore = mutableMapOf<String, List<Cookie>>()
    
    override fun saveFromResponse(url: HttpUrl, cookies: List<Cookie>) {
        cookieStore[url.host] = cookies
    }
    
    override fun loadForRequest(url: HttpUrl): List<Cookie> {
        return cookieStore[url.host] ?: emptyList()
    }
}

六、WebSocket #

6.1 创建WebSocket #

kotlin
val client = OkHttpClient()

val request = Request.Builder()
    .url("wss://example.com/ws")
    .build()

val webSocket = client.newWebSocket(request, object : WebSocketListener() {
    override fun onOpen(webSocket: WebSocket, response: Response) {
        Log.d("WebSocket", "Connected")
    }
    
    override fun onMessage(webSocket: WebSocket, text: String) {
        Log.d("WebSocket", "Received: $text")
    }
    
    override fun onClosing(webSocket: WebSocket, code: Int, reason: String) {
        webSocket.close(1000, null)
        Log.d("WebSocket", "Closing: $code / $reason")
    }
    
    override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) {
        Log.e("WebSocket", "Error", t)
    }
})

// 发送消息
webSocket.send("Hello")

// 关闭连接
webSocket.close(1000, "Goodbye")

七、封装工具类 #

kotlin
object OkHttpUtils {
    
    private val client: OkHttpClient by lazy {
        OkHttpClient.Builder()
            .connectTimeout(30, TimeUnit.SECONDS)
            .readTimeout(30, TimeUnit.SECONDS)
            .writeTimeout(30, TimeUnit.SECONDS)
            .addInterceptor(HttpLoggingInterceptor().apply {
                level = HttpLoggingInterceptor.Level.BODY
            })
            .build()
    }
    
    suspend fun get(url: String, headers: Map<String, String> = emptyMap()): Result<String> {
        return withContext(Dispatchers.IO) {
            try {
                val request = Request.Builder()
                    .url(url)
                    .apply {
                        headers.forEach { (key, value) ->
                            addHeader(key, value)
                        }
                    }
                    .get()
                    .build()
                
                client.newCall(request).execute().use { response ->
                    if (response.isSuccessful) {
                        Result.success(response.body?.string() ?: "")
                    } else {
                        Result.failure(Exception("HTTP ${response.code}"))
                    }
                }
            } catch (e: Exception) {
                Result.failure(e)
            }
        }
    }
    
    suspend fun post(url: String, json: String, headers: Map<String, String> = emptyMap()): Result<String> {
        return withContext(Dispatchers.IO) {
            try {
                val mediaType = "application/json; charset=utf-8".toMediaType()
                val body = json.toRequestBody(mediaType)
                
                val request = Request.Builder()
                    .url(url)
                    .apply {
                        headers.forEach { (key, value) ->
                            addHeader(key, value)
                        }
                    }
                    .post(body)
                    .build()
                
                client.newCall(request).execute().use { response ->
                    if (response.isSuccessful) {
                        Result.success(response.body?.string() ?: "")
                    } else {
                        Result.failure(Exception("HTTP ${response.code}"))
                    }
                }
            } catch (e: Exception) {
                Result.failure(e)
            }
        }
    }
}

八、最佳实践 #

8.1 单例OkHttpClient #

kotlin
object HttpClient {
    val client: OkHttpClient = OkHttpClient.Builder()
        .connectTimeout(30, TimeUnit.SECONDS)
        .build()
}

8.2 使用协程扩展 #

kotlin
suspend fun Call.await(): Response {
    return suspendCancellableCoroutine { continuation ->
        enqueue(object : Callback {
            override fun onFailure(call: Call, e: IOException) {
                continuation.resumeWithException(e)
            }
            
            override fun onResponse(call: Call, response: Response) {
                continuation.resume(response)
            }
        })
        
        continuation.invokeOnCancellation {
            cancel()
        }
    }
}

// 使用
val response = client.newCall(request).await()

8.3 错误处理 #

kotlin
sealed class NetworkResult<out T> {
    data class Success<T>(val data: T) : NetworkResult<T>()
    data class Error(val code: Int, val message: String) : NetworkResult<Nothing>()
    data class Exception(val e: Throwable) : NetworkResult<Nothing>()
}

suspend fun <T> safeApiCall(block: suspend () -> T): NetworkResult<T> {
    return try {
        NetworkResult.Success(block())
    } catch (e: IOException) {
        NetworkResult.Exception(e)
    } catch (e: HttpException) {
        NetworkResult.Error(e.code(), e.message())
    }
}

九、总结 #

本章详细介绍了OkHttp:

  1. OkHttp的基本使用
  2. GET、POST请求
  3. 拦截器机制
  4. 缓存配置
  5. Cookie管理
  6. WebSocket支持
  7. 工具类封装
  8. 最佳实践

OkHttp是Android网络请求的基础库,配合Retrofit使用可以大大提高开发效率。

最后更新:2026-03-26