okHttp 源码分析

OkHttp是越来越火了,安卓4.4开始,谷歌在HttpUrlConnection底层也开始使用OkHttp实现。

public final class URL implements Serializable {
  public URL(String spec) throws MalformedURLException {
    this((URL) null, spec, null);
  }
 public URL(URL context, String spec, URLStreamHandler handler) 
          throws MalformedURLException {
     ······
     setupStreamHandler();
     ······
  }
  void setupStreamHandler() {
      ......
    if (protocol.equals("file")) {
        streamHandler = new FileHandler();
    } else if (protocol.equals("ftp")) {
        streamHandler = new FtpHandler();
    } else if (protocol.equals("http")) {
        // http/https协议,使用的是okhttp.
        String name = "com.android.okhttp.HttpHandler";
        streamHandler = (URLStreamHandler) Class.forName(name).newInstance();
    } else if (protocol.equals("https")) {
        String name = "com.android.okhttp.HttpsHandler";
        streamHandler = (URLStreamHandler) Class.forName(name).newInstance();
    } ······
}
  public URLConnection openConnection() throws IOException {
      return streamHandler.openConnection(this);
  }

now就从源码的角度来分析解剖下OkHttp的内部实现。

     Request request = new Request.Builder().url(mUrl).build();
     OkHttpClient client = new OkHttpClient();//demo构造代码
     client.newCall(request).enqueue(new Callback() {
         @Override //请求失败
         public void onFailure(Call call, IOException e) {}
         @Override//请求成功
         public void onResponse(Call call, Response response) throws IOException {}
     });

1.Request 构建

Request构建采用了Builder设计模式,以处理复杂的参数模型,包括urlHeadermethodRequestBody,以及tag
在传入url后利用HttpUrl对象获取请求的属性,以如下url为例:"http://baidu.com/pathSegment/pathSegment2?ie=utf-8&wd=CSDN#fragment"
1.scheme(HTTP) 通过url.regionMatches()匹配
2.port (80) http默认80 https默认443,其它-1
3.host (baidu.com)
4.queryNamesAndValues [ie,utf-8, wd,CSDN],以key,value成对顺序存储的arrayList
5.pathSegments [pathSegment,pathSegment2]
6.fragment (fragment)。

注意点
添加headparam时不可传入null,否则会直接抛出运行时异常,因此在实际使用中对request进行装饰设计是十分必要的。

2. OkHttpClient()的构造

  static final List DEFAULT_PROTOCOLS = Util.immutableList(
      Protocol.HTTP_2, Protocol.HTTP_1_1);

  static final List DEFAULT_CONNECTION_SPECS = Util.immutableList(
      ConnectionSpec.MODERN_TLS, ConnectionSpec.CLEARTEXT);

  public OkHttpClient() {this(new Builder());}
  public static final class 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做个性配置后采用单例模式生成OkHttpClientRetrofit在此暂不讨论

3.newCall() 与 enqueue()

  @Override public Call newCall(Request request) {
    return new RealCall(this, request, false /* for web socket */);
  }
  ······
  RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
    this.client = client;
    this.originalRequest = originalRequest;
    this.forWebSocket = forWebSocket;
    this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);
  }

newCall()的内部实现只简单构造了一个RealCall对象

注意点
1.OK是支持webSocket链接的
2.构造函数中定义了一个RetryAndFollowUpInterceptor拦截器

异步请求enqueue()

  @Override public void enqueue(Callback responseCallback) {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    captureCallStackTrace();
    client.dispatcher().enqueue(new AsyncCall(responseCallback));
  }
  • 异步请求只有一步流程,就是构建一个AsyncCall,并把它添加到dispatcherrunningAsyncCalls队列中,AsyncCall本质是一个Runnable的装饰类。动态为当前线程设置Name属性,在run方法中调用execute()
final class AsyncCall extends NamedRunnable {
    private final Callback responseCallback;

    AsyncCall(Callback responseCallback) {
      super("OkHttp %s", redactedUrl());
      this.responseCallback = responseCallback;
    }

    @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.callBackonResponse()onFailure()都是在子线程中执行
2.在开发中通常在Activity结束时cancel请求,则会在callback.onFailure()回调给开发层,如果使用匿名内部类做接口回调需防范内存泄漏问题

同步请求execute()

