86.OkHttp3源码解析

本文旨在用简短的代码块完成对OkHttp3大体流程的解析,不求甚解,适可而止,以防走丢(っ•̀ω•́)っ✎⁾⁾

implementation("com.squareup.okhttp3:okhttp:3.12.0")
OkHttpClient okHttpClient = new OkHttpClient.Builder().build();
Request request = new Request.Builder().url("https://www.baidu.com").build();
Call call = mOkHttpClient.newCall(request);
try {
    Response response = call.execute();
    Log.e("-->", response.body().string());
} catch (IOException e) {
    e.printStackTrace();
}

先来回顾一下最基本的用法,这是同步请求,异步请求就是:

call.enqueue(new Callback() {
    @Override
    public void onFailure(Call call, IOException e) {
    }
    @Override
    public void onResponse(Call call, Response response) throws IOException {
    }
});

1.构建OkHttpClient实例
源码里是一系列的参数初始化,如dispatcher等

2.构建Request实例
对请求信息的参数初始化,如url地址、方法(get、post等)、header、body等

3.同步请求call.execute()

@Override 
public Response execute() throws IOException {
	try {
	    client.dispatcher().executed(this);
	    Response result = getResponseWithInterceptorChain();
	    if (result == null) throw new IOException("Canceled");
	    return result;
	} catch (IOException e) {
	    e = timeoutExit(e);
	    eventListener.callFailed(this, e);
	    throw e;
	} finally {
	    client.dispatcher().finished(this);
	}
}

适当精简一下代码,这里出现了第一步构建OkHttpClient时初始化的dispatcher,追踪executed方法

/** Running synchronous calls. Includes canceled calls that haven't finished yet. */
private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
synchronized void executed(RealCall call) {
    runningSyncCalls.add(call);
}

这是把该请求添加到正在运行的同步队列中,而上一步的getResponseWithInterceptorChain方法是构建一个拦截器链,返回Response结果,会在下文重点解析

4.异步请求call.enqueue(Callback responseCallback)

@Override
public void enqueue(Callback responseCallback) {
    client.dispatcher().enqueue(new AsyncCall(responseCallback));
}

还是dispatcher,追踪enqueue方法:

/** Ready async calls in the order they'll be run. */
private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
/** Running asynchronous calls. Includes canceled calls that haven't finished yet. */
private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();

void enqueue(AsyncCall call) {
    synchronized (this) {
        readyAsyncCalls.add(call);
    }
    promoteAndExecute();
}

private boolean promoteAndExecute() {
    List<AsyncCall> executableCalls = new ArrayList<>();
    boolean isRunning;
    synchronized (this) {
        for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
            AsyncCall asyncCall = i.next();
            if (runningAsyncCalls.size() >= maxRequests) break; // Max capacity.
            if (runningCallsForHost(asyncCall) >= maxRequestsPerHost) continue; // Host max capacity.
            i.remove();
            executableCalls.add(asyncCall);
            runningAsyncCalls.add(asyncCall);
        }
        isRunning = runningCallsCount() > 0;
    }
    for (int i = 0, size = executableCalls.size(); i < size; i++) {
        AsyncCall asyncCall = executableCalls.get(i);
        asyncCall.executeOn(executorService());
    }
    return isRunning;
}

首先将请求加入到准备请求队列readyAsyncCalls中,然后在promoteAndExecute方法中遍历准备请求队列,如果正在请求队列runningAsyncCalls小于支持的最大请求数maxRequests,也就是说还有位置的话,就把准备请求队列添加到正在请求队列中。追踪executorService()方法:

public synchronized ExecutorService executorService() {
    if (executorService == null) {
        executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS, 
            new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));
    }
    return executorService;
}

会创建一个线程池,第一个参数0,代表核心线程数为0个,也就是当线程工作结束后会清除所有线程;第二个参数Integer的最大值,代表理论上尽可能多的创建线程,但是受之前判断中maxRequests的限制,超过maxRequests就不会再创建线程;第三个参数60代表线程空闲60s之后会被销毁。
回过头来再看AsyncCall这个类:

