OkHttp源码解析(使用与基本类介绍)

OkHttp使用的很多,我们先来分析一下OkHttp的简单使用以及涉及到的几个基本类。

使用

OkHttp的使用非常简单

  1. 创建一个OkHttpClient 对像
  2. 创建一个Request对象
  3. 用 OkHttpClient对象 和 Request 对象创建一个 Call 对象
  4. 调用同步请求方法 execute() 或者 异步请求方法 enqueue() 来获取 Reponse
java 复制代码
public class OkHttpRequest {
    private final OkHttpClient client = new OkHttpClient();

    public void request() {
        Request request = new Request.Builder().url("https://www.baidu.com/").build();
        
        Call call = client.newCall(request);
        
        try {
            // 同步请求
            Response response = call.execute();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        // 异步请求
        call.enqueue(new Callback() {
            @Override
            public void onFailure(@NonNull Call call, @NonNull IOException e) {

            }

            @Override
            public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {

            }
        });
    }
}

基本类介绍

OkHttpClient

这个类主要是请求的各种配置,采用建造者模式,方便配置一些请求参数

kotlin 复制代码
open class OkHttpClient internal constructor(
    builder: Builder
) : Cloneable, Call.Factory, WebSocket.Factory {

    constructor() : this(Builder())

    class Builder constructor() {
        //调度器
        internal var dispatcher: Dispatcher = Dispatcher()
        //连接池
        internal var connectionPool: ConnectionPool = ConnectionPool()
        //整体流程拦截器
        internal val interceptors: MutableList<Interceptor> = mutableListOf()
        //网络流程拦截器
        internal val networkInterceptors: MutableList<Interceptor> = mutableListOf()
        //流程监听器
        internal var eventListenerFactory: EventListener.Factory = EventListener.NONE.asFactory()
        //连接失败时是否重连
        internal var retryOnConnectionFailure = true
        //服务器认证设置
        internal var authenticator: Authenticator = Authenticator.NONE
        //是否重定向
        internal var followRedirects = true
        //是否从HTTP重定向到HTTPS
        internal var followSslRedirects = true
        //cookie设置
        internal var cookieJar: CookieJar = CookieJar.NO_COOKIES
        //缓存设置
        internal var cache: Cache? = null
        //DNS设置
        internal var dns: Dns = Dns.SYSTEM
        //代理设置
        internal var proxy: Proxy? = null
        //代理选择器设置
        internal var proxySelector: ProxySelector? = null
        //代理服务器认证设置
        internal var proxyAuthenticator: Authenticator = Authenticator.NONE
        //socket配置
        internal var socketFactory: SocketFactory = SocketFactory.getDefault()
        //https socket配置
        internal var sslSocketFactoryOrNull: SSLSocketFactory? = null
        internal var x509TrustManagerOrNull: X509TrustManager? = null
        internal var connectionSpecs: List<ConnectionSpec> = DEFAULT_CONNECTION_SPECS
        //协议
        internal var protocols: List<Protocol> = DEFAULT_PROTOCOLS
        //域名校验
        internal var hostnameVerifier: HostnameVerifier = OkHostnameVerifier
        internal var certificatePinner: CertificatePinner = CertificatePinner.DEFAULT
        internal var certificateChainCleaner: CertificateChainCleaner? = null
        //请求超时
        internal var callTimeout = 0
        //连接超时
        internal var connectTimeout = 10_000
        //读取超时
        internal var readTimeout = 10_000
        //写入超时
        internal var writeTimeout = 10_000
        internal var pingInterval = 0
        internal var minWebSocketMessageToCompress = RealWebSocket.DEFAULT_MINIMUM_DEFLATE_SIZE
        internal var routeDatabase: RouteDatabase? = null

        ···省略代码···
    }
}

Request

也是请求参数配置类,采用了建造者模式,主要是 请求URL、请求方法、请求头、请求体

kotlin 复制代码
class Request internal constructor(
    @get:JvmName("url") val url: HttpUrl,
    @get:JvmName("method") val method: String,
    @get:JvmName("headers") val headers: Headers,
    @get:JvmName("body") val body: RequestBody?,
    internal val tags: Map<Class<*>, Any>
        ) {
    open class Builder {
        //请求的URL
        internal var url: HttpUrl? = null
        //请求方法,如:GET、POST..
        internal var method: String
        //请求头
        internal var headers: Headers.Builder
        //请求体
        internal var body: RequestBody? = null
        ···省略代码···
    }
}

Call

请求调用接口,可以执行请求,也可以取消,只能执行一次

kotlin 复制代码
interface Call : Cloneable {
    // 返回发起此调用的原始请求
    fun request(): Request

