OkHttp使用与源码学习(一)

前言

从开始使用OkHttp已经很久了,它是一个很nice的网络框架,其功能强大,可进行失败重连,在连接中出问题时自动恢复。最近在复习的时候学了它的源码,以下主要是写它的使用和源码学习。

使用

  • GET请求
//异步请求
OkHttpClient client=new OkHttpClient();
Request request=new Request.Builder()
        .url("url")
        .addHeader("headerName","headerValue")//添加头部信息
        .build();
Call call=client.newCall(request);
call.enqueue(new Callback() {
    @Override
    public void onFailure(Request request, IOException e) {
        //请求失败
    }
//此时的OnResponse方法也不是在UI线程中
    @Override
    public void onResponse(Response response) throws IOException {
        //请求成功
    }
});

//同步请求,需放在子线程中
OkHttpClient mOkHttpClient=new OkHttpClient();
final Request request=new Request.Builder().url("url")
        .build();
Call call=mOkHttpClient.newCall(request);
try {
    Response response=call.execute();
    if (response.isSuccessful()){
        response.message();
        response.code();
        response.body().string();
    }
} catch (IOException e) {
    e.printStackTrace();
}

  • POST请求
//异步请求
OkHttpClient client=new OkHttpClient();
FormEncodingBuilder builder=new FormEncodingBuilder();
builder.add("psw","123");//参数
builder.add("name","asendi");
Request request=new Request.Builder()
        .url("url")
        .post(builder.build())
        .build();
client.newCall(request).enqueue(new Callback() {
    @Override
    public void onFailure(Request request, IOException e) {
    }
    //此时的OnResponse方法也不是在UI线程中
    @Override
    public void onResponse(Response response) throws IOException {
    }
});

  • 上传文件
OkHttpClient client=new OkHttpClient();
File file=new File(path);
RequestBody fileBody=RequestBody.create(MediaType.parse("File/*"), file);
Request request=new Request.Builder()
        .url("url")
        .post(fileBody)
        .build();

  • 上传文件+带参数
OkHttpClient client=new OkHttpClient();
        File file=null;
        RequestBody fileBody=
		RequestBody.create(MediaType.parse("application/octet-stream"), file);
        MultipartBuilder build=new MultipartBuilder()
                                .type(MultipartBuilder.FORM)
					.addFormDataPart("username", "asendi")//添加参数
					.addFormDataPart("uId", "1")
                                .addPart(Headers.of("Content-Disposition",
                                        "form-data; name=\"username\""),
                                        RequestBody.create(null,"阿森弟"))
                                .addPart(Headers.of("Content-Disposition",
                                        "form-data; name=\"mFile\";" +
                                         "filename=\"...\""), fileBody);
        RequestBody requestBody=build.build();

源码学习

  • OkHttpClient的创建
public OkHttpClient() {
  this(new Builder());
}
public Builder() {
  dispatcher = new Dispatcher();
  protocols = DEFAULT_PROTOCOLS;
  connectionSpecs = DEFAULT_CONNECTION_SPECS;
  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;
}

在Builder中对里边的配置进行了初始化。

  • 创建Call
newCall(request)

@Override public Call newCall(Request request) {
  return new RealCall(this, request, false /* for web socket */);
}

实际上是创建RealCall对象出来。

  • 开始请求

同步请求

@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);//从请求队列中移除
  }
}

由以上代码可知做了四件事:

  1. 同步操作,防止同时发起请求
  2. 将Call本身添加到请求队列中
  3. 进入发起请求并返回获取的响应结果
  4. 将Call从请求队列中移除
  • getResponseWithInterceptorChain()方法
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);//责任链的开始
}

此方法里边创建了多个拦截器,而这些拦截器才是真正的主角,然后开始了责任链模式,各个拦截器执行特定的功能。

  • RealInterceptorChain#proceed()
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
    Connection connection) throws IOException {
  ......//错误检测省略

//开始逐个执行拦截器
  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().
  ......
  return response;
}

