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

来源:互联网 发布:数据挖掘 关联规则 编辑:程序博客网 时间:2024/05/18 02:45

对于网络请求,一直都是在用一些优秀的开源框架如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();//创建一个callCall 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<Protocol> protocols; //协议    final List<ConnectionSpec> connectionSpecs; //传输层版本和连接协议    final List<Interceptor> interceptors; //拦截器    final List<Interceptor> 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<String> 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<Interceptor> 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<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<>(); //同步请求

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 更透彻
如有侵权还请告知,立即改正!感谢生活、感谢科技、感谢分享!

原创粉丝点击