面试准备之okhttp的使用及源码分析(一)

对于网络请求,一直都是在用一些优秀的开源框架如Vollery,okhttp,Retrofit等,虽然项目中一般不会让自己手写网络请求框架,但是我们在使用中不仅要会使用,还要“知其所以然”,特别现在面试还需要你至少看过一个项目的源码,正常开发中我们也应该多读别人的代码,来提高自己,
一、集成与使用
1、集成
Ecipse:下载jar包,放入libs文件夹下,
AndroidStudio:直接再当前项目的gradle内引入:

compile 'com.squareup.okhttp:okhttp:2.4.0'

2、使用
2.1、同步请求
2.1.1、get请求

//创建okhttpclient对象
 OkHttpClient okHttpClient = new OkHttpClient();
 //创建一个请求
 Request request = new Request.Builder()
         .url("http://xxxx")
         .build();
  //执行网络请求,并返回数据       
 Response response=okHttpClient.newCall(request).execute();
 String data = response.body().string();

解释:首先构建okhttpClient对象,然后创建请求体,然后通过okHttpClient构建Call并执行execute()方法,返回数据,
注意:请求回的数据response会包含很多信息,例如head,body,内容在body内,然后调用string()方法,而不是toString()方法获取,

2.1.2、post请求

//创建okhttpclient对象
 OkHttpClient okHttpClient = new OkHttpClient();
 //创建body
 RequestBody formBody = new FormEncodingBuilder()
                .add("","")
                .build();
 //通过url,body构建一个请求
Request request = new Request.Builder()
                .url("http://www.xxxxxx")
                .post(formBody)
                .build();
//执行网络请求,并返回数据 
Response response=okHttpClient.newCall(request).execute();
String data = response.body().string();             

解释:post再官方文档中可以添加很多格式如JSON、文件、流等,此处的body构建方法是常用的简单参数请求方法,想了解更多可以参考此博客:http://blog.csdn.net/mynameishuangshuai/article/details/51303446,此处就不多分析了。

2.2、异步

//创建okhttpclient对象
 OkHttpClient okHttpClient = new OkHttpClient();
 //创建一个请求
 Request request = new Request.Builder()
         .url("http://xxxx")
         .build();
//创建一个call
Call call =okHttpClient.newCall(request);
//进行网络请求
call.enqueue(new Callback() {
            @Override
            public void onFailure(Request request, IOException e) {  //请求失败
                Log.e("--json--"," shibaile ");
            }

            @Override
            public void onResponse(Response response) throws IOException {  //请求成功
                try {
                    //获取数据,此线程为子线程,想要操作主线程需要使用runOnUiThread()方法
                    String jsondata =response.body().string();
                } catch (JSONException e) {
                    e.printStackTrace();
                }
            }
        });

解释:异步与同步相比是因为调用了enqueue()方法实现了call.back,数据会通过back里的回调方法onResponse()传递过来。

二、源码分析
思路:
1、要分析源码,我们理所当然的要从头开始,再这里我们的头是什么?是okhttpClient,我们需要去okhttpClient类中看构造方法都做什么了记录下来,
2、然后看Request,这个请求体都包含什么内容,定义了什么,再往下,
3、然后就是newcall方法了,我们再去看okhttpClient的newcall方法内部调用那些方法再往下看,
4、最终我们再看call的enqueue()(异步调用)和execute()(同步调用)方法到底怎样讲我们的请求与服务器交互的,又是怎样实现了callback来实现数据的回传,基本大致就是这个思路,接下来我们分析:

**1、okhttpClient的构建:
直接看okhttpClient的构建方法:源码如下:**

//内部构造器 
public OkHttpClient() {
    this(new Builder());   //调用下面的Builder()方法
  }

接下来看new Builder()内都做了什么,Builder是OkHttpClient的内部类,如下:

    final Dispatcher dispatcher;  //分发器
    final Proxy proxy;  //代理
    final List protocols; //协议
    final List connectionSpecs; //传输层版本和连接协议
    final List interceptors; //拦截器
    final List networkInterceptors; //网络拦截器
    final ProxySelector proxySelector; //代理选择
    final CookieJar cookieJar; //cookie
    final Cache cache; //缓存
    final InternalCache internalCache;  //内部缓存
    final SocketFactory socketFactory;  //socket 工厂
    final SSLSocketFactory sslSocketFactory; //安全套接层socket 工厂,用于HTTPS
    final CertificateChainCleaner certificateChainCleaner;//验证确认响应证书 
    final HostnameVerifier hostnameVerifier;    //  主机名字确认
    final CertificatePinner certificatePinner;  //  证书链
    final Authenticator proxyAuthenticator;     //代理身份验证
    final Authenticator authenticator;      // 本地身份验证
    final ConnectionPool connectionPool;    //连接池,复用连接
    final Dns dns;  //域名
    final boolean followSslRedirects;  //安全套接层重定向
    final boolean followRedirects;  //本地重定向
    final boolean retryOnConnectionFailure; //重试连接失败
    final int connectTimeout;    //连接超时时长
    final int readTimeout; //read 超时
    final int writeTimeout; //write 超时

