Android框架第(四)篇---OKHttp的源码分析

**版权声明:本文为小斑马伟原创文章,转载请注明出处!

Android框架第(四)篇---OKHttp的源码分析_第1张图片

在我们的Android框架第三篇中,我们讲到OkHttp的几大特点分析出OKHttp网络框架的好处。为什么这样说它有这些特点呢?我们从源码中分析OKHttp网络请求流程、OKHttp的任务调度机制、OKHttp中拦截器的作用、OKHttp的连接池复用机制几方面可以得出所需要的答案。

一、 enqueue源码分析

当我们要进行网络请求的时候,我们需要调用OkHttpClient里面的newCall方法。通过调用这个方法,我们得到Call对象,使用这个对象,我们能进行异步请求或者是同步请求。

@Override
public void enqueue(Callback responseCallback) {
    enqueue(responseCallback, false);
}

void enqueue(Callback responseCallback, boolean forWebSocket) {
    synchronized (this) {
       if (executed) throw new IllegalStateException("Already Executed");
          executed = true;
       }
     client.dispatcher().enqueue(new AsyncCall(responseCallback,   forWebSocket));
}

从enqueue方法的源码中,可以看下最后面的dispatcher()负责网络请求操作。Dispatcher: 主要控制并发的请求 。也是OKHttp的任务调度机制,主要用于控制并发的请求。

二、 Dispatcher类(OKHttp的任务调度机制,主要用于控制并发的请求)
private int maxRequests = 64;
private int maxRequestsPerHost = 5;

/** Executes calls. Created lazily. */
private ExecutorService executorService;

/** Ready async calls in the order they'll be run. */
private final Deque readyAsyncCalls = new ArrayDeque<>();

/** Running asynchronous calls. Includes canceled calls that haven't   finished yet. */
private final Deque runningAsyncCalls = new ArrayDeque<>();

/** Running synchronous calls. Includes canceled calls that haven't   finished yet. */
private final Deque runningSyncCalls = new ArrayDeque<>();
  • 1.maxRequests:表示最大变化请求数和maxRequestsPerHost每个主机最大变化请求数
  • 2.ExecutorService:消费者线程池 因为它的任务调动机制是基于生产者于消费者模型
  • 3.readyAsyncCalls:将要运行的异步请求队列
  • 4.runningAsyncCalls: 正在运行异步请求队列
  • 5.runningSyncCalls:正在运行的同步请求队列

Dispatcher().enqueue:当正在运行的异步请求队列的数量小于64,并且正在请求的主机书小于50,那么就把请求加到 runningAsyncCalls中,并在线程池中去执行。否则就加入 readyAsyncCalls中进行缓存等待。线程中传过来的AsyncCall 是RealCall的内部类,其内部也实现了execute()方法。

synchronized void enqueue(AsyncCall call) {
    if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
       runningAsyncCalls.add(call);
       executorService().execute(call);
    } else {
       readyAsyncCalls.add(call);
    }
}

其中RealCall类中的execuet方法执行完后都会执行Dispatcher里面的finished方法

synchronized void finished(AsyncCall call) {
    if (!runningAsyncCalls.remove(call)) throw new AssertionError("AsyncCall wasn't running!");
         promoteCalls();
    }

在runningAsyncCalls移除后,还执行了promoteCalls()方法。该方法遍历将要将要运行的异步请求队列,取出下一个请求加入到runningAsyncCalls中,然后把它交给线程池去运行。

private void promoteCalls() {
     if (runningAsyncCalls.size() >= maxRequests) return; // Already running max capacity.
     if (readyAsyncCalls.isEmpty()) return; // No ready calls to promote.

     for (Iterator i = readyAsyncCalls.iterator(); i.hasNext(); ) { //遍历将要将要运行的异步请求队列,取出下一个请求加入到  runningAsyncCalls中,然后把它交给线程池去运行。
           AsyncCall call = i.next();
          if (runningCallsForHost(call) < maxRequestsPerHost) {
              i.remove();
              runningAsyncCalls.add(call);       //加入正在运行的异步请求队列
              executorService().execute(call);  //线程池运行
          }

    if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity.
  }
}

接着回到RealCall中的execute方法中的第一句,有一个函数叫getResponseWithInterceptorChain。从拦截器链表中获取一个Response对象,很明显这句话就是在执行网络请求。