    // 同步请求,立即执行
    // 抛出两种异常
    // 1.请求失败抛出 IOException
    // 2.如果在执行过一回的前提下再次执行抛出 IllegalStateException
    @Throws(IOException::class)
    fun execute(): Response

    // 异步请求,将请求安排在将来的某个时间点执行
    // 如果在执行过一回的前提下再次执行抛出 IllegalStateException
    fun enqueue(responseCallback: Callback)

    // 取消请求。已经完成的请求不能被取消
    fun cancel()

    // 是否已经被执行
    fun isExecuted(): Boolean

    // 是否被取消
    fun isCanceled(): Boolean

    // 一个完整Call请求流程的超时时间配置
    fun timeout(): Timeout

    // 克隆这个Call,创建一个新的相同的Call
    public override fun clone(): Call

    // 利用工厂模式来让 OkHttpClient 来创建 Call对象
    // 这个方法是在OkHttpClient中实现的
    fun interface Factory {
        fun newCall(request: Request): Call
    }
}

RealCall

从Call接口中我们可以知道,OkHttpClient 对象 通过 newCall 方法来创建一个Call 对象。 OkHttpClient.kt

java 复制代码
override fun newCall(request: Request): Call = RealCall(this, request, forWebSocket = false)

从代码可以看到,newCall 方法返回的是一个 RealCall 对象。 RealCall 是 Call 接口的实现。创建RealCall对象后,就要调用同步或异步请求方法,所以它里面还包含同步请求 execute() 与异步请求 enqueue()方法。

AsyncCall

realCall在执行异步请求的时候,是一个 AsyncCall 对象 被调度器中的线程池所执行

kotlin 复制代码
override fun enqueue(responseCallback: Callback) {
    check(executed.compareAndSet(false, true)) { "Already Executed" }

    callStart()
    client.dispatcher.enqueue(AsyncCall(responseCallback))
}

AsyncCall 本质上是个 Runnable

kotlin 复制代码
internal inner class AsyncCall(
    // 用户传入的响应回调的方法
    private val responseCallback: Callback
) : Runnable {
    // 同一个域名的请求次数,volatile + AtomicInteger 保证在多线程下及时可见性与原子性
    @Volatile var callsPerHost = AtomicInteger(0)
    private set

    fun reuseCallsPerHostFrom(other: AsyncCall) {
        this.callsPerHost = other.callsPerHost
    }

    val host: String
    get() = originalRequest.url.host

    val request: Request
    get() = originalRequest

    val call: RealCall
    get() = this@RealCall

    /**
     * Attempt to enqueue this async call on [executorService]. This will attempt to clean up
     * if the executor has been shut down by reporting the call as failed.
     */
    fun executeOn(executorService: ExecutorService) {
        client.dispatcher.assertThreadDoesntHoldLock()

        var success = false
        try {
            // 调用线程池执行
            executorService.execute(this)
            success = true
        } catch (e: RejectedExecutionException) {
            val ioException = InterruptedIOException("executor rejected")
            ioException.initCause(e)
            noMoreExchanges(ioException)
            // 请求失败,调用 Callback 的 onFailure 方法
            responseCallback.onFailure(this@RealCall, ioException)
        } finally {
            if (!success) {
                client.dispatcher.finished(this) // This call is no longer running!
            }
        }
    }

    override fun run() {
        threadName("OkHttp ${redactedUrl()}") {
            var signalledCallback = false
            timeout.enter()
            try {
                val response = getResponseWithInterceptorChain()
                signalledCallback = true
                // 请求成功,获取到服务器返回的 response
                responseCallback.onResponse(this@RealCall, response)
            } catch (e: IOException) {
                if (signalledCallback) {
                    // Do not signal the callback twice!
                    Platform.get().log("Callback failure for ${toLoggableString()}", Platform.INFO, e)
                } else {
                    responseCallback.onFailure(this@RealCall, e)
                }
            } catch (t: Throwable) {
                // 出现异常,调用 cancel 取消请求
                cancel()
                if (!signalledCallback) {
                    val canceledException = IOException("canceled due to $t")
                    canceledException.addSuppressed(t)
                    responseCallback.onFailure(this@RealCall, canceledException)
                }
                throw t
            } finally {
                // 请求结束,调用调度器的 finished
                client.dispatcher.finished(this)
            }
        }
    }
}

Dispatcher

调度器,用来调度 Call 对象,包含线程池,异步请求队列和同步请求队列

kotlin 复制代码
class Dispatcher constructor() {
    // 默认的最大并发请求量
    @get:Synchronized var maxRequests = 64
    set(maxRequests) {
        require(maxRequests >= 1) { "max < 1: $maxRequests" }
        synchronized(this) {
            field = maxRequests
        }
        promoteAndExecute()
    }