这里main定义了很多如connectTimeout(连接超时时长),dns(域名)等再构建OkHttpClient时可以通过builde添加,如果不添加则去builde内部则默认定义这些参数:

public Builder() {
      dispatcher = new Dispatcher();
      protocols = DEFAULT_PROTOCOLS;
      connectionSpecs = DEFAULT_CONNECTION_SPECS;
      eventListenerFactory = EventListener.factory(EventListener.NONE);
      proxySelector = ProxySelector.getDefault();
      cookieJar = CookieJar.NO_COOKIES;
      socketFactory = SocketFactory.getDefault();
      hostnameVerifier = OkHostnameVerifier.INSTANCE;
      certificatePinner = CertificatePinner.DEFAULT;
      proxyAuthenticator = Authenticator.NONE;
      authenticator = Authenticator.NONE;
      connectionPool = new ConnectionPool();
      dns = Dns.SYSTEM;
      followSslRedirects = true;
      followRedirects = true;
      retryOnConnectionFailure = true;
      connectTimeout = 10_000;
      readTimeout = 10_000;
      writeTimeout = 10_000;
      pingInterval = 0;
    }

2、来看Request的内容,request是通过其builder创建的,看下面:

Builder(Request request) {
      this.url = request.url;
      this.method = request.method;
      this.body = request.body;
      this.tag = request.tag;
      this.headers = request.headers.newBuilder();
    }

public HttpUrl url() {
    return url;
  }

  public String method() {
    return method;
  }

  public Headers headers() {
    return headers;
  }

  public String header(String name) {
    return headers.get(name);
  }

  public List headers(String name) {
    return headers.values(name);
  }

  public @Nullable RequestBody body() {
    return body;
  }

  public Object tag() {
    return tag;
  }
  ...

解释:由上面源码可知我们构建Request时可以添加请求的url、设置get或者post请求的method 、body、tag等,类似一个实体类的bean,用来存储这些信息,

3、根据上面的第三部我们来看下newcall方法内部执行了什么
newcall是由okhttpClient调用的,那我们去这里找一下,看源码:

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

我们可以看到这里返回的是RealCall,我们接下来去找RealCall.newRealCall()方法分析

static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
    // Safely publish the Call instance to the EventListener.
    RealCall call = new RealCall(client, originalRequest, forWebSocket);
    call.eventListener = client.eventListenerFactory().create(call);
    return call;
  }
再看这里调用的RealCall 的构造方法,如下:

private RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
    this.client = client;           //   @1
    this.originalRequest = originalRequest;    //   @2
    this.forWebSocket = forWebSocket;      //   @3
    this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);                            //  @4
  }

解释:从上面可以看到
3.1、this.client 就是我们构建的OkHttpClient
3.2、this.originalRequest是我们上面构建包含url、method等的Request;
3.3,forWebSocket 是一个boolean类型的数据表示:

The application's original request unadulterated by redirects or auth headers
//应用程序的原始请求不掺杂重定向或身份验证头,就是表示是否胃最开始的原始请求

3.4、我们可以卡他的源码声明表示这是一个:从失败中恢复,并根据需要跟踪重定向的拦截器,用于重定向的

4、接下来我们需要分析call的enqueue()(异步调用)和execute()(同步调用)具体是怎么实现与服务器通信的,由于上面知道我们这里的call是RealCall,所以我们去RealCall内部去找enqueue()和execute()方法:
4.1、execute()—–同步调用
看源码:

@Override 
public Response execute() throws IOException {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");  //  @1
      executed = true;
    }
    captureCallStackTrace();
    eventListener.callStart(this);
    try {
      client.dispatcher().executed(this);   //   @2
      Response result = getResponseWithInterceptorChain();  //   @3
      if (result == null) throw new IOException("Canceled");
      return result;
    } catch (IOException e) {
      eventListener.callFailed(this, e);
      throw e;
    } finally {
      client.dispatcher().finished(this);    // 4
    }
  }

