okhttp中的线程池及源码分析

来源:互联网 发布:latex for mac下载 编辑:程序博客网 时间:2024/05/22 19:43

okhttp3使用很常见,通常我们使用的时候是这样的:

//创建okHttpClient对象OkHttpClient mOkHttpClient = new OkHttpClient();//创建一个Requestfinal Request request = new Request.Builder()                        .url(url)                        .build();        Call call = mOkHttpClient.newCall(request);        //请求加入调度        call.enqueue(new Callback() {            @Override            public void onResponse(final Response response) throws IOException {            }            @Override            public void onFailure(Request arg0, IOException arg1) {                // TODO Auto-generated method stub            }        });

看下okhttpClient的构造函数,

public OkHttpClient() {        this.dispatcher = new Dispatcher();        ***    }

这里有个dispatcher, 分析下Dispatcher的源码:

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

看到了没,这里有两个构造函数,有一个创建线程池的函数,但并不会在开始调用,可以告诉大家,这里只有当你使用enqueue方法的时候,才会用到这个线程池,okhttp所用的线程池有点类似于newCacheExecutorService,好处是会根据程序的运行情况自动来调整线程池中的线程数量,我这里对这个线程池写过demo,假如你一次有30个请求,它会同步的一次将请求放到非核心线程中,因为非核心线程的数量为系统最大值,所以只要系统有空闲,就会创建线程并顺序处理。
接下来看下Request的build函数,这里用了建造者模式来存放Request的请求参数。
接下来看下Call call = mOkHttpClient.newCall(request);这句话,可以看到真正的调用是RealCall这个类,而这里和okhttp3和okhttp2的区别在于Call这个类,okhttp3中是个接口,而okhttp2中是个对象。

最后是RealCall的enqueue方法,这个方法是重点,我们进去分析下:

client.dispatcher().enqueue(new AsyncCall(responseCallback));

可以看到调用的是Dispatcher的enqueue方法,进去看看,

synchronized void enqueue(AsyncCall call) {        if(this.runningCalls.size() < this.maxRequests && this.runningCallsForHost(call) < this.maxRequestsPerHost) {            this.runningCalls.add(call);            this.getExecutorService().execute(call);        } else {            this.readyCalls.add(call);        }    }

这里判断同时加入的任务有没有超过一些限制,然后执行线程池中的任务,进到Call.AsyncCall方法中:

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

先看下NamedRunnable 类,可以看到执行了execute方法:

 final class AsyncCall extends NamedRunnable {    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);      }        } }

这里根据返回值的判断,回调给Callback的onResponse方法,注意,因为线程池开启的任务是异步任务,而这里的回调都是在子线程中执行的,因此不能直接去更新UI,这也是单独用okhttp不是很方便的原因。

我们再看RealCall中的方法,有个excute方法,这个方法和enqueue方法都是执行任务,而excute执行的是同步任务,如果你像上面一样写下如下代码:

Call call = mOkHttpClient.newCall(request);call.execute();

肯定会报错误的,因为这时执行的时候会认为在主线程执行的http请求,而http请求是必须要在子线程执行的。
我们再看下Response response = 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);    return chain.proceed(originalRequest);  }

这里给interceptors添加了6个拦截器,然后调用RealInterceptorChain的proceed方法,再进去看下:

 public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,RealConnection connection) throws IOException {     ***    RealInterceptorChain next = new RealInterceptorChain(        interceptors, streamAllocation, httpCodec, connection, index + 1, request);    Interceptor interceptor = interceptors.get(index);    Response response = interceptor.intercept(next);    ***    return response;  }
public RealInterceptorChain(List<Interceptor> interceptors, StreamAllocation streamAllocation,      HttpCodec httpCodec, RealConnection connection, int index, Request request) {    this.interceptors = interceptors;    this.connection = connection;    this.streamAllocation = streamAllocation;    this.httpCodec = httpCodec;    this.index = index;    this.request = request;  }

代码很长,核心内容在这里,仔细分析一下,从interceptors里面根据index索引取出拦截器,然后调用intercept方法,并传入RealInterceptorChain 对象,而在intercept方法中都有调用RealInterceptorChain 的proceed方法,这说明什么?他们在循环调用啊!!!我勒个去。。。。根据index的不同,intercept方法中传入的是下一个拦截器对象。那么为什么要这样呢?拦截器里面做了什么,看来我么要去分析下拦截器里面的intercept方法了,先看第一个retryAndFollowUpInterceptor:

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

