Okhttp 同步异步源码分析

Okhttp源码分析

Okhttp在使用过程中我们表面能所看到的流程 大概是是这样,通过OkHttpClient将构建的Request转换为请求呼叫,然后在RealCall中进行异步或同步任务,最后通过一些的拦截器interceptor发出网络请求和得到返回的response

下面我们来看一张请求图。图片来自于网络作者,文末会注明。

在这里插入图片描述

我们先来一段 Okhttp 请求 事咧 (异步请求)

public void get(){
    OkHttpClient mOkHttpClient = new OkHttpClient();
            //建造者模式
            final Request request = new Request.Builder()
                    .url("https://127.0.0.1:727/wms/getLocation")
                    .addHeader("tokenid","shiqi")
                    .build();
              Call call = mOkHttpClient.newCall(request);
              call.enqueue(new Callback() {
                @Override
                public void onFailure(Call call, IOException e) {

                }

                @Override
                public void onResponse(Call call, Response response) throws IOException {
                    if(response != null )
                      Log.i(TAG, "WMS:"+ String.valueOf(response.body().string()));
                    }
                }
            });
}

Request就不多说了 ,主要 存放 请求Url,params,Header 等等一系列参数请求头等

mOkHttpClient.newCall(request); 我们来看看这个newCall方法 进行了什么实质的操作

  /**
   * Prepares the {@code request} to be executed at some point in the future.
   */
  @Override public Call newCall(Request request) {
    return new RealCall(this, request, false /* for web socket */);
  }

 RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
    final EventListener.Factory eventListenerFactory = client.eventListenerFactory();

    this.client = client;
    this.originalRequest = originalRequest;
    this.forWebSocket = forWebSocket;
    this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);

    // TODO(jwilson): this is unsafe publication and not threadsafe.
    this.eventListener = eventListenerFactory.create(this);
  }

通过源代码来看我们 得知 newCall方法 返回了 RealCall对象

什么是RealCall?他的作用是什么?Call是一个接口(呼叫是已准备好执行的请求) RealCall 是其真正的执行者 实现了Call 接口的方法

RealCall 是真正执行操作的请求者,呼叫对象,下面我们继续看 enqueue()方法

    //异步任务使用
    @Override 
    public void enqueue(Callback responseCallback) {
        synchronized (this) {
          //判断是否已经 请求过
          if (executed) throw new IllegalStateException("Already Executed");
          executed = true;
        }
        captureCallStackTrace();
        client.dispatcher().enqueue(new AsyncCall(responseCallback));
   }

//diapather 异步请求的策略

调度进行排队请求 client.dispatcher().enqueue(new AsyncCall(responseCallback));

synchronized void enqueue(AsyncCall call) {
   //当前的请求数量 小于最大请求数量(并发量) 并且运行在客户端的数量 小于 机器的最大请求数量
    if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
      //运行异步调用
      runningAsyncCalls.add(call);
      executorService().execute(call);
    } else {
      //排队等待调用
      readyAsyncCalls.add(call);
    }
  }

当前的请求数量 小于最大请求数量(并发量) 并且运行在客户端的数量 小于 机器的最大请求数量。就把当前排队的请求放到正在进行的异步请求队列中 , 否则就放到即将执行的队列中

我们先看看 这其中参数 ,参数位于Dispather类中

  //当前最大请求数量 并发量  可以自己进行设置     该参数位于Dispather 类中
  //mOkHttpClient.dispatcher().setMaxRequests(1000);
  private int maxRequests = 64;
  //客户端最大请求数量。可以修改
  //mOkHttpClient.dispatcher().setMaxRequestsPerHost(100); 
  private int maxRequestsPerHost = 5;
  private @Nullable Runnable idleCallback;

  /** Executes calls. Created lazily. */
  //请求的线程池
  private @Nullable ExecutorService executorService;

  /** 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<>();

  /** Running synchronous calls. Includes canceled calls that haven't finished yet. */
  //正在运行的同步队列 
  private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();

接下来我们看看 enqueue 方法中参数AsynCall call。onResponse ,onFailure 是不是有点熟悉 正是我们。在Call.enqueue 方法 的回调 结果处理 我们接着继续往下看

final class AsyncCall extends NamedRunnable {
  private final Callback responseCallback;

  AsyncCall(Callback responseCallback) {
    super("OkHttp %s", redactedUrl());
    this.responseCallback = responseCallback;
  }

  String host() {
    return originalRequest.url().host();
  }

  Request request() {
    return originalRequest;
  }

  RealCall get() {
    return RealCall.this;
  }

