okhttp源码解析(一) 请求流程分析

前言

okhttp是我们安卓中运用最多的网络请求框架了,它的优势有很多,如支持HTTP2,SPDY,GZIP压缩,复用连接池提升效率等。

  • 1.简单使用
//异步请求
            OkHttpClient okHttpClient = new OkHttpClient();
            Request request = new Request.Builder().url("https://www.baidu.com").build();
            okHttpClient.newCall(request1).enqueue(new Callback() {
              @Override
             public void onFailure(Call call, IOException e) {}
              @Override
             public void onResponse(Call call, Response response) throws IOException {}
             });
//同步
            OkHttpClient okHttpClient = new OkHttpClient();
            Request request = new Request.Builder().url("https://www.baidu.com").build();
                try {
                    Response response = okHttpClient.newCall(request).execute();
                } catch (IOException e) {
                    e.printStackTrace();
                }

  • 2.OkHttpClient 分析
 public OkHttpClient() {
    this(new Builder());
  }

public static final class Builder {
    Dispatcher dispatcher;
    @Nullable Proxy proxy;
    List<Protocol> protocols;
    List<ConnectionSpec> connectionSpecs;
    final List<Interceptor> interceptors = new ArrayList<>();
    final List<Interceptor> networkInterceptors = new ArrayList<>();
    EventListener.Factory eventListenerFactory;
    ProxySelector proxySelector;
    CookieJar cookieJar;
    @Nullable Cache cache;
    省略若干.....

  • okhttpClient里有个静态内部类Builder,这里运用了Builder设计模式同步builder动态构建出一些常用的参数,如我们常见的connectTimeout,readTimeout,writeTimeout,Cache,我们自定义的拦截器等。
/**
   * 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 */);
  }
  • newCall 方法是其中最重要的一个方法,通过我们传入的request来发起请求的。

  • RealCall是Call的实现类,方法里通过newRealCall方法把okhttpclient和request传了过去,返回了一个RealCall对象,然后我们通过该对象来发起同步或异步请求。

  • 3.Dispatcher分析
  • 上面我们收到RealCall 是用来发起请求的,发起请求就不得不提到一个类Dispatcher

  • 这个类是在我们构建OkhttpClient的时候初始化的,是okhttpclient的成员

  • 这个类用来管理所有同步或异步请求的,它还有发起异步请求的功能

  //用来处理异步请求的线程池
  private @Nullable ExecutorService executorService;
  //待请求的异步任务队列
  private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
 // 正在请求的异步任务队列
  private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
  //正在请求的同步任务队列
  private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
......
//SynchronousQueue是一个内部只能包含一个元素的队列。插入元素到队列的线程被阻塞,直到另一个线程从队列中获取了队列中存储的元素。同样,如果线程尝试获取元素并且当前不存在任何元素,则该线程将被阻塞,直到线程将元素插入队列。
  public synchronized ExecutorService executorService() {
    if (executorService == null) {
      executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
          new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));
    }
    return executorService;
  }
  • 这里用到的线程池核心线程为0,非核心线程数量为无限,闲置时间为60s
  //同步
  synchronized void executed(RealCall call) {
    runningSyncCalls.add(call);
  }
  //异步
  synchronized void enqueue(AsyncCall call) {
    if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
      runningAsyncCalls.add(call);
      executorService().execute(call);
    } else {
      readyAsyncCalls.add(call);
    }
  }

  • Dispatcher中有二个重要的方法,其中executed方法比较简单是直接把传入的请求加到同步队列中

  • enqueue方法会判断正在执行的请求是否小于64并且同一个host的请求是否小于5,这里传入的AsyncCall 实则为一个Runnable对象,满足条件就把它加入到请求队列并调用线程池执行它

  • 否则就把它加入到等待队列中

  • 4.RealCall 分析
  • 上面我们收到RealCall 是用来发起请求的,它发起请求主要有如下二个方法

//同步
  @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);
      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);
    }
  }

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

  • execute方法调用我们上面提到的dispatcher的executed方法,把call加到同步队列
  • 执行getResponseWithInterceptorChain()来获取Response
  • enqueue也是调用的dispatcher执行的enqueue方法
  • 上面我们分析的enqueue方法,AsyncCall是一个Runnable由dispatcher里面的线程池执行它
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);
          LogUtils.LogE("onFailure2");
        }
      } finally {
        client.dispatcher().finished(this);
      }
    }
  }
  • 可以看到里面的execute的方法最终也是调用了RealCall方法里面的getResponseWithInterceptorChain()方法来得到Response,所以getResponseWithInterceptorChain是最终执行请求得到response的方法
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.
    }
  }
  • 这里简要说下client.dispatcher().finished(this)这个方法,它执行的是dispatcher里面的finished方法,它会把执行完成的call从正在请求的队列中移除并且会执行promoteCalls方法把readyAsyncCalls等待的异步请求中移动到runningAsyncCalls,并且调用线程池执行它
  • 这样就保证了异步的请求有序的进行
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, this, eventListener, client.connectTimeoutMillis(),
        client.readTimeoutMillis(), client.writeTimeoutMillis());

    return chain.proceed(originalRequest);
  }

  • 这里就是okhttp最核心的地方了,首先创建了一个Interceptor的集合

  • Interceptor就是拦截器的意思,这里主要有retryAndFollowUpInterceptor,BridgeInterceptor,CacheInterceptor,ConnectInterceptor,CallServerInterceptor五个主要的拦截器

  • retryAndFollowUpInterceptor负责断线重连

  • BridgeInterceptor负责请求头的一些参数构建以及gzip压缩处理

  • CacheInterceptor负责okhttp缓存相关的操作

  • ConnectInterceptor负责建立socket链路连接,里面定义了连接池复用连接等

  • CallServerInterceptor负责发送请求获取响应

  • RealInterceptorChain是用来串联这些拦截器让它们各司其职,调用proceed方法完成请求

  • 5.RealInterceptorChain分析
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
      RealConnection connection) throws IOException {
    if (index >= interceptors.size()) throw new AssertionError();

    calls++;

....省略若干代码
    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);
.....省略若干代码
    return response;
  }
  • 可以看到proceed方法中又创建了一个RealInterceptorChain把interceptors集合以及 index + 1传了进去。

  • 然后通过index获取interceptors集合中的拦截器最后调用intercept方法并且传入了新创建的RealInterceptorChain 。

  • 之后各个拦截器里面又调用同样的proceed方法只是index及有些参数不同,这样就完成了每个拦截器的顺序执行,这样的方式其实是运用到了责任链模式。

  • 看下具体流程图


    {`@L3@6S(HB]SY8`F9{1U25.png
  • 好了这就是整体的源码流程,下篇我们具体分析每个拦截器都来干了什么。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,544评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,430评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,764评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,193评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,216评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,182评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,063评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,917评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,329评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,543评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,722评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,425评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,019评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,671评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,825评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,729评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,614评论 2 353