Okhttp3源码完全解析

上文已经说了,Okhttp和Retrofit是相辅相成的,Retrofit是对Okhttp的上层封装,Okhttp是Http的底层实现。那么,今天就来解析一下Okhttp的源码。

1 如何使用Okhttp

OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
        .url("https://api.github.com/")
        .build();
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的使用如下,我们能够看到,先是new了一个OkhttpClient,然后这个OkhttpClient又new了一个Call,这个Call执行了enqueue方法,这个方法得到请求的结果,是成功或失败。

2  enqueue方法分析

我们看一下enqueue方法,发现Call是一个接口,最终在RealCall找到具体的实现,代码如下。

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

我们可以看到,最终是dispatcher执行这个enqueue,那么我们就看一下这个diapatcher这个对象,再看一下它的enqueue方法。最终可以发现,是由executorService来执行call。而这个executorService传入的对象是Runnable,由此可以断定,Dispatcher这个类是用来做线程控制的。而且,逻辑代码应该是由某个Runnable接口的类来实现的。

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

这个enqueue传入的对象是AsynCall,看一下AsyncCall里面的Runnable方法,发现具体的实现是在execute里面,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 {
      eventListener.callFailed(RealCall.this, e);
      responseCallback.onFailure(RealCall.this, e);
    }
  } finally {
    client.dispatcher().finished(this);
  }
}

从里面可以看到,是网络相关逻辑的代码,并且对网络结果做处理。最重要的代码逻辑是在getResponseWithInterceptorChain()中。其实,这里面就是就是Okhttp的技术核心,也就是各种Interceptor的实现。Interceptor翻译成中文就是拦截器,同时,我们也可以自己加入拦截器。还能自己决定拦截器是放在开头还是结尾。

3 Okhttp的链式调用和拦截器

首先,看一下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, this, eventListener, client.connectTimeoutMillis(),
      client.readTimeoutMillis(), client.writeTimeoutMillis());

  return chain.proceed(originalRequest);
}

可以看到,先是创建了一个List,然后往里面丢了几个Interceptor。然后创建了一个Chain对象,调用了它的proceed方法。要想理解Okhttp的源码,最重要的就是要理解它的链式调用。

所谓链式调用,就是各个Interceptors会依次调用自己的intercept()方法,这里面会有三个步骤,先说第二步,第二步是调用chain.proceed()方法。把请求交给下一个Interceptor,调用它们的intercept()方法。那么第一步,就是调用下一个Intercepter的前置工作,第三步就是对返回的Response做后置工作。

简单的说,这个请求和响应就是一条链,首先A先对Request做自己的前置工作,然后交给B,B也对A传过来的Request做前置工作,然后交给C,C把Request发送到后台,后台返回Response,然后这个Response给B,B对这个Response做后置工作,然后返回A,A再对返回的Response做后置工作。如此,一次网络请求结束。

4 各个Interceptor的作用

既然已经清楚了Okhttp的链式调用,再来看一下各个拦截器的作用。

client.interceptors()和client.networkInterceptors():  首先,这两个拦截器是没有具体逻辑的,需要我们自己去实现,我们可以通过addInterceptor(Interceptor)和ddNetworkInterceptor(Interceptor)添加我们自定义的拦截器,里面可以实现我们需要的功功能。 另外,它们是集合,就表示可以添加不止一个拦截器。无论是client.interceptors()还是client.networkInterceptors()都可以添加多个。它们具体的区别我会在后文中描述。

RetryAndFollowUpInterceptor: 负责重试和重定向的拦截器。这个是有一定条件的,比如你符合某个异常,像RouteException、IOException等,同时你又符合其它条件,比如是否允许重试等等。符合所有条件,才允许重试,像服务器返回404这种,是不会进行重试的。其实内部就是一个死循环,如果请求成功或不符合重试条件就跳出循环,释放资源。否则就继续循环。

BridgeInterceptor: 桥接的拦截器,主要是负责和Http相关的处理,比如添加 Content-Type、Content-Length 等请求头。同时,它还可以对gzip格式的包进行处理。

CacheInterceptor: 缓存的拦截器。获取缓存的网络数据是在交给下一个拦截器前,如果不需要网络获取并且缓存的Response不为空,就获取缓存数据。如果获取了缓存数据,后面的拦截器就不会再执行。

ConnectInterceptor: 连接的拦截器,里面只有前置工作,没有后置工作,因为连接都是建立在发送请求之前的。如果是Http会建立TCP连接,如果是Https在TCP之上还会建立TLS连接。

CallServerInterceptor:负责实际网络请求的I/O操作,里面主要是通过Okio的一些Api来实现。包括从Socket中读取数据和往Sokcket中写入数据

5 addInterceptor addNetworkInterceptor的区别

这个是我们工作中经常会用到的,也是面试中问的比较多的问题,到底是选用addInterceptor还是选用addNetworkInterceptor呢。

Interceptor是在所有的拦截器之前,可以进行最早的前置工作,也可以做最后的善后工作,一般可以在这里面加入统一的Header。而NetworkInterceptor是在CallServerInterceptor之前,可以说是排在所有拦截器之后,除了真正进行网络操作的CallServerInterceptor。这里面看到的数据是原始数据,比如Body还没有被gzip解压等。

所以大多数情况下,我们会选用addInterceptor而不是addNetworkInterceptor。只有我们需要看原始数据的时候,我们才会需要使用addNetworkInterceptor。

至此,Okhttp的源码大致架构已经解析完毕,当然,里面还有更多具体的细节,就不一一描述了。





 

 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值