  @Override protected void execute() {
    boolean signalledCallback = false;
    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) {
      if (signalledCallback) {
        // Do not signal the callback twice!
        Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);
      } else {
        responseCallback.onFailure(RealCall.this, e);
      }
    } finally {
      client.dispatcher().finished(this);
    }
  }
}

executorService().execute(call);

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 ,最大线程数量为 Integer.MAX_VALUE ,空闲线程时间为60秒 ,我们创建完线程池。是时候开始 做请求操作了。

我们回到 上面讲的AnsyCall 方法中我们仔细来看看 是如何进行操作的 在execute方法中 的getResponseWithInterceptorChain()方法来获取 服务端 返回的Response 结果 进行判断 请求成功与否如果请求被取消则在进行OnFailue回调,如果请求成功则进行onResponse的回调。
这里要注意两点:

  • 请求如果被取消,其回调实在onFailue中进行回调的
  • 排队方法的回调是在子线程中完成的

下面我们看看 getResponseWithInterceptorChain()方法 为什么 他能够 获取到。服务端 的数据 。他进行了什么操作

Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    //责任链 集合
    List<Interceptor> interceptors = new ArrayList<>();
    //拦截器
    interceptors.addAll(client.interceptors());
    // 链式重链  30秒内无响应重新请求。(举例)
    interceptors.add(retryAndFollowUpInterceptor);
    //用户请求参数转化  再将服务器响应的数据转换为 BridgeInterceptor
    //负责把用户构造的请求转换为发送到服务器的请求 、把服务器返回的响应转换为用户友好的响应 处理 配置请求头等      信息
    interceptors.add(new BridgeInterceptor(client.cookieJar()));
    //更新缓存
    interceptors.add(new CacheInterceptor(client.internalCache()));
    //负责 和 服务器建立连接
    interceptors.add(new ConnectInterceptor(client));
    if (!forWebSocket) {
      //配置Okhttp networkInterceptors
      interceptors.addAll(client.networkInterceptors());
    }
    //发送请求。读取响应数据
    interceptors.add(new CallServerInterceptor(forWebSocket));
    //链式调用
    Interceptor.Chain chain = new RealInterceptorChain(
        interceptors, null, null, null, 0, originalRequest);
    return chain.proceed(originalRequest);
  }

我们来看看链式调用

Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0, originalRequest);

这个方法 是将上面的 我们定义的 interceotors拦截器结合 ,originalRequest -> 也就是我们写的Request

RealInterceptorChain
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
      RealConnection connection) throws IOException {
    if (index >= interceptors.size()) throw new AssertionError();

    calls++;

    // If we already have a stream, confirm that the incoming request will use it.
    //如果我们已经有一个stream。确定即将到来的request会使用它
    if (this.httpCodec != null && !this.connection.supportsUrl(request.url())) {
      throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
          + " must retain the same host and port");
    }

    // If we already have a stream, confirm that this is the only call to chain.proceed().
    //如果我们已经有一个stream, 确定chain.proceed()唯一的call
    if (this.httpCodec != null && calls > 1) {
      throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
          + " must call proceed() exactly once");
    }

    // Call the next interceptor in the chain.\
    //调用链的下一个拦截器
    RealInterceptorChain next = new RealInterceptorChain(
        interceptors, streamAllocation, httpCodec, connection, index + 1, request);
    Interceptor interceptor = interceptors.get(index);
    Response response = interceptor.intercept(next);

    // Confirm that the next interceptor made its required call to chain.proceed().
    //确认下一个拦截器对chain.process()进行了所需的调用。
    if (httpCodec != null && index + 1 < interceptors.size() && next.calls != 1) {
      throw new IllegalStateException("network interceptor " + interceptor
          + " must call proceed() exactly once");
    }

    // Confirm that the intercepted response isn't null.
    //确认截获的响应不为空
    if (response == null) {
      throw new NullPointerException("interceptor " + interceptor + " returned null");
    }

    return response;
  }

上段代码中午 重要部分

RealInterceptorChain next = new RealInterceptorChain(
        interceptors, streamAllocation, httpCodec, connection, index + 1, request);
    Interceptor interceptor = interceptors.get(index);
    Response response = interceptor.intercept(next);

1.interceptors 我们在getResponseWithInterceptorChain 添加的责任链 (拦截器)集合 进行循环调用

第一个添加的就是—》RetryAndFollowUpInterceptor:负责失败重试以及重定向