final class AsyncCall extends NamedRunnable {
    ...
}
public abstract class NamedRunnable implements Runnable {
    @Override 
    public final void run() {
        String oldName = Thread.currentThread().getName();
        Thread.currentThread().setName(name);
        try {
            execute();
        } finally {
            Thread.currentThread().setName(oldName);
        }
    }

    protected abstract void execute();
}

实现Runnable重写run()方法,向下查找execute的实现类,回到RealCall类中:

@Override 
protected void execute() {
    boolean signalledCallback = false;
    timeout.enter();
    try {
        Response response = getResponseWithInterceptorChain();
        if (retryAndFollowUpInterceptor.isCanceled()) {
            signalledCallback = true;
            responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
        } else {
            signalledCallback = true;
            responseCallback.onResponse(RealCall.this, response);
        }
    } catch (IOException e) {
        e = timeoutExit(e);
        if (signalledCallback) {
            // Do not signal the callback twice!
            Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);
        } else {
            eventListener.callFailed(RealCall.this, e);
            responseCallback.onFailure(RealCall.this, e);
        }
    } finally {
        client.dispatcher().finished(this);
    }
}

又看见了眼熟的getResponseWithInterceptorChain()方法,返回Response,然后是对Response结果的回调

5.拦截器链getResponseWithInterceptorChain()
拦截器是OkHttp中提供的一种强大机制,它可以实现网络监听、请求以及响应重写、请求失败重试等功能

Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    List<Interceptor> interceptors = new ArrayList<>();
    interceptors.addAll(client.interceptors());
    interceptors.add(retryAndFollowUpInterceptor);
    interceptors.add(new BridgeInterceptor(client.cookieJar()));
    interceptors.add(new CacheInterceptor(client.internalCache()));
    interceptors.add(new ConnectInterceptor(client));
    if (!forWebSocket) {
      interceptors.addAll(client.networkInterceptors());
    }
    interceptors.add(new CallServerInterceptor(forWebSocket));
    Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
        originalRequest, this, eventListener, client.connectTimeoutMillis(),
        client.readTimeoutMillis(), client.writeTimeoutMillis());
    return chain.proceed(originalRequest);
}

该方法创建了一系列的拦截器添加到拦截器集合中,并创建拦截器链RealInterceptorChain,最后调用proceed方法

public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec, 
		RealConnection connection) throws IOException {
    // Call the next interceptor in the chain.
    RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
        connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
        writeTimeout);
    Interceptor interceptor = interceptors.get(index);
    Response response = interceptor.intercept(next);
    return response;

在这再次创建拦截器链,并且index+1,在intercept中去请求,返回response,一种类似于递归的思想,使得这几个拦截器串成链。
追踪intercept方法,是Interceptor接口的抽象方法

public interface Interceptor {
    Response intercept(Chain chain) throws IOException;
}

向下查找实现,这里有OkHttp实现好的五种拦截器
86.OkHttp3源码解析_第1张图片
按照拦截器集合的添加顺序,先看RetryAndFollowUpInterceptor重定向拦截器,用于网络请求失败重连功能:

@Override
public Response intercept(Chain chain) throws IOException {
    StreamAllocation streamAllocation = new StreamAllocation(client.connectionPool(),
        createAddress(request.url()), call, eventListener, callStackTrace);
    int followUpCount = 0;
    Response priorResponse = null;
    Response response;
    while (true) {
    	try {
            response = realChain.proceed(request, streamAllocation, null, null);
		} catch (RouteException e) {
		} catch (IOException e) {
		}
		Request followUp;
        try {
            followUp = followUpRequest(response, streamAllocation.route());
        } catch (IOException e) {
            streamAllocation.release();
            throw e;
        }
        if (followUp == null) {
            streamAllocation.release();
            return response;
        }
		if (++followUpCount > MAX_FOLLOW_UPS) {//重连超过一定次数,则释放资源,停止请求
        	streamAllocation.release();
        	throw new ProtocolException("Too many follow-up requests: " + followUpCount);
      	}
    }
}

创建StreamAllocation网络请求所需要的组件,调用proceed创建下一个拦截器,根据异常结果或响应结果判断是否重新请求。

BridgeInterceptor桥接拦截器给request添加头部信息header