@Override protected void execute() {
  boolean signalledCallback = false;
  try {
    Response response = getResponseWithInterceptorChain(forWebSocket); //执行网络请求
    if (canceled) {
      signalledCallback = true;
      responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
    } else {
      signalledCallback = true;
      responseCallback.onResponse(RealCall.this, response);
    }
  } catch (IOException e) {
    if (signalledCallback) {
      // Do not signal the callback twice!
      logger.log(Level.INFO, "Callback failure for " + toLoggableString(), e);
    } else {
      responseCallback.onFailure(RealCall.this, e);
    }
  } finally {
    client.dispatcher().finished(this);
  }
  }
}

在这个方法中,创建了一个Application拦截器琏,是RealCall的一个内部类,然后执行拦截器链的proceed方法 (拦截器拼接请求)

private Response getResponseWithInterceptorChain(boolean forWebSocket) throws IOException {
Interceptor.Chain chain = new ApplicationInterceptorChain(0, originalRequest, forWebSocket);
return chain.proceed(originalRequest);

ApplicationInterceptorChain :Application拦截器链是RealCall的一个内部类。每次从拦截气器垫中取出一个拦截器,如果当我们出现多个拦截器时,会在此位置上进行阻塞,并等待下一个拦截器调用返回。

拦截器:是一种能够监控于重写,重试调用的机制,在通常的情况下,拦截器用来添加和移除转换请求和响应的头部信息,比如将域名替换IP地址,在请求途中,添加host属性,也可以添加一些我们应用中的公共参数,比如设备的ID和版本号等等。

@Override public Response proceed(Request request) throws IOException {
  // If there's another interceptor in the chain, call that.
  if (index < client.interceptors().size()) {
    Interceptor.Chain chain = new ApplicationInterceptorChain(index + 1, request, forWebSocket);
    Interceptor interceptor = client.interceptors().get(index); //每次从拦截气垫中取出一个拦截器
    Response interceptedResponse = interceptor.intercept(chain); //如果当我们出现多个拦截器时,会在此位置上进行阻塞,并等待下一个拦截器调用返回:
 拦截器:是一种能够监控于重写,重试调用的机制,在通常的情况下,拦截器用来添加和移除转换请求和响应的头部信息,比如将域名替换IP地址,在请求途中,添加host属性,也可以添加一些我们应用中的公共参数,比如设备的ID和版本号等等

    if (interceptedResponse == null) {
      throw new NullPointerException("application interceptor " + interceptor
          + " returned null");
    }

    return interceptedResponse;
  }

  // No more interceptors. Do HTTP.
  return getResponse(request, forWebSocket); //调用getResponse方法,在没有更多拦截器的话 执行网络请求 
  }
}

也就是说在没有更多的拦截器的话,就会执行网络请求。这个方法比较长,在这个方法中我们看到HttpEngine对象。并且调用HttpEngine对象的sendRequest方法(发送网络请求)和readResponse方法(读取响应结果) (通过HttpEngine发起请求)。

Response getResponse(Request request, boolean forWebSocket) throws IOException {
// Copy body metadata to the appropriate request headers.
RequestBody body = request.body();
if (body != null) {
  Request.Builder requestBuilder = request.newBuilder();

  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");
  }

  request = requestBuilder.build();
}

// Create the initial HTTP engine. Retries and redirects need new engine for each attempt.
engine = new HttpEngine(client, request, false, false, forWebSocket, null, null, null);

