OKHttp3网络框架流程简介

okhttp是一个开源的网络请求框架,由square公司发布,目前比较流行的版本是okHttp3。
okhttp3框架源码地址: https://github.com/square/okhttp
本文只对okHttp3做大致讲解,不具体分析源码细节等。

就从如何使用开始讲起。

okHttp3简单的使用步骤如下:

1.得到OkHttpClient对象
OkHttpClient okHttpClient = new OkHttpClient.Builder()
        //建造者模式,可选择添加各种拦截器等
        .xxx()
        .xxx()
        ....
        .build();
2.得到Request对象
Request request = new Request.Builder()
        .url(请求地址)
        .build();
3.发起请求
//同步请求
okHttpClient.newCall(request).execute();
//异步请求
okHttpClient.newCall(request).enqueue(new Callback() {
    @Override
    public void onResponse(Call call, Response response) throws IOException {
        //正确响应
    }
    @Override
    public void onFailure(Call call, IOException e) {
        //请求失败
    }
});

看它的处理逻辑:
第一步第二步,使用建造者模式得到OkHttpClient对象和Request对象,这里不细讲了。
第三步开始发起请求,下面是OkHttpClient的newCall()方法:

/**
 * Prepares the {@code request} to be executed at some point in the future.
 */
@Override public Call newCall(Request request) {
  得到了个RealCall对象
  return new RealCall(this, request);
}

//RealCall的构造方法

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

得到了RealCall对象后,具体发起网络请求时,分为同步和异步请求。

一、异步请求
发起异步请求时,调用的是RealCall的enqueue()方法。
//RealCall的enqueue()方法

@Override public void enqueue(Callback responseCallback) {
  synchronized (this) {
    if (executed) throw new IllegalStateException("Already Executed");
    executed = true;
  }
  client.dispatcher().enqueue(new AsyncCall(responseCallback));
}

这里client.dispatcher()得到的是建造者模式生成OkHttpClient对象的时候,new的一个Dispatcher对象。

Dispatcher是负责请求管理与调度的,维护了一个正在运行的请求队列,待完成的请求队列等。

public final class Dispatcher {
  private int maxRequests = 64;//最大并发请求数
  private int maxRequestsPerHost = 5;//每个主机最大请求数为5
  private Runnable idleCallback;

  /** 线程池 */
  private ExecutorService executorService;

  /** 待运行的异步请求队列 */
  private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();

  /** 正在运行的异步请求队列,包括已取消的 */
  private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();

  /** 正在运行的同步请求队列 */
  private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();

  public Dispatcher(ExecutorService executorService) {
    this.executorService = executorService;
  }

  public Dispatcher() {
  }
//创建单例的线程池
  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;
  }

看下Dispatcher的enqueue()方法:

synchronized void enqueue(AsyncCall call) {
//如果正在请求的数量未达到最大数量,就加入运行队列并运行
  if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
    runningAsyncCalls.add(call);
    executorService().execute(call);
  } else {
//如果正在请求的数量已达到最大数量,就加入等待队列,等待取出
    readyAsyncCalls.add(call);
  }
}

异步请求是在加入队列的时候,被包装成AsyncCall对象的。由Dispatcher的enqueue()可见,AsyncCall是交给线程池来执行了。

AsyncCall是Runnable的子类,发起请求最终会执行它的execute()方法。
追代码的话,流程为ThreadPoolExecutor的execute() -> addWorker() -> addWorker() 里的 t.start() ->AsyncCall.run()->AsyncCall.execute()。

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

异步请求的流程分析到这里先放一边,来看看同步请求的处理。

二·、同步请求
发起同步请求时,调用的是RealCall的execute()方法:

@Override public Response execute() throws IOException {
  synchronized (this) {
    if (executed) throw new IllegalStateException("Already Executed");
    executed = true;
  }
  try {
    client.dispatcher().executed(this);
//与异步请求一样,也是用这个方法来真正处理网络请求的流程
    Response result = getResponseWithInterceptorChain();
    if (result == null) throw new IOException("Canceled");
    return result;
  } finally {
    client.dispatcher().finished(this);
  }
}

三、处理网络请求的流程
同步与异步都是用getResponseWithInterceptorChain()这个方法来真正处理网络请求的流程。
下面就看看getResponseWithInterceptorChain()这个方法做了什么:

private 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 (!retryAndFollowUpInterceptor.isForWebSocket()) {
    interceptors.addAll(client.networkInterceptors());
  }
  interceptors.add(new CallServerInterceptor(
      retryAndFollowUpInterceptor.isForWebSocket()));

  Interceptor.Chain chain = new RealInterceptorChain(
      interceptors, null, null, null, 0, originalRequest);
  return chain.proceed(originalRequest);
}

其中Interceptor意为拦截器,是OKHTTP中提供的一个接口:

public interface Interceptor {
  Response intercept(Chain chain) throws IOException;

  interface Chain {
    Request request();

    Response proceed(Request request) throws IOException;

    Connection connection();
  }
}

上面getResponseWithInterceptorChain()这个方法做了这样的处理:
1.把okHttpClient的各种拦截器添加到拦截器列表interceptors中。
2.拦截器列表作为构造参数,生成RealInterceptorChain对象。
3.通过RealInterceptorChain 的proceed(originalRequest)得到请求结果。