@Override
public Response intercept(Chain chain) throws IOException {
    Request userRequest = chain.request();
    Request.Builder requestBuilder = userRequest.newBuilder();

    RequestBody body = userRequest.body();
    if (body != null) {
      MediaType contentType = body.contentType();
      if (contentType != null) {
        requestBuilder.header("Content-Type", contentType.toString());
      }

      long contentLength = body.contentLength();
      if (contentLength != -1) {
        requestBuilder.header("Content-Length", Long.toString(contentLength));
        requestBuilder.removeHeader("Transfer-Encoding");
      } else {
        requestBuilder.header("Transfer-Encoding", "chunked");
        requestBuilder.removeHeader("Content-Length");
      }
    }

    if (userRequest.header("Host") == null) {
      requestBuilder.header("Host", hostHeader(userRequest.url(), false));
    }

    if (userRequest.header("Connection") == null) {
      requestBuilder.header("Connection", "Keep-Alive");
    }

    // If we add an "Accept-Encoding: gzip" header field we're responsible for also decompressing
    // the transfer stream.
    boolean transparentGzip = false;
    if (userRequest.header("Accept-Encoding") == null && userRequest.header("Range") == null) {
      transparentGzip = true;
      requestBuilder.header("Accept-Encoding", "gzip");
    }

    List<Cookie> cookies = cookieJar.loadForRequest(userRequest.url());
    if (!cookies.isEmpty()) {
      requestBuilder.header("Cookie", cookieHeader(cookies));
    }

    if (userRequest.header("User-Agent") == null) {
      requestBuilder.header("User-Agent", Version.userAgent());
    }

    Response networkResponse = chain.proceed(requestBuilder.build());

    HttpHeaders.receiveHeaders(cookieJar, userRequest.url(), networkResponse.headers());

    Response.Builder responseBuilder = networkResponse.newBuilder()
        .request(userRequest);

    if (transparentGzip
        && "gzip".equalsIgnoreCase(networkResponse.header("Content-Encoding"))
        && HttpHeaders.hasBody(networkResponse)) {
      GzipSource responseBody = new GzipSource(networkResponse.body().source());
      Headers strippedHeaders = networkResponse.headers().newBuilder()
          .removeAll("Content-Encoding")
          .removeAll("Content-Length")
          .build();
      responseBuilder.headers(strippedHeaders);
      String contentType = networkResponse.header("Content-Type");
      responseBuilder.body(new RealResponseBody(contentType, -1L, Okio.buffer(responseBody)));
    }
    return responseBuilder.build();
}

负责将用户构建的一个Request请求转化为能够进行网络访问的请求,并将服务端返回的Response转化为用户可用的Response,如gzip压缩和解压。

CacheInterceptor缓存拦截器
先看一下OkHttp缓存的使用方法:

Cache cache = new Cache(new File(getCacheDir(), "cache"), 10 * 1024 * 1024);//10MB
OkHttpClient client = new OkHttpClient.Builder().cache(cache).build();

进入Cache类,发现使用的是DiskLruCache

public Cache(File directory, long maxSize) {
    this(directory, maxSize, FileSystem.SYSTEM);
}
Cache(File directory, long maxSize, FileSystem fileSystem) {
    this.cache = DiskLruCache.create(fileSystem, directory, VERSION, ENTRY_COUNT, maxSize);
}

先看一下Cache的put方法

@Nullable 
CacheRequest put(Response response) {
    String requestMethod = response.request().method();
    if (HttpMethod.invalidatesCache(response.request().method())) {
        try {
            remove(response.request());
        } catch (IOException ignored) {
            // The cache cannot be written.
        }
        return null;
    }
    if (!requestMethod.equals("GET")) {
        // Don't cache non-GET responses. We're technically allowed to cache
        // HEAD requests and some POST requests, but the complexity of doing
        // so is high and the benefit is low.
        return null;
    }

    if (HttpHeaders.hasVaryAll(response)) {
        return null;
    }

    Entry entry = new Entry(response);
    DiskLruCache.Editor editor = null;
    try {
        editor = cache.edit(key(response.request().url()));
        if (editor == null) {
            return null;
        }
        entry.writeTo(editor);
        return new CacheRequestImpl(editor);
    } catch (IOException e) {
        abortQuietly(editor);
        return null;
    }
}

