前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >超详细Okhttp 源码分析

超详细Okhttp 源码分析

作者头像
对话、
发布2022-02-22 14:35:41
1.1K0
发布2022-02-22 14:35:41
举报
文章被收录于专栏:Android-XjAndroid-Xj

文章目录

前言

通过Okhttp使用流程,一步一步查看源码如何实现!

创建OkhttpClient

  • OkHttpClient client = new OkHttpClient.Builder() 封装Okhttp初始化所需要的参数
代码语言:javascript
复制
  public Builder() {
      //分发器 决定异步请求是直接处理还是缓存等待,同步请求直接放入队列中
      dispatcher = new Dispatcher();
      protocols = DEFAULT_PROTOCOLS;
      connectionSpecs = DEFAULT_CONNECTION_SPECS;
      eventListenerFactory = EventListener.factory(EventListener.NONE);
      proxySelector = ProxySelector.getDefault();
      cookieJar = CookieJar.NO_COOKIES;
      socketFactory = SocketFactory.getDefault();
      hostnameVerifier = OkHostnameVerifier.INSTANCE;
      certificatePinner = CertificatePinner.DEFAULT;
      proxyAuthenticator = Authenticator.NONE;
      authenticator = Authenticator.NONE;
      //连接池  客户端 --服务端 中间的连接 称之为 connection 而 所有的连接都是放在连接池中的即:		//	ConnectionPool
      // 当请求的 URL相同时 可以选择复用 
      connectionPool = new ConnectionPool();
      dns = Dns.SYSTEM;
      followSslRedirects = true;
      followRedirects = true;
      retryOnConnectionFailure = true;
      connectTimeout = 10_000;
      readTimeout = 10_000;
      writeTimeout = 10_000;
      pingInterval = 0;
    }

创建Request

  • Request request = new Request.Builder().url(“http://www.baidu.com”) .get().build();
代码语言:javascript
复制
   public Builder() {
       //默认请求方式
      this.method = "GET";
       //头部信息
      this.headers = new Headers.Builder();
    }

build()

代码语言:javascript
复制
   public Request build() {
      if (url == null) throw new IllegalStateException("url == null");
       //把Builder封装好的 对象传递给 Request
      return new Request(this);
    }

Request

代码语言:javascript
复制
  Request(Builder builder) {
      //初始化 基本信息
    this.url = builder.url;
    this.method = builder.method;
    this.headers = builder.headers.build();
    this.body = builder.body;
    this.tag = builder.tag != null ? builder.tag : this;
  }

创建Request

  • Call call = client.newCall(request);
代码语言:javascript
复制
  private RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
      //第一步创建好的OkHttpClient 
    this.client = client;
      //第二步创建好的 Request 
    this.originalRequest = originalRequest;
    this.forWebSocket = forWebSocket;
      //重定向拦截器 
    this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);
  }

以上步骤 是同步和异步请求相同的部分

同步请求(单独部分)

  • Response response = call.execute(); 开启异步请求
代码语言:javascript
复制
  @Override public Response execute() throws IOException {
      //同步代码块
    synchronized (this) {
        //同一个http请求只能执行一次
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
      //捕捉异常
    captureCallStackTrace();
      //开启请求时开启listener
    eventListener.callStart(this);
    try {
        //通过 OkHttpClient 获得 分发器 ,并调用分发器中的 executed 方法 ①
      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);
    }
  }

executed ①

代码语言:javascript
复制
 /** Used by {@code Call#execute} to signal it is in-flight. */
  synchronized void executed(RealCall call) {
      //加入队列 runningSyncCalls ② 
    runningSyncCalls.add(call);
  }

runningSyncCalls②

代码语言:javascript
复制
  /** Ready async calls in the order they'll be run. */
//异步就绪队列
  private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
//异步执行队列
  /** Running asynchronous calls. Includes canceled calls that haven't finished yet. */
  private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
//同步队列
  /** Running synchronous calls. Includes canceled calls that haven't finished yet. */
  private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();

异步请求(单独部分)

  • call.enqueue() 开启异步请求
代码语言:javascript
复制
  @Override public void enqueue(Callback responseCallback) {
    synchronized (this) {
          //同一个http请求只能执行一次
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    captureCallStackTrace();
    eventListener.callStart(this);
     //通过 OkHttpClient 获得 分发器 ,并调用分发器中的 enqueue 方法 ① 传入 AsyncCall ②
    client.dispatcher().enqueue(new AsyncCall(responseCallback));
  }

enqueue①

代码语言:javascript
复制
synchronized void enqueue(AsyncCall call) {
    //首先判断当前 正在请求的数是否小于 最大请求数   private int maxRequests = 64;
    // 并且 当前网络请求的 Host是否小于 maxRequestsPerHost private int maxRequestsPerHost = 5;
    if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
        //条件满足 加入到正在执行的异步请求队列当中
      runningAsyncCalls.add(call);
        //通过线程 executorService ①① 池执行 call请求    
      executorService().execute(call);
    } else {
        //如果条件不满足 就把请求加入到 异步就绪队列 等待条件满足执行 call请求
      readyAsyncCalls.add(call);
    }
  }

executorService ①①

代码语言:javascript
复制
//通过  synchronized 保证对象 是单例 
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;
  }

AsyncCall ②