```java
* CacheInterceptor
CacheInterceptor是用于对缓存处理的,它的intercept方法如下:

```java
@Override public Response intercept(Chain chain) throws IOException {
    Response cacheCandidate = cache != null
        ? cache.get(chain.request())
        : null;//获取缓存中的响应

    long now = System.currentTimeMillis();
	//构建一个缓存策略
    CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();
    Request networkRequest = strategy.networkRequest;
    Response cacheResponse = strategy.cacheResponse;

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

    if (cacheCandidate != null && cacheResponse == null) {
      closeQuietly(cacheCandidate.body()); // The cache candidate wasn't applicable. Close it.
    }

    // 如果没有缓存,并且没有网络请求
    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(Util.EMPTY_RESPONSE)
          .sentRequestAtMillis(-1L)
          .receivedResponseAtMillis(System.currentTimeMillis())
          .build();
    }

    // 如果只是没有网络请求
    if (networkRequest == null) {
      return cacheResponse.newBuilder()
          .cacheResponse(stripBody(cacheResponse))
          .build();
    }

    Response networkResponse = null;
    try {
      networkResponse = chain.proceed(networkRequest);
    } finally {
      // If we're crashing on I/O or otherwise, don't leak the cache body.
      if (networkResponse == null && cacheCandidate != null) {
        closeQuietly(cacheCandidate.body());
      }
    }

    // 存在缓存
    if (cacheResponse != null) {
      if (networkResponse.code() == HTTP_NOT_MODIFIED) {
      //发起请求获取响应
        Response response = cacheResponse.newBuilder()
            .headers(combine(cacheResponse.headers(), networkResponse.headers()))
            .sentRequestAtMillis(networkResponse.sentRequestAtMillis())
            .receivedResponseAtMillis(networkResponse.receivedResponseAtMillis())
            .cacheResponse(stripBody(cacheResponse))
            .networkResponse(stripBody(networkResponse))
            .build();
        networkResponse.body().close();

        // 更新缓存
        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)) {
      CacheRequest cacheRequest = maybeCache(response, networkResponse.request(), cache);
      response = cacheWritingResponse(cacheRequest, response);
    }

    return response;
  }

以上方法主要功能如下:

  1. 根据请求获取缓存中对应的响应

  2. 构建缓存策略

  3. 发送请求,将获取到的响应进行缓存

  4. CallServerInterceptor
    CallServerInterceptor是发起请求并获取响应,它的intercept()方法如下:

@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) {
      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()
        .request(request)
        .handshake(streamAllocation.connection().handshake())
        .sentRequestAtMillis(sentRequestMillis)
        .receivedResponseAtMillis(System.currentTimeMillis())
        .build();

    int code = response.code();
    ......//做进一步的检测

    return response;
  }

此方法主要执行了:

  1. 构建一个HttpCodec 对象,HttpCodec 有两个实现类,分别对应http的版本
  2. 发送请求头
  3. 发送请求体
  4. 获取响应并返回

异步请求

  • enqueue(Callback)方法
  @Override public void enqueue(Callback responseCallback) {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    captureCallStackTrace();
    client.dispatcher().enqueue(new AsyncCall(responseCallback));
  }

同样是进行同步检测防止同时多次发起请求,然后由Dispatcher开始进入请求

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

以上代码为:如果当前正在执行队列未超过限制数量,则直接放入线程池执行该任务,否则放到预备执行的异步任务队列中区。

AsyncCall是Call中的一个内部类,继承与NamedRunnable(run方法中执行execute方法,execute方法为抽象方法)

  • AsyncCall#execute()
  @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);
      }
    }
  }

它里边执行的是:

  1. 执行getResponseWithInterceptorChain(),里面发起网络请求并返回响应。
  2. 根据不同的情况进行回调,也就是传进来的Callback的成功或者失败回调
  3. 最后调用Dispatcher的finished方法

前面getResponseWithInterceptorChain方法有看了,所以看下finished方法

  • Dispatcher#finished()
  private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {
    int runningCallsCount;
    Runnable idleCallback;
    synchronized (this) {
      if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
      if (promoteCalls) promoteCalls();
      runningCallsCount = runningCallsCount();
      idleCallback = this.idleCallback;
    }

    if (runningCallsCount == 0 && idleCallback != null) {
      idleCallback.run();
    }
  }

主要是finished方法里边调用了promoteCalls方法

  • promoteCalls()
  private void promoteCalls() {
    if (runningAsyncCalls.size() >= maxRequests) return; // Already running max capacity.
    if (readyAsyncCalls.isEmpty()) return; // No ready calls to promote.

    for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
      AsyncCall call = i.next();

      if (runningCallsForHost(call) < maxRequestsPerHost) {
        i.remove();
        runningAsyncCalls.add(call);
        executorService().execute(call);
      }

      if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity.
    }
  }

promoteCalls方法里面首先进行判断,如果正在执行的任务超过最大请求数,或者没有预备执行的任务,则直接返回。否则将预备队列的任务添加到正在执行队列,并且添加到线程池中执行。

以上是对OkHttp走一遍流程,最后是一张流程图。
OkHttp使用与源码学习(一)_第1张图片

你可能感兴趣的:(Android)