首先获取到请求的方法,进HttpMethod.invalidatesCache方法里

public static boolean invalidatesCache(String method) {
    return method.equals("POST")
        || method.equals("PATCH")
        || method.equals("PUT")
        || method.equals("DELETE")
        || method.equals("MOVE");     // WebDAV
}

得知如果方法是这几个的话,就会把该请求的缓存remove掉
然后判断方法是不是“GET”,如果不是get方法,该请求就不会缓存
然后判断HttpHeaders.hasVaryAll方法

public static boolean hasVaryAll(Headers responseHeaders) {
    return varyFields(responseHeaders).contains("*");
}

header带“*”的也不缓存
接下来创建Entry对象,它持有response的一系列参数:

Entry(Response response) {
      this.url = response.request().url().toString();
      this.varyHeaders = HttpHeaders.varyHeaders(response);
      this.requestMethod = response.request().method();
      this.protocol = response.protocol();
      this.code = response.code();
      this.message = response.message();
      this.responseHeaders = response.headers();
      this.handshake = response.handshake();
      this.sentRequestMillis = response.sentRequestAtMillis();
      this.receivedResponseMillis = response.receivedResponseAtMillis();
}

得到一个editor进行写入缓存操作

public void writeTo(DiskLruCache.Editor editor) throws IOException {
    BufferedSink sink = Okio.buffer(editor.newSink(ENTRY_METADATA));
    sink.writeUtf8(url).writeByte('\n');
    sink.writeUtf8(requestMethod).writeByte('\n');
    sink.writeDecimalLong(varyHeaders.size()).writeByte('\n');
    for (int i = 0, size = varyHeaders.size(); i < size; i++) {
        sink.writeUtf8(varyHeaders.name(i)).writeUtf8(": ").writeUtf8(varyHeaders.value(i)).writeByte('\n');
    }
    sink.writeUtf8(new StatusLine(protocol, code, message).toString()).writeByte('\n');
    sink.writeDecimalLong(responseHeaders.size() + 2).writeByte('\n');
    for (int i = 0, size = responseHeaders.size(); i < size; i++) {
        sink.writeUtf8(responseHeaders.name(i)).writeUtf8(": ").writeUtf8(responseHeaders.value(i)).writeByte('\n');
    }
    sink.writeUtf8(SENT_MILLIS).writeUtf8(": ").writeDecimalLong(sentRequestMillis).writeByte('\n');
    sink.writeUtf8(RECEIVED_MILLIS).writeUtf8(": ").writeDecimalLong(receivedResponseMillis).writeByte('\n');
    
    if (isHttps()) {
        sink.writeByte('\n');
        sink.writeUtf8(handshake.cipherSuite().javaName()).writeByte('\n');
        writeCertList(sink, handshake.peerCertificates());
        writeCertList(sink, handshake.localCertificates());
        sink.writeUtf8(handshake.tlsVersion().javaName()).writeByte('\n');
    }
    sink.close();
}

再来看Cache的get方法:

@Nullable
Response get(Request request) {
    String key = key(request.url());
    DiskLruCache.Snapshot snapshot;
    Entry entry;
    try {
        snapshot = cache.get(key);
        if (snapshot == null) {
            return null;
        }
    } catch (IOException e) {
        // Give up because the cache cannot be read.
        return null;
    }
    try {
        entry = new Entry(snapshot.getSource(ENTRY_METADATA));
    } catch (IOException e) {
        Util.closeQuietly(snapshot);
        return null;
    }
    Response response = entry.response(snapshot);
    if (!entry.matches(request, response)) {
        Util.closeQuietly(response.body());
        return null;
    }
    return response;
}

看完了Cache的添加获取缓存,接下来看CacheInterceptor的intercept方法:

