目录
前言一、OkHttp继承与基础使用1.添加依赖2. 基本网络请求
二、OkHttp深层原理解析1.OkHttp整体架构2. 拦截器链 (Interceptor Chain)机制3. 连接池与复用机制
三、拦截器详解与实战1. 内置拦截器解析2. 自定义连接器实战
四、高级功能与最佳实践1.文件上传与下载2. WebSocket连接3. 完整配置示例
五、性能优化与调试1.连接池优化2.DNS优化
总结
前言
在Android开发中,网络请求是必不可少的部分。OkHttp作为Square公司开发的高效HTTP客户端,已经成为Android网络请求的事实标准。本文将详细介绍如何在Android应用中用Kotlin集成OkHttp,并深入解析其核心原理。
一、OkHttp继承与基础使用
1.添加依赖
首先在项目的build.gradle文中添加OkHttp依赖:
dependencies {
implementation("com.squareup.okhttp3:okhttp:4.10.0")
// 如果需要日志拦截器
implementation("com.squareup.okhttp3:logging-interceptor:4.10.0")
}
2. 基本网络请求
class OkHttpManager private constructor() {
private val client: OkHttpClient = OkHttpClient.Builder()
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS)
.build()
companion object {
val instance: OkHttpManager by lazy { OkHttpManager() }
}
// GET请求
suspend fun get(url: String): String = withContext(Dispatchers.IO) {
val request = Request.Builder()
.url(url)
.get()
.build()
client.newCall(request).execute().use { response ->
if (!response.isSuccessful) {
throw IOException("Unexpected code: $response")
}
response.body?.string() ?: ""
}
}
// POST请求
suspend fun post(url: String, jsonBody: String): String = withContext(Dispatchers.IO) {
val body = jsonBody.toRequestBody("application/json; charset=utf-8".toMediaType())
val request = Request.Builder()
.url(url)
.post(body)
.build()
client.newCall(request).execute().use { response ->
if (!response.isSuccessful) {
throw IOException("Unexpected code: $response")
}
response.body?.string() ?: ""
}
}
}
// 使用示例
class MainActivity : AppCompatActivity() {
private val okHttpManager = OkHttpManager.instance
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
lifecycleScope.launch {
try {
val result = okHttpManager.get("https://api.example.com/data")
// 处理结果
} catch (e: Exception) {
// 处理错误
}
}
}
}
二、OkHttp深层原理解析
1.OkHttp整体架构
OkHttp采用了分层架构设计,主要包含以下几个核心组件:
┌─────────────────────┐
│ Application Code │
└─────────────────────┘
│
┌─────────────────────┐
│ OkHttp Client │
└─────────────────────┘
│
┌─────────────────────┐
│ Interceptor Chain │ ← 核心处理流程
└─────────────────────┘
│
┌─────────────────────┐
│ Network Layer │ ← 连接管理、HTTP/2、HTTPS
└─────────────────────┘
│
┌─────────────────────┐
│ Socket Layer │ ← TCP/UDP连接
└─────────────────────┘
2. 拦截器链 (Interceptor Chain)机制
拦截器是OkHttp最核心的设计,采用了责任链模式。让我们深入分析其执行流程:
// 简化的拦截器链执行流程
class RealCall(private val client: OkHttpClient, private val originalRequest: Request) : Call {
override fun execute(): Response {
// 构建拦截器链
val chain = RealInterceptorChain(
interceptors = client.interceptors +
listOf(
RetryAndFollowUpInterceptor(client),
BridgeInterceptor(client.cookieJar),
CacheInterceptor(client.cache),
ConnectInterceptor(client),
client.networkInterceptors +
CallServerInterceptor()
),
index = 0,
request = originalRequest,
call = this,
eventListener = client.eventListenerFactory.create(this)
)
return chain.proceed(originalRequest)
}
}
3. 连接池与复用机制
OkHttp通过连接池实现连接复用,显著提升性能:
// 连接池核心实现原理
class ConnectionPool(
maxIdleConnections: Int,
keepAliveDuration: Long,
timeUnit: TimeUnit
) {
private val connections = ArrayDeque<RealConnection>()
private val cleanupRunning = AtomicBoolean()
// 获取可用连接
internal fun get(address: Address, call: RealCall, routes: List<Route>?): RealConnection? {
connections.forEach { connection ->
if (connection.isEligible(address, routes)) {
call.acquireConnectionNoEvents(connection)
return connection
}
}
return null
}
// 清理空闲连接
fun cleanup(now: Long): Long {
var longestIdleDurationNs = Long.MIN_VALUE
var connectionToClose: RealConnection? = null
connections.forEach { connection ->
// 计算空闲时间
val idleDurationNs = now - connection.idleAtNanos
if (idleDurationNs > keepAliveDurationNs) {
// 超过保活时间,关闭连接
connectionToClose = connection
} else if (idleDurationNs > longestIdleDurationNs) {
longestIdleDurationNs = idleDurationNs
}
}
connectionToClose?.let {
it.socket().closeQuietly()
connections.remove(it)
}
return if (longestIdleDurationNs >= 0) {
keepAliveDurationNs - longestIdleDurationNs
} else {
keepAliveDurationNs
}
}
}
三、拦截器详解与实战
1. 内置拦截器解析
RetryAndFollowUpInterceptor
负责重试和重定向处理:
class CustomRetryInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
var request = chain.request()
var response: Response
var retryCount = 0
while (true) {
try {
response = chain.proceed(request)
} catch (e: IOException) {
if (retryCount < MAX_RETRY_COUNT && shouldRetry(e)) {
retryCount++
continue
}
throw e
}
// 处理重定向
val followUp = followUpRequest(response, chain.connection())
if (followUp == null) {
return response
}
request = followUp
}
}
private fun shouldRetry(e: IOException): Boolean {
return e is SocketTimeoutException ||
e is ConnectException
}
}
CacheInterceptor
实现HTTP缓存机制:
class CacheInterceptor(private val cache: Cache?) : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request()
val cacheCandidate = cache?.get(request)
val now = System.currentTimeMillis()
val strategy = CacheStrategy.Factory(now, request, cacheCandidate).compute()
val networkRequest = strategy.networkRequest
val cacheResponse = strategy.cacheResponse
// 如果禁止使用网络且缓存为空,返回504错误
if (networkRequest == null && cacheResponse == null) {
return Response.Builder()
.request(request)
.protocol(Protocol.HTTP_1_1)
.code(504)
.message("Unsatisfiable Request")
.body("".toResponseBody(null))
.build()
}
// 如果不使用网络,直接返回缓存
if (networkRequest == null) {
return cacheResponse!!.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.build()
}
// 发起网络请求
var networkResponse: Response? = null
try {
networkResponse = chain.proceed(networkRequest)
} finally {
// 如果网络请求失败且缓存可用,返回缓存
if (networkResponse == null && cacheResponse != null) {
cacheResponse.body?.closeQuietly()
}
}
// 更新缓存
if (cacheResponse != null) {
if (networkResponse?.code == HTTP_NOT_MODIFIED) {
val updatedResponse = cacheResponse.newBuilder()
.headers(combine(cacheResponse.headers, networkResponse.headers))
.build()
cache.trackConditionalCacheHit()
cache.update(cacheResponse, updatedResponse)
return updatedResponse
} else {
cacheResponse.body?.closeQuietly()
}
}
val response = networkResponse!!.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.networkResponse(stripBody(networkResponse))
.build()
if (cache != null) {
if (response.promisesBody() && CacheStrategy.isCacheable(response, request)) {
cache.put(response)
}
}
return response
}
}
2. 自定义连接器实战
日志拦截器
class LoggingInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request()
val startTime = System.nanoTime()
// 记录请求信息
logRequest(request)
val response = try {
chain.proceed(request)
} catch (e: Exception) {
Log.e("OkHttp", "Request failed: ${e.message}")
throw e
}
// 记录响应信息
logResponse(response, startTime)
return response
}
private fun logRequest(request: Request) {
Log.d("OkHttp", "--> ${request.method} ${request.url}")
request.headers.forEach { name, value ->
Log.d("OkHttp", "$name: $value")
}
request.body?.let { body ->
val buffer = Buffer()
body.writeTo(buffer)
Log.d("OkHttp", buffer.readString(Charsets.UTF_8))
}
}
private fun logResponse(response: Response, startTime: Long) {
val endTime = System.nanoTime()
val duration = (endTime - startTime) / 1e6
Log.d("OkHttp", "<-- ${response.code} ${response.message} ${response.request.url} (${duration}ms)")
response.headers.forEach { name, value ->
Log.d("OkHttp", "$name: $value")
}
}
}
认证拦截器
class AuthInterceptor(private val tokenManager: TokenManager) : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val originalRequest = chain.request()
// 添加认证头
val requestWithAuth = originalRequest.newBuilder()
.header("Authorization", "Bearer ${tokenManager.getToken()}")
.build()
var response = chain.proceed(requestWithAuth)
// 如果token过期,尝试刷新
if (response.code == HttpURLConnection.HTTP_UNAUTHORIZED) {
synchronized(this) {
val newToken = tokenManager.refreshToken()
if (newToken != null) {
val newRequest = originalRequest.newBuilder()
.header("Authorization", "Bearer $newToken")
.build()
response.close()
response = chain.proceed(newRequest)
}
}
}
return response
}
}
class TokenManager {
private var token: String? = null
private var refreshToken: String? = null
fun getToken(): String? = token
fun refreshToken(): String? {
// 实现token刷新逻辑
return null
}
}
网络状态拦截器
class NetworkStateInterceptor(private val context: Context) : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
if (!isNetworkAvailable()) {
throw NoNetworkException()
}
val request = chain.request()
val response = chain.proceed(request)
// 检查服务器返回的网络状态
if (response.code == HttpURLConnection.HTTP_CLIENT_TIMEOUT) {
// 处理超时情况
}
return response
}
private fun isNetworkAvailable(): Boolean {
val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE)
as ConnectivityManager
val network = connectivityManager.activeNetwork
val networkCapabilities = connectivityManager.getNetworkCapabilities(network)
return networkCapabilities?.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) == true
}
}
class NoNetworkException : IOException("No network available")
四、高级功能与最佳实践
1.文件上传与下载
class FileTransferManager(private val client: OkHttpClient) {
// 文件上传
suspend fun uploadFile(url: String, file: File, params: Map<String, String> = emptyMap()): String {
return withContext(Dispatchers.IO) {
val requestBody = MultipartBody.Builder()
.setType(MultipartBody.FORM)
.apply {
// 添加文件
addFormDataPart(
"file",
file.name,
file.asRequestBody("multipart/form-data".toMediaType())
)
// 添加其他参数
params.forEach { (key, value) ->
addFormDataPart(key, value)
}
}
.build()
val request = Request.Builder()
.url(url)
.post(requestBody)
.build()
client.newCall(request).execute().use { response ->
if (!response.isSuccessful) {
throw IOException("Upload failed: ${response.code}")
}
response.body?.string() ?: ""
}
}
}
// 文件下载
suspend fun downloadFile(url: String, outputFile: File): Long {
return withContext(Dispatchers.IO) {
val request = Request.Builder()
.url(url)
.build()
client.newCall(request).execute().use { response ->
if (!response.isSuccessful) {
throw IOException("Download failed: ${response.code}")
}
response.body?.byteStream()?.use { inputStream ->
outputFile.outputStream().use { outputStream ->
inputStream.copyTo(outputStream)
}
}
response.body?.contentLength() ?: 0L
}
}
}
}
2. WebSocket连接
class WebSocketManager {
private var webSocket: WebSocket? = null
private val listener = 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 onClosed(webSocket: WebSocket, code: Int, reason: String) {
Log.d("WebSocket", "Closed: $reason")
}
override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) {
Log.e("WebSocket", "Error: ${t.message}")
}
}
fun connect(url: String, client: OkHttpClient) {
val request = Request.Builder()
.url(url)
.build()
webSocket = client.newWebSocket(request, listener)
}
fun sendMessage(message: String) {
webSocket?.send(message)
}
fun disconnect() {
webSocket?.close(1000, "Manual disconnect")
}
}
3. 完整配置示例
class AdvancedOkHttpClient private constructor(context: Context) {
val client: OkHttpClient by lazy {
OkHttpClient.Builder()
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS)
.connectionPool(ConnectionPool(5, 5, TimeUnit.MINUTES))
.addInterceptor(LoggingInterceptor())
.addInterceptor(AuthInterceptor(TokenManager()))
.addInterceptor(NetworkStateInterceptor(context))
.addNetworkInterceptor(StethoInterceptor()) // Facebook Stetho调试工具
.cache(createCache(context))
.cookieJar(createCookieJar())
.eventListener(createEventListener())
.build()
}
private fun createCache(context: Context): Cache {
val cacheDirectory = File(context.cacheDir, "okhttp_cache")
val cacheSize = 10 * 1024 * 1024L // 10MB
return Cache(cacheDirectory, cacheSize)
}
private fun createCookieJar(): CookieJar {
return object : CookieJar {
private val cookieStore = mutableMapOf<HttpUrl, List<Cookie>>()
override fun saveFromResponse(url: HttpUrl, cookies: List<Cookie>) {
cookieStore[url] = cookies
}
override fun loadForRequest(url: HttpUrl): List<Cookie> {
return cookieStore[url] ?: emptyList()
}
}
}
private fun createEventListener(): EventListener {
return object : EventListener() {
override fun callStart(call: Call) {
Log.d("OkHttp", "Call started: ${call.request().url}")
}
override fun callEnd(call: Call) {
Log.d("OkHttp", "Call ended: ${call.request().url}")
}
override fun requestHeadersEnd(call: Call, request: Request) {
Log.d("OkHttp", "Request headers sent")
}
}
}
companion object {
@Volatile
private var instance: AdvancedOkHttpClient? = null
fun getInstance(context: Context): AdvancedOkHttpClient {
return instance ?: synchronized(this) {
instance ?: AdvancedOkHttpClient(context.applicationContext).also { instance = it }
}
}
}
}
五、性能优化与调试
1.连接池优化
class OptimizedConnectionPool {
companion object {
// 根据应用场景调整连接池参数
fun createConnectionPool(): ConnectionPool {
return ConnectionPool(
maxIdleConnections = 5, // 最大空闲连接数
keepAliveDuration = 5, // 保活时间
timeUnit = TimeUnit.MINUTES
)
}
}
}
2.DNS优化
class CustomDns : Dns {
override fun lookup(hostname: String): List<InetAddress> {
return try {
// 可以在这里实现自定义DNS解析逻辑
InetAddress.getAllByName(hostname).toList()
} catch (e: Exception) {
// 备用DNS解析
Dns.SYSTEM.lookup(hostname)
}
}
}
总结
OkHttp作为一个功能强大、设计优雅的HTTP客户端,其核心优势在于:
拦截器机制: 提供了极大的灵活性,可以轻松实现日志、认证、缓存等功能。连接复用: 通过连接池显著提升网络性能透明压缩: 自动处理Gzip压缩HTTP/2支持: 多路复用减少延迟缓存机制: 减少重复网络请求
通过本文的详细解析和实战示例,相信你已经掌握了如何在Android应用中使用Kotlin集成OkHttp,并理解了其深层原理。在实际开发中,合理使用拦截器、优化连接池配置、实现适当的缓存策略,可以显著提升应用的网络性能。







![[C++探索之旅] 第一部分第十一课:小练习,猜单词 - 鹿快](https://img.lukuai.com/blogimg/20251015/da217e2245754101b3d2ef80869e9de2.jpg)










暂无评论内容