okhttp使用指南
欢迎访问我的主页: https://heeheeaii.github.io/
1. 基础配置
依赖添加
// Gradle (build.gradle.kts)
implementation("com.squareup.okhttp3:okhttp:4.11.0")
implementation("com.squareup.okhttp3:logging-interceptor:4.11.0")
单例客户端配置
object HttpClient {val client: OkHttpClient by lazy {OkHttpClient.Builder().connectTimeout(10, TimeUnit.SECONDS).readTimeout(30, TimeUnit.SECONDS).writeTimeout(30, TimeUnit.SECONDS).addInterceptor(LoggingInterceptor()).addInterceptor(AuthInterceptor()).build()}
}
2. 基本请求方法
GET 请求
// 同步 GET
fun getUser(userId: String): String? {val request = Request.Builder().url("https://api.example.com/users/$userId").build()return try {HttpClient.client.newCall(request).execute().use { response ->if (response.isSuccessful) {response.body?.string()} else {throw Exception("HTTP ${response.code}: ${response.message}")}}} catch (e: Exception) {println("请求失败: ${e.message}")null}
}// 异步 GET
fun getUserAsync(userId: String, callback: (String?) -> Unit) {val request = Request.Builder().url("https://api.example.com/users/$userId").build()HttpClient.client.newCall(request).enqueue(object : Callback {override fun onFailure(call: Call, e: IOException) {callback(null)}override fun onResponse(call: Call, response: Response) {response.use {callback(if (it.isSuccessful) it.body?.string() else null)}}})
}
POST JSON 请求
fun createUser(userData: String): String? {val requestBody = userData.toRequestBody("application/json".toMediaType())val request = Request.Builder().url("https://api.example.com/users").post(requestBody).build()return HttpClient.client.newCall(request).execute().use { response ->if (response.isSuccessful) {response.body?.string()} else {throw Exception("创建失败: ${response.code}")}}
}
POST 表单请求
fun login(username: String, password: String): String? {val formBody = FormBody.Builder().add("username", username).add("password", password).build()val request = Request.Builder().url("https://api.example.com/login").post(formBody).build()return HttpClient.client.newCall(request).execute().use { response ->response.body?.string()}
}
3. 文件操作
文件上传
fun uploadFile(file: File, description: String): String? {val requestBody = MultipartBody.Builder().setType(MultipartBody.FORM).addFormDataPart("description", description).addFormDataPart("file", file.name, file.asRequestBody("application/octet-stream".toMediaType())).build()val request = Request.Builder().url("https://api.example.com/upload").post(requestBody).build()return HttpClient.client.newCall(request).execute().use { response ->response.body?.string()}
}
文件下载
fun downloadFile(url: String, outputFile: File): Boolean {val request = Request.Builder().url(url).build()return try {HttpClient.client.newCall(request).execute().use { response ->if (response.isSuccessful) {response.body?.byteStream()?.use { inputStream ->outputFile.outputStream().use { outputStream ->inputStream.copyTo(outputStream)}}true} else {false}}} catch (e: Exception) {false}
}// 带进度的下载
fun downloadWithProgress(url: String, outputFile: File, onProgress: (progress: Int) -> Unit): Boolean {val request = Request.Builder().url(url).build()return try {HttpClient.client.newCall(request).execute().use { response ->if (response.isSuccessful) {val body = response.body ?: return falseval contentLength = body.contentLength()body.byteStream().use { inputStream ->outputFile.outputStream().use { outputStream ->val buffer = ByteArray(8192)var downloaded = 0Lvar bytesRead: Intwhile (inputStream.read(buffer).also { bytesRead = it } != -1) {outputStream.write(buffer, 0, bytesRead)downloaded += bytesReadif (contentLength > 0) {val progress = (downloaded * 100 / contentLength).toInt()onProgress(progress)}}}}true} else {false}}} catch (e: Exception) {false}
}
4. 实用拦截器
日志拦截器
class LoggingInterceptor : Interceptor {override fun intercept(chain: Interceptor.Chain): Response {val request = chain.request()val startTime = System.nanoTime()println("发送请求: ${request.method} ${request.url}")val response = chain.proceed(request)val endTime = System.nanoTime()println("收到响应: ${response.code} in ${(endTime - startTime) / 1e6}ms")return response}
}
认证拦截器
class AuthInterceptor(private val token: String) : Interceptor {override fun intercept(chain: Interceptor.Chain): Response {val originalRequest = chain.request()val authenticatedRequest = originalRequest.newBuilder().header("Authorization", "Bearer $token").build()return chain.proceed(authenticatedRequest)}
}
重试拦截器
class RetryInterceptor(private val maxRetries: Int = 3) : Interceptor {override fun intercept(chain: Interceptor.Chain): Response {var request = chain.request()var response = chain.proceed(request)var retryCount = 0while (!response.isSuccessful && retryCount < maxRetries) {retryCount++println("重试第 $retryCount 次: ${request.url}")response.close()response = chain.proceed(request)}return response}
}
缓存拦截器
class CacheInterceptor(private val maxAge: Int = 60) : Interceptor {override fun intercept(chain: Interceptor.Chain): Response {val request = chain.request()val response = chain.proceed(request)return response.newBuilder().header("Cache-Control", "public, max-age=$maxAge").build()}
}
5. 高级配置
缓存设置
val cacheSize = 10 * 1024 * 1024L // 10MB
val cache = Cache(File(context.cacheDir, "http-cache"), cacheSize)val clientWithCache = OkHttpClient.Builder().cache(cache).addNetworkInterceptor(CacheInterceptor()).build()
HTTPS 配置
// 信任所有证书(仅用于开发环境)
fun createUnsafeClient(): OkHttpClient {val trustAllCerts = arrayOf<TrustManager>(object : X509TrustManager {override fun checkClientTrusted(chain: Array<X509Certificate>, authType: String) {}override fun checkServerTrusted(chain: Array<X509Certificate>, authType: String) {}override fun getAcceptedIssuers(): Array<X509Certificate> = arrayOf()})val sslContext = SSLContext.getInstance("SSL")sslContext.init(null, trustAllCerts, SecureRandom())return OkHttpClient.Builder().sslSocketFactory(sslContext.socketFactory, trustAllCerts[0] as X509TrustManager).hostnameVerifier { _, _ -> true }.build()
}// 证书锁定(生产环境推荐)
val clientWithPinning = OkHttpClient.Builder().certificatePinner(CertificatePinner.Builder().add("api.example.com", "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=").build()).build()
6. 工具类封装
HTTP 工具类
class HttpUtil private constructor() {companion object {private val client = OkHttpClient.Builder().connectTimeout(10, TimeUnit.SECONDS).readTimeout(30, TimeUnit.SECONDS).addInterceptor(HttpLoggingInterceptor().apply { level = HttpLoggingInterceptor.Level.BODY }).build()// GET 请求fun get(url: String, headers: Map<String, String> = emptyMap()): String? {val requestBuilder = Request.Builder().url(url)headers.forEach { (key, value) -> requestBuilder.addHeader(key, value) }return try {client.newCall(requestBuilder.build()).execute().use { response ->if (response.isSuccessful) response.body?.string() else null}} catch (e: Exception) {null}}// POST JSONfun postJson(url: String, json: String, headers: Map<String, String> = emptyMap()): String? {val requestBody = json.toRequestBody("application/json".toMediaType())val requestBuilder = Request.Builder().url(url).post(requestBody)headers.forEach { (key, value) -> requestBuilder.addHeader(key, value) }return try {client.newCall(requestBuilder.build()).execute().use { response ->if (response.isSuccessful) response.body?.string() else null}} catch (e: Exception) {null}}// 异步请求fun getAsync(url: String, callback: (String?) -> Unit) {val request = Request.Builder().url(url).build()client.newCall(request).enqueue(object : Callback {override fun onFailure(call: Call, e: IOException) {callback(null)}override fun onResponse(call: Call, response: Response) {response.use { callback(if (it.isSuccessful) it.body?.string() else null) }}})}}
}
API 响应包装类
data class ApiResponse<T>(val code: Int,val message: String,val data: T?
) {val isSuccess: Boolean get() = code == 200
}// 使用示例
inline fun <reified T> parseResponse(json: String): ApiResponse<T>? {return try {Gson().fromJson(json, object : TypeToken<ApiResponse<T>>() {}.type)} catch (e: Exception) {null}
}
7. 实际使用示例
用户服务示例
class UserService {private val baseUrl = "https://api.example.com"fun login(email: String, password: String): ApiResponse<String>? {val loginData = mapOf("email" to email, "password" to password)val json = Gson().toJson(loginData)val response = HttpUtil.postJson("$baseUrl/login", json)return response?.let { parseResponse<String>(it) }}fun getUserProfile(token: String): ApiResponse<User>? {val headers = mapOf("Authorization" to "Bearer $token")val response = HttpUtil.get("$baseUrl/profile", headers)return response?.let { parseResponse<User>(it) }}fun uploadAvatar(token: String, avatarFile: File, callback: (Boolean) -> Unit) {val requestBody = MultipartBody.Builder().setType(MultipartBody.FORM).addFormDataPart("avatar", avatarFile.name,avatarFile.asRequestBody("image/*".toMediaType())).build()val request = Request.Builder().url("$baseUrl/upload-avatar").addHeader("Authorization", "Bearer $token").post(requestBody).build()HttpClient.client.newCall(request).enqueue(object : Callback {override fun onFailure(call: Call, e: IOException) {callback(false)}override fun onResponse(call: Call, response: Response) {callback(response.isSuccessful)}})}
}
8. 调试和测试
调试技巧
// 详细日志记录
val loggingInterceptor = HttpLoggingInterceptor().apply {level = HttpLoggingInterceptor.Level.BODY
}// 网络状态监控
class NetworkStatusInterceptor : Interceptor {override fun intercept(chain: Interceptor.Chain): Response {val request = chain.request()println("请求: ${request.method} ${request.url}")println("请求头: ${request.headers}")val startTime = System.currentTimeMillis()val response = chain.proceed(request)val endTime = System.currentTimeMillis()println("响应: ${response.code} (${endTime - startTime}ms)")println("响应头: ${response.headers}")return response}
}
Mock 数据
class MockInterceptor : Interceptor {override fun intercept(chain: Interceptor.Chain): Response {val request = chain.request()// 根据 URL 返回模拟数据return when {request.url.toString().contains("/users/123") -> {val mockResponse = """{"id":123,"name":"测试用户"}"""Response.Builder().request(request).protocol(Protocol.HTTP_1_1).code(200).message("OK").body(mockResponse.toResponseBody("application/json".toMediaType())).build()}else -> chain.proceed(request)}}
}