代码比较长 贴部分代码

 Response response = null;
            boolean releaseConnection = true;
            try {
                response = ((RealInterceptorChain) chain).proceed(request, streamAllocation, null, null);    //(1)
                releaseConnection = false;
            } catch (RouteException e) {
                // The attempt to connect via a route failed. The request will not have been sent.
                //通过路线连接失败,请求将不会再发送
                if (!recover(e.getLastConnectException(), true, request)) throw e.getLastConnectException();
                releaseConnection = false;
                continue;
            } catch (IOException e) {
                // An attempt to communicate with a server failed. The request may have been sent.
                // 与服务器尝试通信失败,请求不会再发送。
                if (!recover(e, false, request)) throw e;
                releaseConnection = false;
                continue;
            } finally {
                // We're throwing an unchecked exception. Release any resources.
                //抛出未检查的异常,释放资源
                if (releaseConnection) {
                    streamAllocation.streamFailed(null);
                    streamAllocation.release();
                }
            }

            // Attach the prior response if it exists. Such responses never have a body.
            // 附加上先前存在的response。这样的response从来没有body
            // TODO: 2016/8/23 这里没赋值,岂不是一直为空?
            if (priorResponse != null) { //  (2)
                response = response.newBuilder()
                        .priorResponse(priorResponse.newBuilder()
                                .body(null)
                                .build())
                        .build();
            }

在上述代码中 主要还是 第一步调用下一个拦截器 并且对当前的请求做对应的异常处理

response = ((RealInterceptorChain) chain).proceed(request, streamAllocation, null, null);

2 .BridgeInterceptor 负责把用户构造的请求转换为发送到服务器的请求、把服务器返回的响应转换为用户友好的响应的

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

    //检查request。将用户的request转换为发送到server的请求
    RequestBody body = userRequest.body();     //(1)
    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.
    //GZIP压缩
    boolean transparentGzip = false;
    if (userRequest.header("Accept-Encoding") == 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());   //(2)

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

    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);
      responseBuilder.body(new RealResponseBody(strippedHeaders, Okio.buffer(responseBody)));
    }

    return responseBuilder
}

1)先对于request的格式进行检查

2)调用下一个interceptor来得到response

3)下面就是对得到的response进行一些判断操作,最后将结果返回。

3.CacheInterceptor 再下来就是缓存的使用

4.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, doExtensiveHealthChecks);
       RealConnection connection = streamAllocation.connection();

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

在上述建立连接的代码中主要是 创建的了HttpCodec对象 他是对http 协议操作的的抽象

5.配置OkHttpClient时设置的 NetworkInterceptors。

6.CallServerInterceptor:发送和接收数据

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

    long sentRequestMillis = System.currentTimeMillis();
    httpCodec.writeRequestHeaders(request);

    if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) {   //===(1)
      Sink requestBodyOut = httpCodec.createRequestBody(request, request.body().contentLength());
      BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);
      request.body().writeTo(bufferedRequestBody);
      bufferedRequestBody.close();
    }

    httpCodec.finishRequest();

    Response response = httpCodec.readResponseHeaders()     //====(2)
        .request(request)
        .handshake(streamAllocation.connection().handshake())
        .sentRequestAtMillis(sentRequestMillis)
        .receivedResponseAtMillis(System.currentTimeMillis())
        .build();

    if (!forWebSocket || response.code() != 101) {
      response = response.newBuilder()
          .body(httpCodec.openResponseBody(response))
          .build();
    }

    if ("close".equalsIgnoreCase(response.request().header("Connection"))
        || "close".equalsIgnoreCase(response.header("Connection"))) {
      streamAllocation.noNewStreams();
    }

    int code = response.code();
    if ((code == 204 || code == 205) && response.body().contentLength() > 0) {
      throw new ProtocolException(
          "HTTP " + code + " had non-zero Content-Length: " + response.body().contentLength());
    }

    return response;
  }

HttpCodec 开始处理 request 得到responese 在反回到客户端

3.同步请求
//HTTP GET
    public String get(String url) throws IOException {
        //新建OKHttpClient客户端
        OkHttpClient client = new OkHttpClient();
        //新建一个Request对象
        Request request = new Request.Builder()
                .url(url)
                .build();
        //Response为OKHttp中的响应
        Response response = client.newCall(request).execute();
        if (response.isSuccessful()) {
            return response.body().string();
        }else{
            throw new IOException("Unexpected code " + response);
        }
    }

同步内容和异常 基本一致主要在于 execute方法 同步无需 new CallBack, execute直接返回了 Respnse结果

其余大体一致

 @Override public Response execute() throws IOException {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    captureCallStackTrace();
    try {
      client.dispatcher().executed(this);
      Response result = getResponseWithInterceptorChain();
      if (result == null) throw new IOException("Canceled");
      return result;
    } finally {
      client.dispatcher().finished(this);
    }
  }

本文参考修改来源于 [mecury](https://www.jianshu.com/u/dd2aad4c6950 兄弟。 感谢?
本文记录 学习笔记

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值