okhttp1

okhttp分享一:okhttp基本使用及interceptor链

本文主要内容如下

  • okhttp 简介
  • okhttp 使用
  • okhttp 请求构建基础类源码分析

一、okhttp简介

现在我们说的okhttp一般是指okhttp3.0之后的版本,okhttp3.0之后square公司对okhttp框架进行了重构,解决了很多2.x中存在的bug,引入了拦截器模式,支持了更多的http字段配置,并最终形成了现在的okhttp框架。本文所有的代码分析均基于okhttp 3.12.0版本,okhttp最近推出了okhttp4.0版本,用kotlin重写了一遍okhttp,但逻辑与最新的3.x保持一致。
我看到很多文章都会比较okhttp与volley,再讲解okhttp之前,我们先来简单理一下okhttp、volley、httpurlconnection这三个安卓侧使用最广的几个网络请求框架,先看示意图


android网络框架对比.jpg

实际上volley是在httpurlconnection的基础上进行了进一步的封装,加入了一些线程管理,缓存逻辑,其底层网络发送用根据版本不同(>2.2)分别使用了httpurlconnection、httpclient;而httpurlconnection在4.4后底层是使用okhttp2.x实现的,以下是android源码中集成的okhttp版本对应情况

  • Android 4.4.4_r1 : okhttp 1.1.2
  • Android 5.0.1_r1 : okhttp 2.0.0
  • Android 6.0.1_r1 : okhttp 2.4.0
  • Android 7.1.0_r1 : okhttp 2.6.0

可以看到,android7才集成了2.6.0的okhttp,因此虽然说httprulconnection底层使用的是okhttp,但并不是我们现在普遍说的okhttp(3.x)。在开始看下面的讲解前,大家最好可以看看http、rfc协议相关知识,okhttp基本就是http协议在android侧的实现与优化。

okhttp优点如下

  • 支持spdy、http2、websocket
  • 根据策略选择最优路线
  • 有自动维护的链接池,从代码层面复用tcp链接,减少握手次数
  • 支持同步与异步请求,通过线程池处理并发
  • 责任链模式,模块功能明晰,扩展性好。
  • 使用基于NIO的okio框架代替javaio,速度快
  • 严格遵循http、rfc协议

二、okhttp使用