第一步添加到拦截器列表interceptors中的各种拦截器,包括下面这些:

1)在配置 OkHttpClient 时设置的 interceptors;
2)负责失败重试以及重定向的 RetryAndFollowUpInterceptor;
3)负责把用户构造的请求转换为发送到服务器的请求、把服务器返回的响应转换为用户友好的响应的 BridgeInterceptor;
4)负责读取缓存直接返回、更新缓存的 CacheInterceptor;
5)负责和服务器建立连接的ConnectInterceptor;
6)配置 OkHttpClient 时设置的 networkInterceptors;
7)负责向服务器发送请求数据、从服务器读取响应数据的 CallServerInterceptor。

OkHttp的这种拦截器链采用的是责任链模式,这样的好处是将请求的发送和处理分开,并且可以动态添加中间的处理方实现对请求的处理、短路等操作。
原文链接:https://blog.csdn.net/import_sadaharu/article/details/81416244

下面是RealInterceptorChain 的proceed方法:

@Override public Response proceed(Request request) throws IOException {
  return proceed(request, streamAllocation, httpStream, connection);
}

public Response proceed(Request request, StreamAllocation streamAllocation, HttpStream httpStream,
    Connection 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.httpStream != null && !sameConnection(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.httpStream != 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, httpStream, connection, index + 1, request);
  Interceptor interceptor = interceptors.get(index);
  Response response = interceptor.intercept(next);
  
  xxx其它处理,略过	
  return response;
}

其中proceed()方法里的这几句,要单独拿出来讲下:

  RealInterceptorChain next = new RealInterceptorChain(
      interceptors, streamAllocation, httpStream, connection, index + 1, request);
  Interceptor interceptor = interceptors.get(index);
  Response response = interceptor.intercept(next);

这里的拦截器链,实现了一个递归的效果。
默认情况下,这里的interceptors.get(index),会得到RetryAndFollowUpInterceptor。
在RetryAndFollowUpInterceptor的intercept方法里,进行了网络通信相关的判断和拦截,如果不满足拦截条件的话,就会调用chain.proceed()方法。

这样,流程又走到了这里。而且由于上次传入的是index+1,所以这次interceptors.get(index)得到的是interceptors列表的下一个拦截器了,也就是BridgeInterceptor。

同理,BridgeInterceptor的intercept方法里,进行一些特有的判断和拦截,如果不满足拦截条件的话,就会调用chain.proceed()方法。

值得一提的是CacheInterceptor,缓存拦截器。

缓存拦截器中的缓存策略:
(1)首先会尝试从缓存Cache中获取当前请求request的缓存response,并根据传入的请求request和获取的缓存response通过缓存策略对象CacheStrategy的工厂类的get方法生成缓存策略类CacheStrategy。
(2)CacheStrategy的工厂类的get方法里面会根据一些规则生成CacheStrategy,这里面的规则实际上控制着缓存拦截器CacheInterceptor的处理逻辑。而这些规则都是根据请求的Request和缓存的Response的header头部信息来生成的,比如是否有noCache标志位,是否是immutable不可变的,以及缓存是否过期等。
(3)CacheInterceptor会根据CacheStrategy中的networkRequest和cacheResponse是否为空,来判断是请求网络还是直接使用缓存。
(4)网络得到的networkResponse会和本地已经存在的cacheResponse,做比较,决定是否来更新缓存的cacheResponse。

作者:xxq2dream 链接:https://www.jianshu.com/p/d8590c4232a3

即如果满足一定要求的话,就会从缓存中取响应并返回,而不会发起网络请求了;否则传递给下一个拦截器。

依次类推,最终的网络请求,是被CallServerInterceptor处理的。CallServerInterceptor是拦截器链中最后一个拦截器,负责将网络请求提交给服务器。
CallServerInterceptor的intercept方法里发起请求并得到响应后,这个响应Response会按照原先递归调用的相反顺序层层传递回去,最后也就成为RealCall里的getResponseWithInterceptorChain()的结果,这个结果会传入Callback里,由用户自己处理。

总结
(1)拦截器链和缓存策略都是OkHttp3的亮点所在,此外还有复用连接池等。
(2)拦截器链通过责任链的模式,将网络请求过程中的职责功能都分割开,分别用不同的拦截器来完成失败重连、缓存处理、网络连接等问题。而且用户还可以添加自定义的拦截器,非常灵活,满足面向对象的开闭原则。
(3)缓存策略指的是对于请求的响应的缓存。OkHttp中有专门类Cache来实现缓存,Cache中采用了DiskLruCache,以Request的URL的md5为key,相应Response为value。此外Cache中还通过外观模式对外提供了InternalCache接口变量,用于调用Cache中的方法,也满足面向对象的接口隔离原则和依赖倒置原则等。
(4)缓存拦截器中会根据生成的缓存策略类CacheStrategy的2个变量networkRequest和cacheResponse来决定是连接网络还是直接使用缓存。如果2个变量都为空,则直接返回504,请求失败;如果缓存不为空,则直接返回缓存;如果networkRequest不为空,就通过调用RealInterceptorChain的proceed方法将请求继续转发到下一个拦截器。

作者:xxq2dream 链接:https://www.jianshu.com/p/d8590c4232a3

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值