由上面我们可以看到:
1、判断当前的call是否被执行过,一个call只能执行一次,如果想要一个完全一样的 call,可以利用 all#clone 方法进行克隆。
2、利用 client.dispatcher().executed(this) 来进行实际执行,dispatcher 是刚才看到的 OkHttpClient.Builder 的成员之一,它的文档说自己是异步 HTTP 请求的执行策略,现在看来,同步请求它也有掺和。
3、最终的Response 是由getResponseWithInterceptorChain()函数获取 HTTP 返回结果,从函数名可以看出,这一步还会进行一系列“拦截”操作。
4、最后还要通知 dispatcher 自己已经执行完毕
其实最重要的还是看respone是怎么来的,所以我们来看getResponseWithInterceptorChain():

Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    List interceptors = new ArrayList<>();
    interceptors.addAll(client.interceptors());      //     @1
    interceptors.add(retryAndFollowUpInterceptor);   //    @2
    interceptors.add(new BridgeInterceptor(client.cookieJar()));  //  @3
    interceptors.add(new CacheInterceptor(client.internalCache())); //   @4
    interceptors.add(new ConnectInterceptor(client));  //    @5
    if (!forWebSocket) {   //上面定义的是否胃原始请求
      interceptors.addAll(client.networkInterceptors());   //   @6
    }
    interceptors.add(new CallServerInterceptor(forWebSocket));  //   @7  

    Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
        originalRequest, this, eventListener, client.connectTimeoutMillis(),
        client.readTimeoutMillis(), client.writeTimeoutMillis());

    return chain.proceed(originalRequest);         //     8
  }

由上面可知:
1、在配置 OkHttpClient 时设置的 interceptors;
2、负责失败重试以及重定向的 RetryAndFollowUpInterceptor;
3、负责把用户构造的请求转换为发送到服务器的请求、把服务器返回的响应转换为用户友好的响应的 BridgeInterceptor;
4、负责读取缓存直接返回、更新缓存的 CacheInterceptor;
5、负责和服务器建立连接的 ConnectInterceptor;
6、配置 OkHttpClient 时设置的 networkInterceptors;
7、负责向服务器发送请求数据、从服务器读取响应数据的 CallServerInterceptor。
8、开始链式调用

重点:整个网络请求这里最重要了,再上面我们知道interceptors 集合添加了各种参数,但是最终返回数据的是 8 位置,所以我们先看 8 这个位置是怎么样讲这些参数调用的:

4.1.1 RealInterceptorChain.proceed源码如下:

 public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
      RealConnection connection) throws IOException {
      //index由其构造器中传入,我们可以看到是0,当interceptors长度小于等于0时,抛出“声明错误”异常
    if (index >= interceptors.size()) throw new AssertionError();

    calls++;

    // If we already have a stream, confirm that the incoming request will use it.
    //如果我们已经有了一个流,请确认传入的请求将使用它。
    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().
    //如果我们已经有了一个流,请确认这是惟一的调用链..继续()。
    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, call, eventListener, connectTimeout, readTimeout,
        writeTimeout);
    Interceptor interceptor = interceptors.get(index);
    Response response = interceptor.intercept(next);

    // Confirm that the next interceptor made its required call to chain.proceed().
    //确认下一个拦截器对链..继续()进行了必要的调用。
    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");
    }

    if (response.body() == null) {
      throw new IllegalStateException(
          "interceptor " + interceptor + " returned a response with no body");
    }
    //将respone返回
    return response;
  }

还是看根本找respone是怎么来的,

// Call the next interceptor in the chain.
    //在链中调用下一个拦截器。
    RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
        connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
        writeTimeout);        //  @1
    Interceptor interceptor = interceptors.get(index);   //  @2
    Response response = interceptor.intercept(next);    //  @3

解释:
1、与我们上面getResponseWithInterceptorChain()内调用的方法一样,重新实例化下一个拦截器对应的RealIterceptorChain对象,
2、获取当前的interceptor,由上面知道interceptors是一个装载interceptor的List集合;
3、调用当前拦截器的intercept()方法,并将下一个拦截器的RealIterceptorChain对象传递下。
有上面的getResponseWithInterceptorChain()方法中我们知道她分贝添加的是

interceptors----retryAndFollowUpInterceptor---BridgeInterceptor==CacheInterceptor--ConnectInterceptor--networkInterceptors--CallServerInterceptor,

我们刚刚说过respone是当前拦截器调用intercept方法并将下一个拦截器的RealIterceptorChain对象传递下去,下面分析retryAndFollowUpInterceptor;(都说上面的方法是责任链模式,因为对此不精通所以没有说明,以后研究之后改正)