这里是循环调用的体现,流程是先去处理其他5个拦截器,得到response之后再来这边处理,那么接下来看第二个拦截器BridgeInterceptor,同样在intercept方法里面调用了如下函数:

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

意味着再去处理剩余的4个处理器,等待response进行处理,如此类推,最后处理的拦截器就是CallServerInterceptor,我们看下intercept方法:

@Override public Response intercept(Chain chain) throws IOException {    RealInterceptorChain realChain = (RealInterceptorChain) chain;    HttpCodec httpCodec = realChain.httpStream();    StreamAllocation streamAllocation = realChain.streamAllocation();    RealConnection connection = (RealConnection) realChain.connection();    Request request = realChain.request();    long sentRequestMillis = System.currentTimeMillis();    httpCodec.writeRequestHeaders(request);//写入请求头    Response.Builder responseBuilder = null;    if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) {//判断是否有body数据      // If there's a "Expect: 100-continue" header on the request, wait for a "HTTP/1.1 100      // Continue" response before transmitting the request body. If we don't get that, return what      // we did get (such as a 4xx response) without ever transmitting the request body.      if ("100-continue".equalsIgnoreCase(request.header("Expect"))) {        httpCodec.flushRequest();        responseBuilder = httpCodec.readResponseHeaders(true);      }      if (responseBuilder == null) {        // Write the request body if the "Expect: 100-continue" expectation was met.        Sink requestBodyOut = httpCodec.createRequestBody(request, request.body().contentLength());        BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);        request.body().writeTo(bufferedRequestBody);        bufferedRequestBody.close();      } else if (!connection.isMultiplexed()) {        // If the "Expect: 100-continue" expectation wasn't met, prevent the HTTP/1 connection from        // being reused. Otherwise we're still obligated to transmit the request body to leave the        // connection in a consistent state.        streamAllocation.noNewStreams();      }    }    httpCodec.finishRequest();    if (responseBuilder == null) {      responseBuilder = httpCodec.readResponseHeaders(false);    }    Response response = responseBuilder        .request(request)        .handshake(streamAllocation.connection().handshake())        .sentRequestAtMillis(sentRequestMillis)        .receivedResponseAtMillis(System.currentTimeMillis())        .build();    int code = response.code();    if (forWebSocket && code == 101) {      // Connection is upgrading, but we need to ensure interceptors see a non-null response body.      response = response.newBuilder()          .body(Util.EMPTY_RESPONSE)          .build();    } else {      response = response.newBuilder()          .body(httpCodec.openResponseBody(response))          .build();    }    if ("close".equalsIgnoreCase(response.request().header("Connection"))        || "close".equalsIgnoreCase(response.header("Connection"))) {      streamAllocation.noNewStreams();    }    if ((code == 204 || code == 205) && response.body().contentLength() > 0) {      throw new ProtocolException(          "HTTP " + code + " had non-zero Content-Length: " + response.body().contentLength());    }    return response;  }}

看到了,这里就是真正的http请求,但我们发现这里的http请求不是用HttpUrlConnection,那他是怎么做的呢?
先是封装了Request对象,然后写入到HttpCodec中,接着判断是否带body数据,如果携带了,写入到request.body()中,然后刷新httpCodec,httpCodec中的source和sink,对应着socket请求中的输入和输出,从HttpCodec中读取响应的头信息,返回Response.Builder。

httpCodec.finishRequest();//这里是真正的执行请求    if (responseBuilder == null) {      responseBuilder = httpCodec.readResponseHeaders(false);    }

最后拿到Response数据:

Response response = responseBuilder        .request(request)        .handshake(streamAllocation.connection().handshake())        .sentRequestAtMillis(sentRequestMillis)        .receivedResponseAtMillis(System.currentTimeMillis())        .build();

后面是拿到的response数据进行处理。
前面说了,其他的拦截器都是对这里的Response进行结果的处理。CacheInterceptor主要是对结果进行缓存的控制,retryAndFollowUpInterceptor对重定向情况的处理。

原创粉丝点击