    // 单个host支持的最大并发量
    @get:Synchronized var maxRequestsPerHost = 5
    set(maxRequestsPerHost) {
        require(maxRequestsPerHost >= 1) { "max < 1: $maxRequestsPerHost" }
        synchronized(this) {
            field = maxRequestsPerHost
        }
        promoteAndExecute()
    }

    @set:Synchronized
    @get:Synchronized
    var idleCallback: Runnable? = null

    private var executorServiceOrNull: ExecutorService? = null

    // 线程池,处理请求调用
    @get:Synchronized
    @get:JvmName("executorService") val executorService: ExecutorService
    get() {
        if (executorServiceOrNull == null) {
            executorServiceOrNull = ThreadPoolExecutor(0, Int.MAX_VALUE, 60, TimeUnit.SECONDS,
                                                       SynchronousQueue(), threadFactory("$okHttpName Dispatcher", false))
        }
        return executorServiceOrNull!!
    }

    // 已经准备好的异步请求队列
    private val readyAsyncCalls = ArrayDeque<AsyncCall>()

    // 正在运行的异步请求队列,包含取消但是还未 finish 的 AsyncCall
    private val runningAsyncCalls = ArrayDeque<AsyncCall>()

    // 正在运行的同步请求队列,包含取消但是还未 finish 的 RealCall
    private val runningSyncCalls = ArrayDeque<RealCall>()
}

以上是OkHttp的简单使用 以及 使用过程中涉及到的部分类的介绍,后续我们从一个请求的整个流程来分析OkHttp的源码。

相关推荐
码农爱java6 小时前
设计模式--抽象工厂模式【创建型模式】
java·设计模式·面试·抽象工厂模式·原理·23种设计模式·java 设计模式
Jiude7 小时前
算法题题解记录——双变量问题的 “枚举右,维护左”
python·算法·面试
长安051113 小时前
面试经典题目:LeetCode134_加油站
c++·算法·面试
正在绘制中13 小时前
Java重要面试名词整理(一):MySQL&JVM&Tomcat
java·开发语言·面试
沉默王二14 小时前
虾皮开的很高,还有签字费。
后端·面试·github
Do14 小时前
时间系列三:实现毫秒级倒计时
前端·javascript·面试
小蜗牛慢慢爬行14 小时前
有关异步场景的 10 大 Spring Boot 面试问题
java·开发语言·网络·spring boot·后端·spring·面试
ThisIsClark15 小时前
【后端面试总结】MySQL主从复制逻辑的技术介绍
mysql·面试·职场和发展
程序猿进阶16 小时前
深入解析 Spring WebFlux:原理与应用
java·开发语言·后端·spring·面试·架构·springboot
LCG元1 天前
【面试问题】JIT 是什么?和 JVM 什么关系?
面试·职场和发展