2.1、get

  OkHttpClient client = new OkHttpClient();

  Request request = new Request.Builder()
      .url(url)
      .build();
  // 同步    
  // Response response = client.newCall(request).execute();
  // 异步
  client.newCall(request).enqueue(new Callback() {
                                      @Override
                                      public void onFailure(Call call, IOException e) {
                                          ...
                                      }
                          
                                      @Override
                                      public void onResponse(Call call, Response response) throws IOException {
                                          ...
                                       });

2.2、POST

post和get的不同在于对Request请求的构造不同(因为post需要携带参数),post方式中的Request需要传递一个RequestBody作为post的参数。RequestBody有两个子类:FormBody和MultipartBody。
这两个子类分别对应表单提交和文件上传,除此以外,requestBody还可以用json格式。以json格式为例,使用方式如下。

public static final MediaType JSON
    = MediaType.parse("application/json; charset=utf-8");

  OkHttpClient client = new OkHttpClient();

  RequestBody body = RequestBody.create(JSON, json);

  Request request = new Request.Builder()
      .url(url)
      .post(body)
      .build();
// 同步
// Response response = client.newCall(request).execute();
// 异步
  client.newCall(request).enqueue(new Callback() {
                                      @Override
                                      public void onFailure(Call call, IOException e) {
                                          ...
                                      }
                          
                                      @Override
                                      public void onResponse(Call call, Response response) throws IOException {
                                          ...
                                       });

三、okhttp 请求构建基础类源码分析

根据从上面的GET请求,显示用builder构建了Request对象,然后执行了OKHttpClient.java的newCall方法,那么我们先下看这个newCall里面都做了什么

3.1 Call及RealCall

/**
 * A call is a request that has been prepared for execution. A call can be canceled. As this object
 * represents a single request/response pair (stream), it cannot be executed twice.
 */
public interface Call extends Cloneable {
  /** Returns the original request that initiated this call. */
  Request request();

  /**
   * Invokes the request immediately, and blocks until the response can be processed or is in
   * error.
   *
   * 

To avoid leaking resources callers should close the {@link Response} which in turn will * close the underlying {@link ResponseBody}. * *

{@code
   *
   *   // ensure the response (and underlying response body) is closed
   *   try (Response response = client.newCall(request).execute()) {
   *     ...
   *   }
   *
   * }
* *

The caller may read the response body with the response's {@link Response#body} method. To * avoid leaking resources callers must {@linkplain ResponseBody close the response body} or the * Response. * *

Note that transport-layer success (receiving a HTTP response code, headers and body) does * not necessarily indicate application-layer success: {@code response} may still indicate an * unhappy HTTP response code like 404 or 500. * * @throws IOException if the request could not be executed due to cancellation, a connectivity * problem or timeout. Because networks can fail during an exchange, it is possible that the * remote server accepted the request before the failure. * @throws IllegalStateException when the call has already been executed. */ Response execute() throws IOException; /** * Schedules the request to be executed at some point in the future. * *

The {@link OkHttpClient#dispatcher dispatcher} defines when the request will run: usually * immediately unless there are several other requests currently being executed. * *

This client will later call back {@code responseCallback} with either an HTTP response or a * failure exception. * * @throws IllegalStateException when the call has already been executed. */ void enqueue(Callback responseCallback); /** Cancels the request, if possible. Requests that are already complete cannot be canceled. */ void cancel(); /** * Returns true if this call has been either {@linkplain #execute() executed} or {@linkplain * #enqueue(Callback) enqueued}. It is an error to execute a call more than once. */ boolean isExecuted(); boolean isCanceled(); /** * Create a new, identical call to this one which can be enqueued or executed even if this call * has already been. */ Call clone(); interface Factory { Call newCall(Request request); } }

Call是一个接口类,从注释可以了解,Call可以理解为对一次请求的封装,需要对请求的操作,Call中都有对应方法。Call接口内部提供了Factory工厂方法模式(将对象的创建延迟到工厂类的子类去进行,从而实现动态配置)。
OKHttpClient实现了Call.Factory接口,返回了一个RealCall对象。那我们就来看下RealCall这个类。

final class RealCall implements Call {
    final OkHttpClient client;
    final RetryAndFollowUpInterceptor retryAndFollowUpInterceptor;

    /**
     * There is a cycle between the {@link Call} and {@link EventListener} that makes this awkward.
     * This will be set after we create the call instance then create the event listener instance.
     */
    private EventListener eventListener;

    /**
     * The application's original request unadulterated by redirects or auth headers.
     */
    final Request originalRequest;
    final boolean forWebSocket;

    // Guarded by this.
    private boolean executed;

    private RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
        this.client = client;
        this.originalRequest = originalRequest;
        this.forWebSocket = forWebSocket;
        this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);
    }

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

    @Override
    public Request request() {
        return originalRequest;
    }


// 同步请求
    @Override
    public Response execute() throws IOException {
        synchronized (this) {
            //只能执行一次
            if (executed) throw new IllegalStateException("Already Executed");
            executed = true;
        }
        captureCallStackTrace();
        eventListener.callStart(this);
        try {
            //  进入请求入队,进行队列管理
            client.dispatcher().executed(this);
           //   构建interceptor链并发送请求获取响应
            Response result = getResponseWithInterceptorChain();
            if (result == null) throw new IOException("Canceled");
            return result;
        } catch (IOException e) {
            eventListener.callFailed(this, e);
            throw e;
        } finally {
           // 请求结束,出队
            client.dispatcher().finished(this);
        }
    }

    private void captureCallStackTrace() {
        Object callStackTrace = Platform.get().getStackTraceForCloseable("response.body().close()");
        retryAndFollowUpInterceptor.setCallStackTrace(callStackTrace);
    }



// 异步请求
    @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));
    }

    @Override
    public void cancel() {
        retryAndFollowUpInterceptor.cancel();
    }

    @Override
    public synchronized boolean isExecuted() {
        return executed;
    }

    @Override
    public boolean isCanceled() {
        return retryAndFollowUpInterceptor.isCanceled();
    }

    @SuppressWarnings("CloneDoesntCallSuperClone")
    // We are a final type & this saves clearing state.
    @Override
    public RealCall clone() {
        return RealCall.newRealCall(client, originalRequest, forWebSocket);
    }

    StreamAllocation streamAllocation() {
        return retryAndFollowUpInterceptor.streamAllocation();
    }

    final class AsyncCall extends NamedRunnable {
        private final Callback responseCallback;

        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 {
                    eventListener.callFailed(RealCall.this, e);
                    responseCallback.onFailure(RealCall.this, e);
                }
            } finally {
                client.dispatcher().finished(this);
            }
        }
    }

    /**
     * Returns a string that describes this call. Doesn't include a full URL as that might contain
     * sensitive information.
     */
    String toLoggableString() {
        return (isCanceled() ? "canceled " : "")
                + (forWebSocket ? "web socket" : "call")
                + " to " + redactedUrl();
    }

    String redactedUrl() {
        return originalRequest.url().redact();
    }

    Response getResponseWithInterceptorChain() throws IOException {
        // Build a full stack of interceptors.
        List 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, this, eventListener, client.connectTimeoutMillis(),
                client.readTimeoutMillis(), client.writeTimeoutMillis());

        return chain.proceed(originalRequest);
    }
}

