OkHttp是怎么工作的 - 从介绍到分析

OkHttp

Retrofit使用OkHttp,Volley支持替换底层http栈为OkHttp,甚至Google的最新源码里,都用起了OkHttp,替换了原来用的HttpClient。

笔者虽然一直听说OkHttp怎么怎么好,但始终云里雾里究竟它如何优越,所以抽空瞄了瞄源码,小小分析一下。

官网说了什么

okhttp官网

  • HTTP/2 和 SPDY 支持允许所有到同一个host的请求共享一个socket。
  • 连接池减少请求延时 (如果 SPDY 不可用).
  • 透明的GZIP支持,减少下载大小。
  • 缓存Response以减少网络去做完全重复的请求。

OkHttp在网络有问题的时候表现很好:
- 它会静默从常见的连接问题中恢复。
- 如果你的服务有多个IP地址对应,OkHttp会在首次连接失败的时候尝试其他地址。
- OkHttp使用现代的TLS features (SNI, ALPN) 来初始化连接, 并在握手失败的时候倒回到TLS 1.0。

OkHttp 2.0的API设计为流式builders和immutability,同时支持同步blocking call和异步带callback的call。

你可以不用重写网络层代码来试试OkHttp。okhttp-urlconnection模块实现了大家熟悉的java.net.HttpURLConnection API,而okhttp-apache模块实现了Apache HttpClient API.

OkHttp支持Android 2.3和以上,对Java要求至少1.7。

对了,OkHttp还用了Okio来做快速I/O和可调整大小的buffer。

使用

分别来看看Get和Post请求吧

Get请求并获得response body:

OkHttpClient client = new OkHttpClient();

String run(String url) throws IOException {
  Request request = new Request.Builder()
      .url(url)
      .build();

  Response response = client.newCall(request).execute();
  return response.body().string();
}

Post请求,看上去和上面的差别只在于Request的Builder多了.post(body):

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

OkHttpClient client = new OkHttpClient();

String post(String url, String json) throws IOException {
  RequestBody body = RequestBody.create(JSON, json);
  Request request = new Request.Builder()
      .url(url)
      .post(body)
      .build();
  Response response = client.newCall(request).execute();
  return response.body().string();
}

分析

这么多module,里面的类也不算少,怎么看起呢,不如就以使用里面的代码作为切入点吧。

OkHttpClient

作为入口,OkHttpClient里面包含了各种东西,OkHttpClient:

  // 之前提到的协议
  private static final List DEFAULT_PROTOCOLS = Util.immutableList(
      Protocol.HTTP_2, Protocol.SPDY_3, Protocol.HTTP_1_1);
  // TLS那些事
  private static final List DEFAULT_CONNECTION_SPECS = Util.immutableList(
      ConnectionSpec.MODERN_TLS, ConnectionSpec.COMPATIBLE_TLS, ConnectionSpec.CLEARTEXT);

  // 这里实现了各种抽象方法,初始化了Intelnal单例,提供给各处使用
  static {
    Internal.instance = new Internal() {
  }

  /** 懒加载(HTTPS那些事) */
  private static SSLSocketFactory defaultSslSocketFactory;
  // 也是之前提过的,多个IP时候的选择策略
  private final RouteDatabase routeDatabase;
  // 异步请求执行时候的策略,用到了ExecutorService作线程池,默认创建的是core 0,max Integer.MAX_VALUE,keep alive 60秒的线程池
  private Dispatcher dispatcher;
  // 代理嘛,大家都懂得,分为DIRECT(直连无代理)、HTTP和SOCKS
  private Proxy proxy;
  private List protocols;
  private List connectionSpecs;
  // 拦截器 - 在Response正式返回前处理数据,比如Gzip解压
  private final List interceptors = new ArrayList<>();
  // 这个网络拦截器可就牛逼了。。。相较前面那个发生地更早,在HttpEngine的readResponse中被调用
  // 可以做诸如CheckHandshake初始化的时候加进去了一个握手的拦截器去检查是否在黑名单;LoggingInterceptors则加了一个log的拦截器,输出请求和response的信息;Progress加了个包装response的拦截器,来增加progress功能;RewriteResponseCacheControl直接重写server的cache控制头
  private final List networkInterceptors = new ArrayList<>();
  // 代理服务器选择器
  private ProxySelector proxySelector;
  // Cookie处理器,可以接受get和put cookie的事件
  private CookieHandler cookieHandler;

  // 就是个接口
  /** Non-null if this client is caching; possibly by {@code cache}. */
  private InternalCache internalCache;
  // 可以自定义,内部包含了上面的InternalCache实现
  private Cache cache;

  // 都是java的东西
  private SocketFactory socketFactory;
  private SSLSocketFactory sslSocketFactory;
  private HostnameVerifier hostnameVerifier;

  // 证书验证
  private CertificatePinner certificatePinner;
  // 回复服务器认证要求
  private Authenticator authenticator;
  // 连接池,同一个host的请求可能共享同一个connection,该类还实现了connection为以后的使用保持打开的策略
  private ConnectionPool connectionPool;
  // 接口,实际使用了InetAddress.getAllByName
  private Network network;

  private boolean followSslRedirects = true;
  private boolean followRedirects = true;
  private boolean retryOnConnectionFailure = true;

  // 其实10_000就是10000,这里是10000毫秒即10秒
  private int connectTimeout = 10_000;
  private int readTimeout = 10_000;
  private int writeTimeout = 10_000;

再看看构造函数:


  public OkHttpClient() {
    routeDatabase = new RouteDatabase();
    dispatcher = new Dispatcher();
  }

  private OkHttpClient(OkHttpClient okHttpClient) {
    // 反正就是各种参数拷贝过来
  }

那么当我们调用new OkHttpClient()的时候,其实就是new了两个成员变量。

嗯。。。好像很轻,还是看看具体的请求执行吧

Response response = client.newCall(request).execute();

看来具体执行是在Call里面

public class Call {
  private final OkHttpClient client;

  // Guarded by this.
  private boolean executed;
  volatile boolean canceled;

  /** The application's original request unadulterated by redirects or auth headers. */
  Request originalRequest;
  HttpEngine engine;

原来是Call持有了HttpEngine,Call是一个待准备执行的请求,可以被取消,由于它代表了单个的请求/回复 对,所以不能被执行两次。

具体execute的执行:

  public Response execute() throws IOException {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    try {
      // 结果又交给了OkHttpClient的Dispatcher去执行
      client.getDispatcher().executed(this);
      Response result = getResponseWithInterceptorChain(false);
      if (result == null) throw new IOException("Canceled");
      return result;
    } finally {
      client.getDispatcher().finished(this);
    }
  }

嗯…我们再跳Dispatcher:

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

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

直接就丢进了队列里。坑爹呢?不执行了?!不是叫executed嘛?!收拾好日了狗的心情,我们继续看下去…

你可能感兴趣的:(Android)