int followUpCount = 0;
while (true) {
  if (canceled) {
    engine.releaseStreamAllocation();
    throw new IOException("Canceled");
  }

  boolean releaseConnection = true;
  try {
    engine.sendRequest();
    engine.readResponse();
    releaseConnection = false;
  } catch (RequestException e) {
    // The attempt to interpret the request failed. Give up.
    throw e.getCause();
  } catch (RouteException e) {
    // The attempt to connect via a route failed. The request will not have been sent.
    HttpEngine retryEngine = engine.recover(e.getLastConnectException(), null);
    if (retryEngine != null) {
      releaseConnection = false;
      engine = retryEngine;
      continue;
    }
    // Give up; recovery is not possible.
    throw e.getLastConnectException();
  } catch (IOException e) {
    // An attempt to communicate with a server failed. The request may have been sent.
    HttpEngine retryEngine = engine.recover(e, null); //做了HttpEngine的恢复方法
    if (retryEngine != null) {
      releaseConnection = false;
      engine = retryEngine;
      continue;
    }

    // Give up; recovery is not possible.
    throw e;
  } finally {
    // We're throwing an unchecked exception. Release any resources.
    if (releaseConnection) {
      StreamAllocation streamAllocation = engine.close();
      streamAllocation.release();
    }
  }

  Response response = engine.getResponse();
  Request followUp = engine.followUpRequest();

  if (followUp == null) {
    if (!forWebSocket) {
      engine.releaseStreamAllocation();
    }
    return response;
  }

  StreamAllocation streamAllocation = engine.close();

  if (++followUpCount > MAX_FOLLOW_UPS) {
    streamAllocation.release();
    throw new ProtocolException("Too many follow-up requests: " + followUpCount);
  }

  if (!engine.sameConnection(followUp.url())) {
    streamAllocation.release();
    streamAllocation = null;
  }

  request = followUp;
  engine = new HttpEngine(client, request, false, false, forWebSocket, streamAllocation, null,
      response);
     }
  }
}

HttpEngine发起请求:sendRequest方法:发起网络请求,最主要的是做了缓存的策略。首先创建一个Request对象。获取OkHttpClient对象中的Cache,同时Cache在初始化过程中,会读取缓存中的曾经请求过的信息。上一次与服务器交互的responseCache (这里的缓存都是基于Map的,key是请求中的url的MD5,value是文件中查询到的缓存,页面接环是基于ROU算法的。cacheCandidate是可以读取缓存数据,根据缓存策略可以得到 networkRequest 和 cacheResponse两个值。根据这两个值是否为null,来进行处理。

public void sendRequest() throws RequestException, RouteException, IOException {
if (cacheStrategy != null) return; // Already sent.
if (httpStream != null) throw new IllegalStateException();

Request request = networkRequest(userRequest); //创建一个Request对象,

InternalCache responseCache = Internal.instance.internalCache(client);//获取OkHttpClient对象中的Cache,同时Cache在初始化过程中,会读取缓存中的曾经请求过的信息
Response cacheCandidate = responseCache != null  //上一次与服务器交互的responseCache (这里的缓存都是基于Map的,key是请求中的url的MD5,value是文件中查询到的缓存,页面接环是基于ROU算法的
    ? responseCache.get(request)  //cacheCandidate是可以读取缓存数据,根据缓存策略可以得到 networkRequest 和  cacheResponse两个值。根据这两个值是否为null,来进行处理。当我们不进行网络请求,并且缓存不存在过期时 回504的错误。
    : null;

long now = System.currentTimeMillis();
cacheStrategy = new CacheStrategy.Factory(now, request, cacheCandidate).get();
networkRequest = cacheStrategy.networkRequest; //就是我们要发起的网络请求
cacheResponse = cacheStrategy.cacheResponse; //缓存的响应

if (responseCache != null) {
  responseCache.trackResponse(cacheStrategy);//记录当前的请求是网络发起的请求还是缓存发起的请求
}

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) { //当我们不进行网络请求,并且缓存不存在过期时 回504的错误。
  userResponse = new Response.Builder()
      .request(userRequest)
      .priorResponse(stripBody(priorResponse))
      .protocol(Protocol.HTTP_1_1)
      .code(504)
      .message("Unsatisfiable Request (only-if-cached)")
      .body(EMPTY_BODY)
      .build();
  return;
}

// If we don't need the network, we're done.
if (networkRequest == null) { //表示不进行网络请求,并且缓存可以用时,那么直接回缓存的数据
  userResponse = cacheResponse.newBuilder()
      .request(userRequest)
      .priorResponse(stripBody(priorResponse))
      .cacheResponse(stripBody(cacheResponse))
      .build();
  userResponse = unzip(userResponse);
  return;
}

// We need the network to satisfy this request. Possibly for validating a conditional GET.
boolean success = false;
try {   //在其他的情况下,发起网络访问。
  httpStream = connect();
  httpStream.setHttpEngine(this);

  if (writeRequestHeadersEagerly()) {
    long contentLength = OkHeaders.contentLength(request);
    if (bufferRequestBody) {
      if (contentLength > Integer.MAX_VALUE) {
        throw new IllegalStateException("Use setFixedLengthStreamingMode() or "
            + "setChunkedStreamingMode() for requests larger than 2 GiB.");
      }

      if (contentLength != -1) {
        // Buffer a request body of a known length.
        httpStream.writeRequestHeaders(networkRequest);
        requestBodyOut = new RetryableSink((int) contentLength);
      } else {
        // Buffer a request body of an unknown length. Don't write request headers until the
        // entire body is ready; otherwise we can't set the Content-Length header correctly.
        requestBodyOut = new RetryableSink();
      }
    } else {
      httpStream.writeRequestHeaders(networkRequest);
      requestBodyOut = httpStream.createRequestBody(networkRequest, contentLength);
    }
  }
  success = true;
} finally {
  // If we're crashing on I/O or otherwise, don't leak the cache body.
  if (!success && cacheCandidate != null) {
    closeQuietly(cacheCandidate.body());
  }
}
}

