okhttp使用指南

欢迎访问我的主页: https://heeheeaii.github.io/

1. 基础配置

依赖添加

kotlin 复制代码
// Gradle (build.gradle.kts)
implementation("com.squareup.okhttp3:okhttp:4.11.0")
implementation("com.squareup.okhttp3:logging-interceptor:4.11.0")

单例客户端配置

kotlin 复制代码
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 请求

kotlin 复制代码
// 同步 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 请求

kotlin 复制代码
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 表单请求

kotlin 复制代码
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. 文件操作

文件上传

kotlin 复制代码
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()
    }
}

文件下载

kotlin 复制代码
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 false
                val contentLength = body.contentLength()
                
                body.byteStream().use { inputStream ->
                    outputFile.outputStream().use { outputStream ->
                        val buffer = ByteArray(8192)
                        var downloaded = 0L
                        var bytesRead: Int
                        
                        while (inputStream.read(buffer).also { bytesRead = it } != -1) {
                            outputStream.write(buffer, 0, bytesRead)
                            downloaded += bytesRead
                            
                            if (contentLength > 0) {
                                val progress = (downloaded * 100 / contentLength).toInt()
                                onProgress(progress)
                            }
                        }
                    }
                }
                true
            } else {
                false
            }
        }
    } catch (e: Exception) {
        false
    }
}

4. 实用拦截器

日志拦截器

kotlin 复制代码
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
    }
}

认证拦截器

kotlin 复制代码
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)
    }
}

重试拦截器

kotlin 复制代码
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 = 0
        
        while (!response.isSuccessful && retryCount < maxRetries) {
            retryCount++
            println("重试第 $retryCount 次: ${request.url}")
            
            response.close()
            response = chain.proceed(request)
        }
        
        return response
    }
}

缓存拦截器

kotlin 复制代码
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. 高级配置

缓存设置

kotlin 复制代码
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 配置

kotlin 复制代码
// 信任所有证书(仅用于开发环境)
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 工具类

kotlin 复制代码
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 JSON
        fun 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 响应包装类

kotlin 复制代码
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. 实际使用示例

用户服务示例

kotlin 复制代码
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. 调试和测试

调试技巧

kotlin 复制代码
// 详细日志记录
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 数据

kotlin 复制代码
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)
        }
    }
}
相关推荐
Monkey-旭5 小时前
Android 注解完全指南:从基础概念到自定义实战
android·java·kotlin·注解·annotation
alexhilton1 天前
如何构建Android应用:深入探讨原则而非规则
android·kotlin·android jetpack
TeleostNaCl1 天前
SMBJ 简单使用指南 实现在 Java/Android 程序中访问 SMB 服务器
android·java·运维·服务器·经验分享·kotlin
小孔龙1 天前
Kotlin 序列化:重复引用是技术问题还是架构缺陷?
android·kotlin·json
Kapaseker1 天前
每个Kotlin开发者应该掌握的最佳实践,第三趴
android·kotlin
啦工作呢2 天前
ES6 promise-try-catch-模块化开发
android·okhttp
低调小一2 天前
双端 FPS 全景解析:Android 与 iOS 的渲染机制、监控与优化
android·ios·kotlin·swift·fps
YAY_tyy2 天前
【Cesium 开发实战教程】第六篇:三维模型高级交互:点击查询、材质修改与动画控制
前端·javascript·3d·教程·cesium
用户092 天前
Kotlin 将会成为跨平台开发的终极选择么?
android·面试·kotlin