首先看下execute()方法,该方法是同步请求用的方法,该方法首先获取client对象中的dispatcher对象并调用其executed(RealCall call)方法,之后调用getResponseWithInterceptorChain方法构建拦截器链,并通过拦截器链发送请求获取response。okhttp中dispatcher负责任务分发、线程管理,虽然okhttpclient提供了外部传入dispatcher的api,但是Dispatcher类为final,也不建议自定义dipatcher传入。下面我们看一下先简单看下Dispatcher类中同步请求相关部分。

  /** Running synchronous calls. Includes canceled calls that haven't finished yet. */
  private final Deque runningSyncCalls = new ArrayDeque<>();

   ...

/** Used by {@code Call#execute} to signal it is in-flight. */
  synchronized void executed(RealCall call) {
    runningSyncCalls.add(call);
  }

   ...
/** Used by {@code Call#execute} to signal completion. */
  void finished(RealCall call) {
    finished(runningSyncCalls, call, false);
  }

  private  void finished(Deque 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();
    }
  }

调用的executed方法实际上只将传入的call加入了一个runningSyncCalls的双向队列,而finish()方法就是将该call从队列中移除。因为是同步请求,所以并没有什么任务分发,线程管理,这些在同步请求中应该由外部处理。

再看下RealCall中另一个比较重要的方法getResponseWithInterceptorChain

Response getResponseWithInterceptorChain() throws IOException {
        // Build a full stack of interceptors.
        List 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, this, eventListener, client.connectTimeoutMillis(),
                client.readTimeoutMillis(), client.writeTimeoutMillis());

        return chain.proceed(originalRequest);
    }

从代码中看,该方法首先构建了一个List interceptors,再利用其生成Interceptor.Chain,最后调用其proceed方法获取response。首先简单介绍下okhttp中的几个基础的interceptor:

  • RetryAndFollowUpInterceptor:重试拦截器
    处理重试的一个拦截器,会去处理一些异常,只要不是致命的异常就会重新发起一次请求(把Request给下级),如果是致命的异常就会抛给上一级;
    处理一些重定向等等,比如 3xx、307、407
  • BridgeInterceptor:
    对于请求,设置一些通用的请求头,Cookie、Connection、Content-Type、Content-Length等;
    对于响应,如果返回的数据被压缩了,采用 ZipSource。处理Cookie;
  • CacheInterceptor:
    缓存拦截器,负责缓存相关处理,根据http协议中对缓存的规定,结合okio维护缓存逻辑。
  • ConnectInterceptor:
    负责建立连接的拦截器,经过该interceptor后,客户端与服务器的链接就已经成功建立。
  • CallServerInterceptor:
    基于ConnectInterceptor建立的链接,给服务器写数据和读取返回数据;

再看一下chain.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++;

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

    return response;
  }

看到在proceed方面里面又new了一个RealInterceptorChain类的next对象,这个next对象和chain最大的区别就是index属性值不同chain是0而next是1,然后取interceptors下标为1的对象的interceptor。由从上文可知,如果没有开发者自定义的Interceptor时,首先调用的RetryAndFollowUpInterceptor,如果有开发者自己定义的interceptor则调用开发者interceptor。
取出interceptor后调用其intercept方法,而每个interceor的intercept方法中又会调用chain.proceed方法,依次循环。到最后一个CallServerInterceptor后中断递归,直接返回response给上一个interceptor,在一层一层处理response后向上返回,最后回到realCall中的execute方法。其流程如下图


interceptor链.jpg

你可能感兴趣的:(okhttp1)