readResponse():这个方法就是解析HTTP响应报头的,如果有缓存,并且可用,则用缓存数据,并且更新缓存数据。否则就用网络返回的数据

public void readResponse() throws IOException {
 if (userResponse != null) {
     return; // Already ready.
}
if (networkRequest == null && cacheResponse == null) {
  throw new IllegalStateException("call sendRequest() first!");
}
if (networkRequest == null) {
  return; // No network response to read.
}

Response networkResponse;

if (forWebSocket) {
  httpStream.writeRequestHeaders(networkRequest);
  networkResponse = readNetworkResponse();
} else if (!callerWritesRequestBody) {
  networkResponse = new NetworkInterceptorChain(0, networkRequest).proceed(networkRequest);
} else {
  // Emit the request body's buffer so that everything is in requestBodyOut.
  if (bufferedRequestBody != null && bufferedRequestBody.buffer().size() > 0) {
    bufferedRequestBody.emit();
  }

  // Emit the request headers if we haven't yet. We might have just learned the Content-Length.
  if (sentRequestMillis == -1) {
    if (OkHeaders.contentLength(networkRequest) == -1
        && requestBodyOut instanceof RetryableSink) {
      long contentLength = ((RetryableSink) requestBodyOut).contentLength();
      networkRequest = networkRequest.newBuilder()
          .header("Content-Length", Long.toString(contentLength))
          .build();
    }
    httpStream.writeRequestHeaders(networkRequest);
  }

  // Write the request body to the socket.
  if (requestBodyOut != null) {
    if (bufferedRequestBody != null) {
      // This also closes the wrapped requestBodyOut.
      bufferedRequestBody.close();
    } else {
      requestBodyOut.close();
    }
    if (requestBodyOut instanceof RetryableSink) {
      httpStream.writeRequestBody((RetryableSink) requestBodyOut);
    }
  }

  networkResponse = readNetworkResponse(); //读取了网络的响应
}

receiveHeaders(networkResponse.headers()); //接受了网络的头部信息

// If we have a cache response too, then we're doing a conditional get.
if (cacheResponse != null) { 
  if (validate(cacheResponse, networkResponse)) { //检查了缓存是否可用
    userResponse = cacheResponse.newBuilder() //如果可用,就用newBuilder对象
        .request(userRequest)
        .priorResponse(stripBody(priorResponse))
        .headers(combine(cacheResponse.headers(), networkResponse.headers()))
        .cacheResponse(stripBody(cacheResponse))
        .networkResponse(stripBody(networkResponse))
        .build();
    networkResponse.body().close(); //关闭网络连接
    releaseStreamAllocation(); //释放连接

    // Update the cache after combining headers but before stripping the
    // Content-Encoding header (as performed by initContentStream()).
    InternalCache responseCache = Internal.instance.internalCache(client);
    responseCache.trackConditionalCacheHit();
    responseCache.update(cacheResponse, stripBody(userResponse));
    userResponse = unzip(userResponse);
    return;
  } else {
    closeQuietly(cacheResponse.body());
  }
}

userResponse = networkResponse.newBuilder()
    .request(userRequest)
    .priorResponse(stripBody(priorResponse))
    .cacheResponse(stripBody(cacheResponse))
    .networkResponse(stripBody(networkResponse))
    .build();

if (hasBody(userResponse)) {
  maybeCache();
  userResponse = unzip(cacheWritingResponse(storeRequest, userResponse));
}
}

