1.OkHttp使用方法
okhttp是一个第三方类库,用于android中请求网络。
implementation("com.squareup.okhttp3:okhttp:4.9.0")
val url = "https://www.baidu.com/"
val client = OkHttpClient()
val request : Request = Request.Builder()
.url(url)
.build()
client.newCall(request)
.enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
}
override fun onResponse(call: Call, response: Response) {
println("Response status code: ${response.code}")//这里输出了状态码
}
})
2.OkHttp请求流程
2.1 从请求处理开始分析
无论在使用OkHttp进行什么请求的时候都会创建OkHttpClient对象并调用他的 newCall() 方法
1.newCall()
override fun newCall(request: Request): Call = RealCall(this, request, forWebSocket = false)
返回了一个RealCall对象,所以也就意味着我们使用OkHttpClient对象调用的execute()操作实际上是RealCall的execute()操作,那我们就来看RealCall的execute()方法
2.RealCall的execute()
override fun execute(): Response {
// check()是kotlin特有的一个方法,他本质上就是一个if,
// 但是当他的判断语句是false的话,
// 他就会抛出一个IllegalStateException异常,异常的内容就是后面的语句
// executed是一个布尔值,他的作用就是判断是不是执行过了,
// 如果执行过了还执行了这个方法的话就抛异常
check(executed.compareAndSet(false, true)) { "Already Executed" }
timeout.enter()
callStart()
try {
//调用了client.dispatcher,是Dispatcher类的一个对象
client.dispatcher.executed(this)
return getResponseWithInterceptorChain()
} finally {
client.dispatcher.finished(this)
}
}
2.2 Dispatcher任务调度
Dispatcher类,有如下成员变量定义:
// 最大并发请求数
var maxRequests = 64
// 每个主机的最大请求数
var maxRequestsPerHost = 5
// 消费者线程
val executorService: ExecutorService
// 将要运行的异步请求队列
private val readyAsyncCalls = ArrayDeque<AsyncCall>()
// 正在运行的异步请求队队列
private val runningAsyncCalls = ArrayDeque<AsyncCall>()
// 正在运行的同步请求队列
private val runningSyncCalls = ArrayDeque<RealCall>()
Dispatcher的构造方法
// 主构造方法,没写具体实现
class Dispatcher constructor() {}
constructor(executorService: ExecutorService) : this() {
this.executorServiceOrNull = executorService
}
传进来的executorService传给了executorServiceOrNull
executorServiceOrNull
// executorServiceOrNull这个应该是因为kotlin的空安全检查特性而定义的,
//本质上就是executorService
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!!
}
我们可以看到executorService的set方法,就是创建了一个线程池。再结合他有两个构造器就知道:如果没有给Dispatcher传入一个线程池他就会自己创建一个线程池。这个线程池适合执行大量且耗时较少的任务。
enqueue()方法
internal fun enqueue(call: AsyncCall) {
synchronized(this) {
readyAsyncCalls.add(call)
//来一个请求就把他添加到就绪请求队列中去
// Mutate the AsyncCall so that it shares the AtomicInteger of an existing running call to
// the same host.
//然后就来判断forWebSocket这个属性
//OkHttp还可以进行WebSocket通信,而这个属性就是为WebSocket通信准备的。
if (!call.call.forWebSocket) {
val existingCall = findExistingCallWithHost(call.host)
if (existingCall != null) call.reuseCallsPerHostFrom(existingCall)
}
}
promoteAndExecute()
}
在RealCall的newRealCall()方法这块,这个方法传入的参数中有一个Boolean值名字就叫forWebSocket。
companion object {
fun newRealCall(
client: OkHttpClient,
originalRequest: Request,
forWebSocket: Boolean
): RealCall {
// Safely publish the Call instance to the EventListener.
return RealCall(client, originalRequest, forWebSocket).apply {
transmitter = Transmitter(client, this)
}
}
}
promoteAndExecute()方法:
private fun promoteAndExecute(): Boolean {
// 这个和check也一样,只不过抛出的是AssertionError异常
this.assertThreadDoesntHoldLock()
// mutableListOf是kotlin里面的可变list集合
val executableCalls = mutableListOf<AsyncCall>()
val isRunning: Boolean
synchronized(this) {
val i = readyAsyncCalls.iterator()
//首先将已经就绪队列遍历一遍
while (i.hasNext()) {
val asyncCall = i.next()
//判断正在运行的数量是不是大于定义的最大请求数
//如果大于的话直接退出循环
if (runningAsyncCalls.size >= this.maxRequests) break // Max capacity.
//如果不大于,则判断这个主机请求数是不是大于定义的每个主机最大请求数
//如果大于就跳过这个请求换下一个请求
if (asyncCall.callsPerHost.get() >= this.maxRequestsPerHost) continue // Host max capacity.
i.remove()
asyncCall.callsPerHost.incrementAndGet()
//不大于就把它调入正在运行的请求队列里面,直到遍历完成。
executableCalls.add(asyncCall)
runningAsyncCalls.add(asyncCall)
}
//然后判断还有没有正在运行的请求,如果有就isRunning置true。
isRunning = runningCallsCount() > 0
}
//接着再取出executableCalls里的每一个元素,然后执行executteOn()方法。
for (i in 0 until executableCalls.size) {
val asyncCall = executableCalls[i]
asyncCall.executeOn(executorService)
}
return isRunning
}
AsyncCall的executeOn()方法:
fun executeOn(executorService: ExecutorService) {
client.dispatcher.assertThreadDoesntHoldLock()
var success = false
try {
//如果成功执行就将success置true
executorService.execute(this)
success = true
} catch (e: RejectedExecutionException) {
//如果不成功,则抛异常并返回给responseCallback的onFailure()方法。
val ioException = InterruptedIOException("executor rejected")
ioException.initCause(e)
noMoreExchanges(ioException)
responseCallback.onFailure(this@RealCall, ioException)
} finally {
if (!success) {
//如果没有成功执行也就是success为false,
//那么在finally中就会执行client.dispatcher.finished()方法:
client.dispatcher.finished(this) // This call is no longer running!
}
}
}
这段代码就是在执行线程池中的线程
internal fun finished(call: AsyncCall) {
call.callsPerHost.decrementAndGet()
finished(runningAsyncCalls, call)
}
这个方法先将传入的AsyncCall的callsPerHost给减1,然后再调用了finished()方法,
private fun <T> finished(calls: Deque<T>, call: T) {
val idleCallback: Runnable?
synchronized(this) {
if (!calls.remove(call)) throw AssertionError("Call wasn't in-flight!")
idleCallback = this.idleCallback
}
//promoteAndExecute()方法,他的返回值就是判断当前这个运行队列中还有没有请求
//如果还有就返回true,没有就false。
val isRunning = promoteAndExecute()
//if,判断isRunning和idleCallback,
//如果当前这个请求还没有执行的话,就调用run()方法执行当前请求。这样每个请求都执行完毕了。
if (!isRunning && idleCallback != null) {
idleCallback.run()
}
}
run()方法
override fun run() {
threadName("OkHttp ${redactedUrl()}") {
var signalledCallback = false
transmitter.timeoutEnter()
try {
//调用了一个getResponseWithInterceptorChain()方法,并返回了response
val response = getResponseWithInterceptorChain()
signalledCallback = true
//并将它返回给了responseCallback.onResponse()方法
responseCallback.onResponse(this@RealCall, response)
} catch (e: IOException) {
if (signalledCallback) {
// Do not signal the callback twice!
Platform.get().log(INFO, "Callback failure for ${toLoggableString()}", e)
} else {
//如果失败了就将结果返回给responseCallback.onFailure()方法
responseCallback.onFailure(this@RealCall, e)
}
} finally {
//最后调用client.dispatcher.finished()方法。
client.dispatcher.finished(this)
}
}
}
2.3 Interceptor拦截器
getResponseWithInterceptorChain()方法
@Throws(IOException::class)
internal fun getResponseWithInterceptorChain(): Response {
// Build a full stack of interceptors.
//首先就创建了一大堆的连接器并添加到interceptors集合中。
val interceptors = mutableListOf<Interceptor>()
interceptors += client.interceptors
interceptors += RetryAndFollowUpInterceptor(client)
interceptors += BridgeInterceptor(client.cookieJar)
interceptors += CacheInterceptor(client.cache)
interceptors += ConnectInterceptor
if (!forWebSocket) {
interceptors += client.networkInterceptors
}
interceptors += CallServerInterceptor(forWebSocket)
//然后创建了一个RealInterceptorChain对象,
val chain = RealInterceptorChain(
call = this,
interceptors = interceptors,
index = 0,
exchange = null,
request = originalRequest,
connectTimeoutMillis = client.connectTimeoutMillis,
readTimeoutMillis = client.readTimeoutMillis,
writeTimeoutMillis = client.writeTimeoutMillis
)
var calledNoMoreExchanges = false
try {
//并调用了他的proceed()方法,
val response = chain.proceed(originalRequest)
if (isCanceled()) {
response.closeQuietly()
throw IOException("Canceled")
}
//接着主要目的就是将proceed()返回的response给返回去。
return response
} catch (e: IOException) {
calledNoMoreExchanges = true
throw noMoreExchanges(e) as Throwable
} finally {
if (!calledNoMoreExchanges) {
noMoreExchanges(null)
}
}
}
RealInterceptorChain的proceed()方法:
@Throws(IOException::class)
override fun proceed(request: Request): Response {
check(index < interceptors.size)
//首先就是一个index,index是RealInterceptorChain构造器中传入的参数,是第四个参数,
//所以我们看getResponseWithInterceptorChain()方法中创建RealInterceptorChain对象时构造器的第四个传入的值为0。
//然后判断index的值是不是大于interceptors的大小,如果大于就抛异常,否则就继续检查
calls++
if (exchange != null) {
check(exchange.finder.sameHostAndPort(request.url)) {
"network interceptor ${interceptors[index - 1]} must retain the same host and port"
}
check(calls == 1) {
"network interceptor ${interceptors[index - 1]} must call proceed() exactly once"
}
}
// Call the next interceptor in the chain.
//然后再创建RealInterceptorChain对象,此时创建的对象传入的index为此时的index+1
//然后再调用interceptor的intercept()方法,并返回response。
val next = copy(index = index + 1, request = request)
val interceptor = interceptors[index]
@Suppress("USELESS_ELVIS")
val response = interceptor.intercept(next) ?: throw NullPointerException(
"interceptor $interceptor returned null")
if (exchange != null) {
check(index + 1 >= interceptors.size || next.calls == 1) {
"network interceptor $interceptor must call proceed() exactly once"
}
}
check(response.body != null) { "interceptor $interceptor returned a response with no body" }
return response
}
2.3 Interceptor源码
OkHttp中Interceptor的实现类有:
ConnectInterceptor:连接拦截器。
CallServerInterceptor:请求服务器拦截器
CacheInterceptor:缓存拦截器
BridgeInterceptor:桥梁拦截器。
其中较为重要的就是ConnectInterceptor和CallServerInterceptor
ConnectInterceptor
这个类主要用来实现网络请求连接。
intercept方法
@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
//先将传入的chain对象造型成了RealInterceptorChain的对象
//然后调用他的request()和transmitter()方法,分别得到当前chain的request和transmitter
val realChain = chain as RealInterceptorChain
val request = realChain.request()
val transmitter = realChain.transmitter()
// We need the network to satisfy this request. Possibly for validating a conditional GET.
//然后执行了request的method()方法,判断request的类型是不是GET
//如果是doExtensiveHealthChecks就为false,否则为true
val doExtensiveHealthChecks = request.method != "GET"
//接着把doExtensiveHealthChecks传入transmitter的newExchange()方法中去
val exchange = transmitter.newExchange(chain, doExtensiveHealthChecks)
//然后再调用了proceed()方法
return realChain.proceed(request, transmitter, exchange)
}
CallServerInterceptor
这个类是网络请求的本质。它的intercept方法源码如下:
主要是向服务器发送请求数据和接受服务器返回的数据
@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
val realChain = chain as RealInterceptorChain
val exchange = realChain.exchange()
val request = realChain.request()
val requestBody = request.body
val sentRequestMillis = System.currentTimeMillis()
// 写入请求头
exchange.writeRequestHeaders(request)
var responseHeadersStarted = false
var responseBuilder: Response.Builder? = null
// 写入请求体信息
if (HttpMethod.permitsRequestBody(request.method) && requestBody != null) {
// 如果请求上有“expect:100 continue”头
// 请等待“http/1.1 100 continue”响应,然后再传输请求主体.
// 如果我们没有得到,返回我们得到的(例如4xx响应),而不传输请求体。
if ("100-continue".equals(request.header("Expect"), ignoreCase = true)) {
exchange.flushRequest()
responseHeadersStarted = true
exchange.responseHeadersStart()
responseBuilder = exchange.readResponseHeaders(true)
}
if (responseBuilder == null) {
if (requestBody.isDuplex()) {
// 准备一个双工主体,以便应用程序稍后可以发送请求主体。
exchange.flushRequest()
val bufferedRequestBody = exchange.createRequestBody(request, true).buffer()
requestBody.writeTo(bufferedRequestBody)
} else {
// 如果满足“expect:100 continue”预期,则编写请求正文。
val bufferedRequestBody = exchange.createRequestBody(request, false).buffer()
requestBody.writeTo(bufferedRequestBody)
bufferedRequestBody.close()
}
} else {
exchange.noRequestBody()
if (!exchange.connection()!!.isMultiplexed) {
// 如果不满足“expect:100 continue”的要求,请防止重用HTTP/1连接。
// 否则,我们仍然有义务传输请求主体以使连接保持一致状态。
exchange.noNewExchangesOnConnection()
}
}
} else {
exchange.noRequestBody()
}
// 结束请求
if (requestBody == null || !requestBody.isDuplex()) {
exchange.finishRequest()
}
if (!responseHeadersStarted) {
exchange.responseHeadersStart()
}
if (responseBuilder == null) {
responseBuilder = exchange.readResponseHeaders(false)!!
}
// 读取响应头信息
var response = responseBuilder
.request(request)
.handshake(exchange.connection()!!.handshake())
.sentRequestAtMillis(sentRequestMillis)
.receivedResponseAtMillis(System.currentTimeMillis())
.build()
var code = response.code
if (code == 100) {
// 服务器发送了一个100继续,即使我们没有请求。
// 再次尝试读取实际响应
response = exchange.readResponseHeaders(false)!!
.request(request)
.handshake(exchange.connection()!!.handshake())
.sentRequestAtMillis(sentRequestMillis)
.receivedResponseAtMillis(System.currentTimeMillis())
.build()
code = response.code
}
exchange.responseHeadersEnd(response)
// openResponseBody 获取响应体信息
response = if (forWebSocket && code == 101) {
// 连接正在升级,但我们需要确保拦截器看到非空的响应主体。
response.newBuilder()
.body(EMPTY_RESPONSE)
.build()
} else {
response.newBuilder()
.body(exchange.openResponseBody(response))
.build()
}
if ("close".equals(response.request.header("Connection"), ignoreCase = true) ||
"close".equals(response.header("Connection"), ignoreCase = true)) {
exchange.noNewExchangesOnConnection()
}
if ((code == 204 || code == 205) && response.body?.contentLength() ?: -1L > 0L) {
throw ProtocolException(
"HTTP $code had non-zero Content-Length: ${response.body?.contentLength()}")
}
//返回一个响应
return response
}
2.4 Transmiter
Transmitter类是OkHttp的应用层和网络层的一个桥梁类。
该类的初始化:
class Transmitter(
//构造器中传入了两个参数,一个OkHttpClient,一个Call
private val client: OkHttpClient,
private val call: Call
) {
//然后又创建了一个连接池connectionPool,
//还有一个监听器,我们可以通过扩展这个类来监听程序的HTTP的调用数量、大小和持续时间
private val connectionPool: RealConnectionPool = client.connectionPool.delegate
private val eventListener: EventListener = client.eventListenerFactory.create(call)
private val timeout = object : AsyncTimeout() {
override fun timedOut() {
cancel()
}
}.apply {
timeout(client.callTimeoutMillis.toLong(), MILLISECONDS)
}
2.5 RealConnection
class RealConnection(
val connectionPool: RealConnectionPool,
private val route: Route
) : Http2Connection.Listener(), Connection {
// 以下字段由connect()初始化,从不重新分配。
// 底层socket
private var rawSocket: Socket? = null
/**
* 应用层套接字。如果此连接不使用SSL,则可以是[sslsocket]分层在[rawsocket]上,也可以是[rawsocket]本身。
*/
// 应用层socket
private var socket: Socket? = null
// 握手
private var handshake: Handshake? = null
// 协议
private var protocol: Protocol? = null
// http2的连接
private var http2Connection: Http2Connection? = null
// 与服务器交互的输入输出流
private var source: BufferedSource? = null
private var sink: BufferedSink? = null
// 跟踪连接状态下的字段由连接池保护。
/**
* 如果为true,则不能在此连接上创建新的交换。一旦是true的,这总是true的。
* 由[ConnectionPool]监视。
*/
var noNewExchanges = false
/**
* 建立可能由于所选路由而导致的流时出现问题的次数。由[ConnectionPool]保护。
*/
internal var routeFailureCount = 0
internal var successCount = 0
private var refusedStreamCount = 0
/**
* 此连接可以承载的最大并发流数。
* 如果“allocations.size()<allocationlimit”,则可以在此连接上创建新流。
*/
private var allocationLimit = 1
connect()方法:
fun connect(
connectTimeout: Int,
readTimeout: Int,
writeTimeout: Int,
pingIntervalMillis: Int,
connectionRetryEnabled: Boolean,
call: Call,
eventListener: EventListener
) {
//首先检查是否已经建立连接,如果已经建立就抛异常,没有的话就继续
check(protocol == null) { "already connected" }
// 线路的选择
//接着就得到了ConnectionSpecs,然后根据他建立了一个connectionSpecSelector集合
var routeException: RouteException? = null
val connectionSpecs = route.address.connectionSpecs
val connectionSpecSelector = ConnectionSpecSelector(connectionSpecs)
//接着判断是不是安全连接,也就是ssl连接
//如果不是的话就判断了一些属性,先确定是不是明文然后再确定主机能不能接受明文操作。
if (route.address.sslSocketFactory == null) {
if (ConnectionSpec.CLEARTEXT !in connectionSpecs) {
throw RouteException(UnknownServiceException(
"CLEARTEXT communication not enabled for client"))
}
val host = route.address.url.host
if (!Platform.get().isCleartextTrafficPermitted(host)) {
throw RouteException(UnknownServiceException(
"CLEARTEXT communication to $host not permitted by network security policy"))
}
} else {
if (Protocol.H2_PRIOR_KNOWLEDGE in route.address.protocols) {
throw RouteException(UnknownServiceException(
"H2_PRIOR_KNOWLEDGE cannot be used with HTTPS"))
}
}
//接着就开始连接,判断是不是要进行隧道通信,
//如果是就调用connectTunnel()建立隧道通信,如果不是就调用connectSocket()建立普通的通信
while (true) {
try {
// 如果要求隧道模式,建立通道连接,通常不是这种
if (route.requiresTunnel()) {
connectTunnel(connectTimeout, readTimeout, writeTimeout, call, eventListener)
if (rawSocket == null) {
// 我们无法连接隧道,但适当关闭了我们的资源。
break
}
} else {
// 一般都走这条逻辑了,实际上很简单就是socket的连接
connectSocket(connectTimeout, readTimeout, call, eventListener)
}
// https的建立
//通过establishProtocol()建立协议。如果是HTTP/2就设置相关属性。
establishProtocol(connectionSpecSelector, pingIntervalMillis, call, eventListener)
eventListener.connectEnd(call, route.socketAddress, route.proxy, protocol)
break
} catch (e: IOException) {
socket?.closeQuietly()
rawSocket?.closeQuietly()
socket = null
rawSocket = null
source = null
sink = null
handshake = null
protocol = null
http2Connection = null
eventListener.connectFailed(call, route.socketAddress, route.proxy, null, e)
if (routeException == null) {
routeException = RouteException(e)
} else {
routeException.addConnectException(e)
}
if (!connectionRetryEnabled || !connectionSpecSelector.connectionFailed(e)) {
throw routeException
}
}
}
if (route.requiresTunnel() && rawSocket == null) {
throw RouteException(ProtocolException(
"Too many tunnel connections attempted: $MAX_TUNNEL_ATTEMPTS"))
}
val http2Connection = this.http2Connection
if (http2Connection != null) {
synchronized(connectionPool) {
allocationLimit = http2Connection.maxConcurrentStreams()
}
}
}
再来看看具体如何实现
connectSocket()方法
@Throws(IOException::class)
private fun connectSocket(
connectTimeout: Int,
readTimeout: Int,
call: Call,
eventListener: EventListener
) {
val proxy = route.proxy
val address = route.address
// 根据代理类型选择socket类型是代理还是直连
// 首先先判断连接类型,如果是直连或者HTTP连接就直连,否则的话走Socket代理
val rawSocket = when (proxy.type()) {
Proxy.Type.DIRECT, Proxy.Type.HTTP -> address.socketFactory.createSocket()!!
else -> Socket(proxy)
}
this.rawSocket = rawSocket
//然后通过eventListener.connectStart()方法创建连接,
//再设定超时->完成连接->创建用于I/O的source和sink
eventListener.connectStart(call, route.socketAddress, proxy)
rawSocket.soTimeout = readTimeout
try {
// 连接socket,之所以这样写是因为支持不同的平台
// 里面实际上是 socket.connect(address, connectTimeout);
Platform.get().connectSocket(rawSocket, route.socketAddress, connectTimeout)
} catch (e: ConnectException) {
throw ConnectException("Failed to connect to ${route.socketAddress}").apply {
initCause(e)
}
}
// 下面的Try/Catch块是一种避免Android 7.0崩溃的伪黑客方法
// More details:
// https://github.com/square/okhttp/issues/3245
// https://android-review.googlesource.com/#/c/271775/
try {
// 得到输入/输出流
source = rawSocket.source().buffer()
sink = rawSocket.sink().buffer()
} catch (npe: NullPointerException) {
if (npe.message == NPE_THROW_WITH_NULL) {
throw IOException(npe)
}
}
}
connectTunnel()方法
//先创建隧道请求,然后建立socket连接,再发送请求建立隧道。
@Throws(IOException::class)
private fun connectTunnel(
connectTimeout: Int,
readTimeout: Int,
writeTimeout: Int,
call: Call,
eventListener: EventListener
) {
var tunnelRequest: Request = createTunnelRequest()
val url = tunnelRequest.url
for (i in 0 until MAX_TUNNEL_ATTEMPTS) {
connectSocket(connectTimeout, readTimeout, call, eventListener)
tunnelRequest = createTunnel(readTimeout, writeTimeout, tunnelRequest, url)
?: break // 已成功创建隧道。
// 代理决定在身份验证质询后关闭连接。
// 我们需要创建一个新的连接,但这次需要使用身份验证凭据。
rawSocket?.closeQuietly()
rawSocket = null
sink = null
source = null
eventListener.connectEnd(call, route.socketAddress, route.proxy, null)
}
}
3.请求流程
同步请求的操作
异步请求的操作