  @Override public Response execute() throws IOException {
    ······
    try {
      //在dispatcher的runningSyncCalls队列中添加这个请求
      client.dispatcher().executed(this);
      //处理请求,得到响应
      Response result = getResponseWithInterceptorChain();
      if (result == null) throw new IOException("Canceled");
      return result;
    } finally {
      //从diapatcher的runningSyncCalls队列中移除这个请求
      client.dispatcher().finished(this);
    }
  }
  • 在同步请求流程中,dispatcher只负责了realCall状态的记录,没有控制调度

4.dispatcher--OkHttp的流水线

public final class Dispatcher {
  private int maxRequests = 64;      
  private int maxRequestsPerHost = 5;
  private Runnable idleCallback;     
  private ExecutorService executorService;
  //等待状态的异步队列
  private final Deque readyAsyncCalls = new ArrayDeque<>();
  //运行状态的异步队列
  private final Deque runningAsyncCalls = new ArrayDeque<>();
  //运行状态的同步队列
  private final Deque runningSyncCalls = new ArrayDeque<>();
  synchronized void enqueue(AsyncCall call) {
    if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
      runningAsyncCalls.add(call);
      executorService().execute(call);
    } else {
      readyAsyncCalls.add(call);
    }
  }
  public synchronized ExecutorService executorService() {
    if (executorService == null) {
      executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
          new SynchronousQueue(), Util.threadFactory("OkHttp Dispatcher", false));
    }
    return executorService;
  }
  • 最多支持64个异步请求并发
  • 同一个host下最多支持5个连接
  • 懒加载了一个线程池,常驻线程数量0,最大容量无限量。
  • 当试图添加异步请求时有个双重条件判断,满足会立即执行,否则添加到等待队列
    runningAsyncCalls.size() < maxRequests
    运行中的异步请求数量小于MaxRequest(默认64)
    runningCallsForHost(call) < maxRequestsPerHost
    指向的host当前连接请求数小于maxRequestsPerHost(默认5)

request结束后的队列优化

  • 异步请求结束时
    void finished(AsyncCall call) {finished(runningAsyncCalls, call, true);}
  • 同步请求结束时
    void finished(RealCall call) {finished(runningSyncCalls, call, false);}
    关注finished(Deque, T, boolean)方法
  private  void finished(Deque calls, T call, boolean promoteCalls) {
    ······
    synchronized (this) {
      if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
      if (promoteCalls) promoteCalls();   //轮询等待队列中的异步请求
      ······
    }
}

1.首先将结束的请求从相应的队列中移除
2.如果是异步请求,则会刷新异步等待队列,从备用的readyAsyncCalls中取出AsyncCall以使其获得执行机会。如下

  private void promoteCalls() {
    if (runningAsyncCalls.size() >= maxRequests) return; // Already running max capacity.
    if (readyAsyncCalls.isEmpty()) return; // No ready calls to promote.
    健壮性判断
    for (Iterator i = readyAsyncCalls.iterator(); i.hasNext(); ) {
      AsyncCall call = i.next();
      if (runningCallsForHost(call) < maxRequestsPerHost) {
        i.remove();
        runningAsyncCalls.add(call);
        executorService().execute(call);
      }
      for循环的继续执行条件在这里添加
      if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity.
    }
  }

readyAsyncCalls中取出一个AsyncCall,并添加到runningAsyncCalls中执行

无论在异步请求asyncCall或者是同步请求execute中我们都见到了具体的网络执行是在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);
    return chain.proceed(originalRequest);
  }

注意数据结构,这里采用了ArrayList来记录所有的拦截器Interceptor按序排列如下

  • client.interceptors() → 通过OkHttpClient#Builder.addInterceptor()添加的拦截器,通常是做一些添加统一的token之类操作
  • retryAndFollowUpInterceptor→ 在RealCall()构造方法中实例化,负责错误重试和请求重定向
  • BridgeInterceptor→添加网络请求相关的必要的一些请求头,比如Content-Type、Content-Length、Transfer-Encoding、Host、User-Agent等等
  • CacheInterceptor→处理缓存相关操作,通过OkHttpClient#Builder.cache()或者setInternalCache()添加
  • ConnectInterceptor → 负责与服务器进行连接的操作
  • networkInterceptors → 通过OkHttpClient#Builder.addNetworkInterceptor()添加的拦截器,注意它和interceptors的顺序不同
  • CallServerInterceptor→这个拦截器中进行最终的网络请求
    最终定义一个拦截器数据链Interceptor.Chain,通过proceed()intercept()完成操作如下所示
    okhttp拦截器原理

先写到这里,抽空我会把OkHttp的缓存机制与IO 分别整理一遍

你可能感兴趣的:(okHttp 源码分析)