4.1.2 retryAndFollowUpInterceptor –重定向拦截器 ,我们看他重写的方法intercept:

@Override 
public Response intercept(Chain chain) throws IOException {
    Request request = chain.request();
    RealInterceptorChain realChain = (RealInterceptorChain) chain;
    Call call = realChain.call();
    EventListener eventListener = realChain.eventListener();

    streamAllocation = new StreamAllocation(client.connectionPool(), createAddress(request.url()),
        call, eventListener, callStackTrace);

    int followUpCount = 0;
    Response priorResponse = null;
    while (true) {
      if (canceled) {
        streamAllocation.release();
        throw new IOException("Canceled");
      }

      Response response;
      boolean releaseConnection = true;
      try {
        response = realChain.proceed(request, streamAllocation, null, null);
        releaseConnection = false;
      } catch (RouteException e) {
        // The attempt to connect via a route failed. The request will not have been sent.
        //他试图通过一条线路连接失败。请求将不会被发送。
        if (!recover(e.getLastConnectException(), false, request)) {
          throw e.getLastConnectException();
        }
        releaseConnection = false;
        continue;
      } catch (IOException e) {
        // An attempt to communicate with a server failed. The request may have been sent.
        //试图与服务器通信的尝试失败了。请求可能已经发送了。
        boolean requestSendStarted = !(e instanceof ConnectionShutdownException);
        if (!recover(e, requestSendStarted, 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.
      if (priorResponse != null) {
        response = response.newBuilder()
            .priorResponse(priorResponse.newBuilder()
                    .body(null)
                    .build())
            .build();
      }

      Request followUp = followUpRequest(response);

      if (followUp == null) {
        if (!forWebSocket) {
          streamAllocation.release();
        }
        return response;
      }

      closeQuietly(response.body());

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

      if (followUp.body() instanceof UnrepeatableRequestBody) {
        streamAllocation.release();
        throw new HttpRetryException("Cannot retry streamed HTTP body", response.code());
      }

      if (!sameConnection(response, followUp.url())) {
        streamAllocation.release();
        streamAllocation = new StreamAllocation(client.connectionPool(),
            createAddress(followUp.url()), call, eventListener, callStackTrace);
      } else if (streamAllocation.codec() != null) {
        throw new IllegalStateException("Closing the body of " + response
            + " didn't close its backing stream. Bad interceptor?");
      }

      request = followUp;
      priorResponse = response;
    }
  }

可以看出在response = ((RealInterceptorChain) chain).proceed(request, streamAllocation, null, null);中直接调用了下一个拦截器,然后捕获可能的异常来进行操作,然后我们看下一个拦截器中的proceed

4.1.3 BridgeInterceptor 负责把用户构造的请求转换为发送到服务器的请求、把服务器返回的响应转换为用户友好的响应,我们看它重写的方法intercept():

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

    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.
    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());   //  @2

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

解释:
1、1到2位置都是校验我们的请求Request,并且通过requestBuilder重新构建了一个能更规范的request,
2、调用下一个拦截器返回数据;
3、对返回的数据进行处理
接下来看下一个拦截器

4.1.4 CacheInterceptor 负责读取缓存直接返回、更新缓存的 ,看源码:

@Override 
public Response intercept(Chain chain) throws IOException {
    Response cacheCandidate = cache != null        //     @1
        ? cache.get(chain.request()) //通过request得到缓存
        : null;

    long now = System.currentTimeMillis();

    CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get(); //根据request来得到缓存策略     @2
    Request networkRequest = strategy.networkRequest;
    Response cacheResponse = strategy.cacheResponse;

    if (cache != null) {
      cache.trackResponse(strategy);
    }

    if (cacheCandidate != null && cacheResponse == null) { //存在缓存的response,但是不允许缓存
      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.
      //如果我们禁止使用网络,且缓存为null,失败
    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(EMPTY_BODY)
          .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);//网络请求拦截器    //     @3
    } finally {
      // If we're crashing on I/O or otherwise, don't leak the cache body.
        //如果我们因为I/O或其他原因崩溃,不要泄漏缓存体
      if (networkResponse == null && cacheCandidate != null) {
        closeQuietly(cacheCandidate.body());
      }
    }

    // If we have a cache response too, then we're doing a conditional get.     @4
      //如果我们有一个缓存的response,然后我们正在做一个条件GET
    if (cacheResponse != null) {
      if (validate(cacheResponse, networkResponse)) { //比较确定缓存response可用
        Response response = cacheResponse.newBuilder()
            .headers(combine(cacheResponse.headers(), networkResponse.headers()))
            .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()).
          //更新缓存,在剥离content-Encoding之前
        cache.trackConditionalCacheHit();
        cache.update(cacheResponse, response);
        return response;
      } else {
        closeQuietly(cacheResponse.body());
      }
    }

    Response response = networkResponse.newBuilder()
        .cacheResponse(stripBody(cacheResponse))
        .networkResponse(stripBody(networkResponse))
        .build();

    if (HttpHeaders.hasBody(response)) {    // =========(5)
      CacheRequest cacheRequest = maybeCache(response, networkResponse.request(), cache);
      response = cacheWritingResponse(cacheRequest, response);
    }

    return response;
  }