@Override public Response intercept(Chain chain) throws IOException {
    Response cacheCandidate = cache != null
        ? cache.get(chain.request())
        : null;
    long now = System.currentTimeMillis();
    CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();
    Request networkRequest = strategy.networkRequest;
    Response cacheResponse = strategy.cacheResponse;
    if (cache != null) {
      cache.trackResponse(strategy);
    }
    if (cacheCandidate != null && cacheResponse == null) {
      closeQuietly(cacheCandidate.body()); // The cache candidate wasn't applicable. Close it.
    }
    // If we're forbidden from using the network and the cache is insufficient, fail.
    if (networkRequest == null && cacheResponse == null) {
      return new Response.Builder()
          .request(chain.request())
          .protocol(Protocol.HTTP_1_1)
          .code(504)
          .message("Unsatisfiable Request (only-if-cached)")
          .body(Util.EMPTY_RESPONSE)
          .sentRequestAtMillis(-1L)
          .receivedResponseAtMillis(System.currentTimeMillis())
          .build();
    }
    // If we don't need the network, we're done.
    if (networkRequest == null) {
      return cacheResponse.newBuilder()
          .cacheResponse(stripBody(cacheResponse))
          .build();
    }

    Response networkResponse = null;
    try {
      networkResponse = chain.proceed(networkRequest);
    } finally {
      // If we're crashing on I/O or otherwise, don't leak the cache body.
      if (networkResponse == null && cacheCandidate != null) {
        closeQuietly(cacheCandidate.body());
      }
    }
    // If we have a cache response too, then we're doing a conditional get.
    if (cacheResponse != null) {
      if (networkResponse.code() == HTTP_NOT_MODIFIED) {
        Response response = cacheResponse.newBuilder()
            .headers(combine(cacheResponse.headers(), networkResponse.headers()))
            .sentRequestAtMillis(networkResponse.sentRequestAtMillis())
            .receivedResponseAtMillis(networkResponse.receivedResponseAtMillis())
            .cacheResponse(stripBody(cacheResponse))
            .networkResponse(stripBody(networkResponse))
            .build();
        networkResponse.body().close();
        // Update the cache after combining headers but before stripping the
        // Content-Encoding header (as performed by initContentStream()).
        cache.trackConditionalCacheHit();
        cache.update(cacheResponse, response);
        return response;
      } else {
        closeQuietly(cacheResponse.body());
      }
    }
    Response response = networkResponse.newBuilder()
        .cacheResponse(stripBody(cacheResponse))
        .networkResponse(stripBody(networkResponse))
        .build();
    if (cache != null) {
      if (HttpHeaders.hasBody(response) && CacheStrategy.isCacheable(response, networkRequest)) {
        // Offer this request to the cache.
        CacheRequest cacheRequest = cache.put(response);
        return cacheWritingResponse(cacheRequest, response);
      }
      if (HttpMethod.invalidatesCache(networkRequest.method())) {
        try {
          cache.remove(networkRequest);
        } catch (IOException ignored) {
          // The cache cannot be written.
        }
      }
    }
    return response;
}

先拿到网络请求networkRequest和缓存响应cacheResponse,有以下几种判断:
如果网络请求和缓存响应都为空,那么报一个504的错误,拦截器链终止;
如果网络请求为空,那就把缓存响应返回,拦截器链终止;否则调用下一个拦截器;
如果缓存响应不为空,并且网络响应结果码是HTTP_NOT_MODIFIED,就返回缓存并更新;
如果网络响应有响应体并且可以缓存,就把缓存写进去,把网络响应返回;
最后,如果网络请求方法不是get,就删除这个请求的缓存。

ConnectInterceptor连接拦截器

@Override
public Response intercept(Chain chain) throws IOException {
    RealInterceptorChain realChain = (RealInterceptorChain) chain;
    Request request = realChain.request();
    StreamAllocation streamAllocation = realChain.streamAllocation();

    // We need the network to satisfy this request. Possibly for validating a conditional GET.
    boolean doExtensiveHealthChecks = !request.method().equals("GET");
    HttpCodec httpCodec = streamAllocation.newStream(client, chain, doExtensiveHealthChecks);
    RealConnection connection = streamAllocation.connection();

    return realChain.proceed(request, streamAllocation, httpCodec, connection);
}

StreamAllocation是之前在第一个重定向拦截器RetryAndFollowUpInterceptor创建的,HttpCodec对象用于处理request和response,RealConnection用于网络请求io传输,调用proceed将这几个对象传递给下一个拦截器。

CallServerInterceptor发送请求,读取响应
算了,不写了,要命了
在这里插入图片描述

你可能感兴趣的:(Android,源码,OkHttp,源码)