private static boolean validate(Response cached, Response network) {
if (network.code() == HTTP_NOT_MODIFIED) { //如果服务器返回的304,表示缓存是有效的
  return true;
}

// The HTTP spec says that if the network's response is older than our
// cached response, we may return the cache's response. Like Chrome (but
// unlike Firefox), this client prefers to return the newer response.
Date lastModified = cached.headers().getDate("Last-Modified"); //通过网络请求中的Last-Modified信息,我们来计算是否有最新数据,如果有最新的数据,则表示缓存不可用,如果没有,则表示缓存有效
if (lastModified != null) {
  Date networkLastModified = network.headers().getDate("Last-Modified");
  if (networkLastModified != null
      && networkLastModified.getTime() < lastModified.getTime()) {
    return true;
  }
}

return false;
}

public HttpEngine recover(IOException e, Sink requestBodyOut) {
if (!streamAllocation.recover(e, requestBodyOut)) {
  return null;
}

if (!client.retryOnConnectionFailure()) {
  return null;
}

StreamAllocation streamAllocation = close();

// For failure recovery, use the same route selector with a new connection.
return new HttpEngine(client, userRequest, bufferRequestBody, callerWritesRequestBody, //失败之后,重连机制
    forWebSocket, streamAllocation, (RetryableSink) requestBodyOut, priorResponse);
}
三、OKHttp的网络请求流程

OkHttp网络请求的流程:当我们发起OKHttp网络请求的时候,首先会判断它是同步请求还是异步请求。如果是异步请求,异步线程池执行异步请求线程。之后都会经过拦截器这一块,通过拦截器的机制我们会通过网络的拼接。拼接好了一个请求后,通过HttpEngine对象发起一个请求。发起完请求之后就会有一个缓存机制,如果当前有缓存,读取缓存文件系统。没有缓存读取,则进行网络请求,读取网络请求。直到最后结束。

Android框架第(四)篇---OKHttp的源码分析_第2张图片
Okhttp网路请求流程
四、任务调度机制-Dispatcher

根据生产者和消费者模型理论分析,当入队请求时,如果满足正在运行的请求数量小于64,同时曾在运行的本机请求数量小于50时候,那么就把请求加到 runningAsyncCalls中,并在线程池中去执行。如果消费池满了,那么就把它加入到readyAsyncCalls对象中。进行缓存等待取执行。而当我们任务执行完之后,调用finish方法的promoteCall()方法。手动的移除缓存区。主动清理过程,因此不会发生死锁的。

Android框架第(四)篇---OKHttp的源码分析_第3张图片
Okhttp任务调度机制流程
五、拦截器的作用

拦截器是OKHttp中强大的流程控制装置,主要用来监控LOG,修改请求,修改结果等。OKHttp内部维护了一个Interceptors的List,通过InterceptorChain进行多次拦截修改操作。

六、连接池复用机制

连接池复用机制:当我们进行一个网络请求的时候。首先要打开一个socket链接,然后往里面写入数据,读取服务器响应的数据,关闭链接。而Http协议底层是通过TCP协议,而TCP是要有三次握手才能请求成功,所以每次请求的时候,都要进行一次三次握手去建立链接的话,会是我们效率非常低。所以OKhttp使用复用链接机制。流程中有个超时的机制,如果超时了,就把它关闭,如果没有超时,这个链接就可以继续复用。当我们下次进行网络请求的时候,就不需要继续创建。而OKHttp支持5个并发KeepAlive,默认链路生命为5分钟。

Android框架第(四)篇---OKHttp的源码分析_第4张图片
连接池复用机制

连接池复用机制关键对象:

  • 1.Connection:对jdk的socket物理连接的包装
  • 2.StreamAllocation:表示Connection被上层高级代码的引用次数
  • 3.ConnectionPool:Socket连接池,对连接缓存进行回收于

你可能感兴趣的:(Android框架第(四)篇---OKHttp的源码分析)