代码语言:javascript
复制
 final class AsyncCall extends NamedRunnable 
 public abstract class NamedRunnable implements Runnable
 //就是 一个 Runnable run()方法②①

run()方法②①

代码语言:javascript
复制
@Override public final void run() {
    String oldName = Thread.currentThread().getName();
    Thread.currentThread().setName(name);
    try {
        //真正执行操作的方法  execute ②①①
      execute();
    } finally {
      Thread.currentThread().setName(oldName);
    }
  }

execute ②①①

代码语言:javascript
复制
    @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 {
          //回收请求 finished②①①①
        client.dispatcher().finished(this);
      }
    }

finished②①①①

代码语言:javascript
复制
private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {
    int runningCallsCount;
    Runnable idleCallback;
    synchronized (this) {
      if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
        // promoteCalls() 调整任务队列
      if (promoteCalls) promoteCalls();
        //异步进入此方法 
        //重新赋值 正在执行的请求数  runningCallsCount() == runningAsyncCalls.size() + 				//runningSyncCalls.size();
      runningCallsCount = runningCallsCount();
      idleCallback = this.idleCallback;
    }
	//空闲时执行空闲回调 
    if (runningCallsCount == 0 && idleCallback != null) {
      idleCallback.run();
    }
  }

Dispatcher(Okhttp核心)分发器

  • 作用:

为维护请求的状态,并维护一个线程池,用于执行请求

代码语言:javascript
复制
 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.
    }
  }

拦截器(Okhttp核心)

代码语言:javascript
复制
  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));
	//创建 Chain 即链 
    Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
        originalRequest, this, eventListener, client.connectTimeoutMillis(),
        client.readTimeoutMillis(), client.writeTimeoutMillis());
		// 调用 proceed()发起网络请求
    return chain.proceed(originalRequest); 
  }

proceed

代码语言:javascript
复制
 public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
      RealConnection 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.httpCodec != null && !this.connection.supportsUrl(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.httpCodec != 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, httpCodec,
        connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
        writeTimeout);
    Interceptor interceptor = interceptors.get(index);
     //放入 intercept  实现 这个方法是5个拦截器
     //RetryAndFollowUpInterceptor
     //BridgeInterceptor
     //CacheInterceptor
     //ConnectInterceptor
     //CallServerInterceptor 
    Response response = interceptor.intercept(next);

    // Confirm that the next interceptor made its required call to chain.proceed().
    if (httpCodec != null && index + 1 < interceptors.size() && next.calls != 1) {
      throw new IllegalStateException("network interceptor " + interceptor
          + " must call proceed() exactly once");
    }

    // Confirm that the intercepted response isn't null.
    if (response == null) {
      throw new NullPointerException("interceptor " + interceptor + " returned null");
    }

    if (response.body() == null) {
      throw new IllegalStateException(
          "interceptor " + interceptor + " returned a response with no body");
    }

    return response;
  }

5个拦截器的简单总结 :

1. RetryAndFollowUpInterceptor重定向拦截器

RetryAndFollowUpInterceptor负责失败重试以及重定向。

RetryAndFollowUpInterceptor的作用就是处理了一些连接异常以及重定向。

2. BridgeInterceptor桥拦截器

BridgeInterceptor就跟它的名字那样,它是一个连接桥,它负责把用户构造的请求转换为发送给服务器的请求,把服务器返回的响应转换为对用户友好的响应。

转换的过程就是添加一些服务端需要的header信息。

在Request阶段配置用户信息,并添加一些请求头。在Response阶段,进行gzip解压。

3. CacheInterceptor缓存拦截器

1.根据策略,不使用网络,又没有缓存的直接报错,并返回错误码504。

2.根据策略,不使用网络,有缓存的直接返回。

3.前面两个都没有返回,继续执行下一个Interceptor,即ConnectInterceptor。

4.接收到网络结果,如果响应code式304,则使用缓存,返回缓存结果。

5.读取网络结果。

6.对数据进行缓存。

7.返回网络读取的结果。

4. ConnectInterceptor连接拦截器

在RetryAndFollowUpInterceptor里初始化了一个StreamAllocation对象,我们说在这个StreamAllocation对象里初始化了一个Socket对象用来做连接,但是并没有 真正的连接,等到处理完hader和缓存信息之后,才调用ConnectInterceptor来进行真正的连接

5. CallServerInterceptor请求服务器拦截器

这是链中的最后一个拦截器。 它会对服务器进行网络请求 我们通过ConnectInterceptor已经连接到服务器了,接下来我们就是写入请求数据以及读出返回数据了。整个流程:

写入请求头 写入请求体 读取响应头 读取响应体

总结

Okhttp源码实现逻辑还是非常容易理解的,如果不理解,多看几遍就Ok了!设计的非常精妙,从中可以学习到许多编程思想。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 文章目录
  • 前言
  • 创建OkhttpClient
  • 创建Request
    • build()
      • Request
      • 创建Request
      • 同步请求(单独部分)
        • executed ①
          • runningSyncCalls②
          • 异步请求(单独部分)
            • enqueue①
              • executorService ①①
                • AsyncCall ②
                  • run()方法②①
                    • execute ②①①
                      • finished②①①①
                      • Dispatcher(Okhttp核心)分发器
                      • 拦截器(Okhttp核心)
                        • proceed
                          • 5个拦截器的简单总结 :
                      • 总结
                      领券
                      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档