OkHttp源码解析06-宏观

OKHTTP的使用

OkHttpClient okHttpClient = new OkHttpClient.Builder().build();

Request request = new Request.Builder().url("https://www.baidu.com").get().build(); //用到了建造者模式

Call call = okHttpClient.newCall(request);

call.enqueue(new Callback() {
    @Override
    public void onFailure(Call call, IOException e) {
        System.out.println("请求失败.. E:" + e.toString());
    }

    @Override
    public void onResponse(Call call, Response response) throws IOException {
        String string = response.body().string();
        System.out.println("请求完成.. result:" + string);
		//response.body().byteStream();
		//response.body().charStream();
    }
});

主流程源码分析:

@Override public void enqueue(Callback responseCallback) {
	synchronized (this) {
	  //不能执行大于一次enqueue
	  if (executed) throw new IllegalStateException("Already Executed");
	  executed = true;
	}
	captureCallStackTrace();
	eventListener.callStart(this);
	//拿到调度器dispatcher.enqueue方法
	client.dispatcher().enqueue(new AsyncCall(responseCallback));
}

接着来看client.dispatcher.enqueue

synchronized void enqueue(AsyncCall call) {
	/*同时运行的异步任务小于64 && 同时访问同一个服务器 不能超过5个*/
	if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
		//加入到执行队列
	  	runningAsyncCalls.add(call);
	  	executorService().execute(call);
	} else {
		//加入到等待队列
	  	readyAsyncCalls.add(call);
	}
 }

这里有两个队列,会根据如上的判断,添加到指定队列里去


  /** 等待执行的队列 */
  private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();

  /** 异步运行的队列 enqueue */
  private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();

  /** 同步运行的队列 execute */
  private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();

Deque: 双端队列
AsyncCall: 执行耗时任务

来看下AsyncCall,这里getResponseWithInterceptorChain()是责任链模式,最终会请求到服务器

final class AsyncCall extends NamedRunnable {
    private final Callback responseCallback;
	//...
    @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) {
          // signalledCallback代表已经回调过CallBack,说明这个异常是用户造成的,不处理
          // 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);
      }
    }
  }

分析OKHTTP的线程池

executorService().execute(call);这行代码,就是调用线程池去执行。

先来看executorService(),位于Dispatcher类

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,最大线程数为MAX_VAUE,缓存时间为60秒的一个线程池,

这里的配置和Executors.newCachedThreadPool()一致; //创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。这种线程池适合执行大量耗时较少的任务。

结论:Okhttp里面的线程池,采用的是缓存方案

OkHttp责任链模式

getResponseWithInterceptorChain()用到了责任链模式,来看下相关代码,在RealCall

Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    List<Interceptor> interceptors = new ArrayList<>();
    //用户自定义的拦截器,用在与服务器建立链接之前进行拦截
    interceptors.addAll(client.interceptors());
    //重试和失败重定向拦截器
    interceptors.add(retryAndFollowUpInterceptor);
    //桥接和适配拦截器。主要补充用户创建请求当中的一些请求头Content-Type等
    interceptors.add(new BridgeInterceptor(client.cookieJar()));
    //主要处理缓存
    interceptors.add(new CacheInterceptor(client.internalCache()));
    //与服务器建立链接。创建可以用的RealConnection(对java.io和java.nio进行了封装)
    interceptors.add(new ConnectInterceptor(client));
    if (!forWebSocket) {
      //用户自定义的拦截器,用在与服务器建立链接之后进行拦截。只有非socket进行设置
      interceptors.addAll(client.networkInterceptors());
    }
    //向服务器发送请求和接收数据。将请求写入IO流,再从IO流中读取响应数据
    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);
  }

可以看到,Interceptor.Chain的实现类是RealInterceptorChain,最终调用的是RealInterceptorChain.proceed

@Override public Response proceed(Request request) throws IOException {
   return proceed(request, streamAllocation, httpCodec, connection);
 }

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

   // 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;
 }

来看interceptor.intercept(next);,这里以interceptor的其中一个实现类BridgeInterceptor为例

@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();
  }

主要来看chain.proceed(requestBuilder.build()),可以看到,这里创建了requestBuilder,并调用了下一个chain,从而最终调用到向服务器请求的chain

源码分析中的版本
com.squareup.okhttp3:okhttp:3.10.0

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

氦客

你的鼓励是我创作最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值