1、三元运算符判断是否有缓存,有的话直接通过缓存的request来获取respone,然后进行判断当前response是否有效,没有将cacheCandate赋值为空。
2、根据request判断缓存的策略,是否要使用了网络,缓存 或两者都使用
3、调用下一个拦截器,与服务器交互得到response
4、如果本地已经存在cacheResponse,那么让它和网络得到的networkResponse做比较,决定是否来更新缓存的cacheResponse
5、缓存未经缓存过的response
当没有缓存的时候我们需要去请求网络,接下来看如何请求建立链接的

4.1.5 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.
    //我们需要网络来满足这个请求。可能是为了验证条件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);
  }

实际上建立连接就是创建了一个 HttpCodec 对象,它将在后面的步骤中被使用,那它又是何方神圣呢?它是对 HTTP 协议操作的抽象,有两个实现:Http1CodecHttp2Codec,顾名思义,它们分别对应 HTTP/1.1 和 HTTP/2 版本的实现。

在Http1Codec中,它利用 OkioSocket的读写操作进行封装,Okio 以后有机会再进行分析,现在让我们对它们保持一个简单地认识:它对java.io和java.nio进行了封装,让我们更便捷高效的进行 IO 操作。

而创建HttpCodec对象的过程涉及到StreamAllocationRealConnection,代码较长,这里就不展开,这个过程概括来说,就是找到一个可用的RealConnection,再利用RealConnection的输入输出(BufferedSource和BufferedSink)创建HttpCodec对象,供后续步骤使用。(还在研究这里)

4.1.6 NetworkInterceptors 配置OkHttpClient时设置的 NetworkInterceptors。就是正常的配置,

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

解释:
1、检查请求方法,用Httpcodec处理request
2、进行网络请求得到response,最终返回respone

同步请求总结:前面说了拦截器用了责任链设计模式,它将请求一层一层向下传,知道有一层能够得到Resposne就停止向下传递,然后将response向上面的拦截器传递,然后各个拦截器会对respone进行一些处理,最后会传到RealCall类中通过execute来得到esponse。

4.2异步请求
上面我们看的是call的同步,接下来看call的enqueue()(异步调用)

//异步调用的方法
@Override public void enqueue(Callback responseCallback) {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    captureCallStackTrace();
    eventListener.callStart(this);
    client.dispatcher().enqueue(new AsyncCall(responseCallback));
  }

看最好一行代码,调用了Dispatcher类中的enqueue(Call ),继续看:

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

解释:如果中的runningAsynCalls不满,且call占用的host小于最大数量,则将call加入到runningAsyncCalls中执行,同时利用线程池执行call;否者将call加入到readyAsyncCalls中。runningAsyncCalls和readyAsyncCalls是什么呢?看下面:

/** 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<>(); //同步请求

call加入到线程池中执行了。现在再看AsynCall的代码,它是RealCall中的内部类:

//异步请求
    final class AsyncCall extends NamedRunnable {
        private final Callback responseCallback;

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

AysncCall中的execute()中的方法,同样是通过Response response = getResponseWithInterceptorChain();来获得response,这样异步任务也同样通过了interceptor,剩下的流程就和上面一样了。

五、整体流程图(正在绘制。。。)

六、总结:本文就okhttp的使用及源码进行分析,但是分析过程是由调用的方法去源码中查找,然后一点一点的拨开,感觉还是欠缺思路,在此特备感谢我参考两篇文章,给我指引了方向,如有侵权还请告知,接下还会继续阅读该源码,因为有的地方还么有吃透,再此提醒各位,看源码时不要太纠结于代码如何实现,“只见树木,不见森林”,要自己跳出来,希望本文对有需要得人能有所帮助,
参考博客:https://blog.piasy.com/2016/07/11/Understand-OkHttp/ 源码分析

参考博客:http://www.jianshu.com/p/27c1554b7fee 更透彻
如有侵权还请告知,立即改正!感谢生活、感谢科技、感谢分享!

你可能感兴趣的:(面试准备)