Retrofit+OkHttp框架源码分析

2021-11-15/2022-03-27

Retrofit+OkHttp框架源码分析

Retrofit

Retrofit一般是这样使用的:

//第一部分
Retrofit mRetrofit = new Retrofit.Builder()
                .baseUrl(PlayAndroid_URL)
                //设置请求的client
                .client(OkHttpClient)
                .addConverterFactory(GsonConverterFactory.create())
                .build();

//第二部分

      API api = mRetrofit.create(API.class);

//第三部分

	  Call<ArticleBean> questionArticle = api.getQuestionArticle(mPage);

	  questionArticle.enqueue(new Callback<ArticleBean>() {
            @Override
            public void onResponse(Call<ArticleBean> call, Response<ArticleBean> response) {
                ...
          }

            @Override
            public void onFailure(Call<ArticleBean> call, Throwable t) {
                ...
            }
        });

第一部分:Retrofit的创建过程

这些主要是去进行初始配置的初始化,会在build方法调用时把初始化的参数进行使用

.baseUrl(PlayAndroid_URL)
            //设置请求的client
            .client(OkHttpClient)
            .addConverterFactory(GsonConverterFactory.create())
            
            //这里以.client为例子(其它的差不多)
                public Builder client(OkHttpClient client) {
      return callFactory(Objects.requireNonNull(client, "client == null"));
    }

    public Builder callFactory(okhttp3.Call.Factory factory) {
      this.callFactory = Objects.requireNonNull(factory, "factory == null");
      return this;
    }
Builder(Platform platform) {
      this.platform = platform;
    }

//platform 是平台的意思,就是会根据使用的平台的不同提供不同的资源
    public Builder() {
      this(Platform.get());
    }
    
    public Retrofit build() {
        //baseUrl 一定要初始化 否则会扔出异常
      if (baseUrl == null) {
        throw new IllegalStateException("Base URL required.");
      }

       //如果我们.client(OkHttpClient)初始化了this.callFactory就会用我们自己的okhttp3.Call.Factory,否则使用默认的,下面的同理
      okhttp3.Call.Factory callFactory = this.callFactory;
      if (callFactory == null) {
        callFactory = new OkHttpClient();
      }

      Executor callbackExecutor = this.callbackExecutor;
      if (callbackExecutor == null) {
        callbackExecutor = platform.defaultCallbackExecutor();
      }

        //适配器工厂 比如将数据转换为RX流
      // Make a defensive copy of the adapters and add the default Call adapter.
      List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
      callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor));

        //转换器工厂 比如GsonConverterFactory
      // Make a defensive copy of the converters.
      List<Converter.Factory> converterFactories =
          new ArrayList<>(
              1 + this.converterFactories.size() + platform.defaultConverterFactoriesSize());

      // Add the built-in converter factory first. This prevents overriding its behavior but also
      // ensures correct behavior when using converters that consume all types.
      converterFactories.add(new BuiltInConverters());
      converterFactories.addAll(this.converterFactories);
      converterFactories.addAll(platform.defaultConverterFactories());

      return new Retrofit(
          callFactory,
          baseUrl,
          unmodifiableList(converterFactories),
          unmodifiableList(callAdapterFactories),
          callbackExecutor,
          validateEagerly);
    }

第二部分:Call的创建过程

首先看create做了什么事

API api = mainRetrofit.create(API.class);
public <T> T create(final Class<T> service) {
  validateServiceInterface(service);
  return (T)
      Proxy.newProxyInstance(
          service.getClassLoader(),
          new Class<?>[] {service},
          new InvocationHandler() {
            private final Platform platform = Platform.get();
            private final Object[] emptyArgs = new Object[0];

            @Override
            public @Nullable Object invoke(Object proxy, Method method, @Nullable Object[] args)
                throws Throwable {
              // If the method is a method from Object then defer to normal invocation.
              if (method.getDeclaringClass() == Object.class) {
                return method.invoke(this, args);
              }
              args = args != null ? args : emptyArgs;
              return platform.isDefaultMethod(method)
                  ? platform.invokeDefaultMethod(method, service, proxy, args)
                  : loadServiceMethod(method).invoke(args);
            }
          });
}

这里使用了动态代理,对我们的api接口类进行了动态代理

//像Object.toString()这种默认方法就直接执行,不用进行代理(防止浪费性能)
          if (method.getDeclaringClass() == Object.class) {
            return method.invoke(this, args);
          }

platform.isDefaultMethod(method)是说如果调用的是 利用java8特性接口有默认实现的方法就也是直接调用

boolean isDefaultMethod(Method method) {
  return hasJava8Types && method.isDefault();
}

这些我们一般用不到,关键是看loadServiceMethod(method).invoke(args)这行代码

private final Map<Method, ServiceMethod<?>> serviceMethodCache = new ConcurrentHashMap<>();
ServiceMethod<?> loadServiceMethod(Method method) {
  ServiceMethod<?> result = serviceMethodCache.get(method);
  if (result != null) return result;

  synchronized (serviceMethodCache) {
    result = serviceMethodCache.get(method);
    if (result == null) {
      result = ServiceMethod.parseAnnotations(this, method);
      serviceMethodCache.put(method, result);
    }
  }
  return result;
}

这里就是首先判断这个方法有没有已经创建过了,如果创建过了就直接从缓存池中取出,否则就创建并放入缓存池中,我们待会再来看是怎么创建方法的

先来看看create方法最前面的validateServiceInterface这个方法

private void validateServiceInterface(Class<?> service) {
   //判断service是不是接口,不是就掏出异常
  if (!service.isInterface()) {
    throw new IllegalArgumentException("API declarations must be interfaces.");
  }

    //这里会遍历service以及它的所有父接口是否有泛型
  Deque<Class<?>> check = new ArrayDeque<>(1);
  check.add(service);
  while (!check.isEmpty()) {
    Class<?> candidate = check.removeFirst();
      //不允许使用泛型
    if (candidate.getTypeParameters().length != 0) {
      StringBuilder message =
          new StringBuilder("Type parameters are unsupported on ").append(candidate.getName());
      if (candidate != service) {
        message.append(" which is an interface of ").append(service.getName());
      }
      throw new IllegalArgumentException(message.toString());
    }
    Collections.addAll(check, candidate.getInterfaces());
  }

    //这里的意思是是否激进的初始化,如果是,就对在create方法执行时,将所有的方法加载,否则就会在使用到这个方法的使用进行初始化
  if (validateEagerly) {
    Platform platform = Platform.get();
    for (Method method : service.getDeclaredMethods()) {
      if (!platform.isDefaultMethod(method) && !Modifier.isStatic(method.getModifiers())) {
        loadServiceMethod(method);
      }
    }
  }
}

接下来看看接口中的方法具体是怎么被load的

result = ServiceMethod.parseAnnotations(this, method);
static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
  RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);

  Type returnType = method.getGenericReturnType();
  if (Utils.hasUnresolvableType(returnType)) {
    throw methodError(
        method,
        "Method return type must not include a type variable or wildcard: %s",
        returnType);
  }
  if (returnType == void.class) {
    throw methodError(method, "Service methods cannot return void.");
  }

  return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
}

核心代码是

return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);

这段代码很长,主要是会根据是否使用了协程来进行不同的HttpServiceMethod的返回

由于协程还不是很熟悉,所以在这里挖个坑,暂时分析不使用协程的部分

static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
    Retrofit retrofit, Method method, RequestFactory requestFactory) {
  boolean isKotlinSuspendFunction = requestFactory.isKotlinSuspendFunction;
  boolean continuationWantsResponse = false;
  boolean continuationBodyNullable = false;

    //拿到方法中的所有注解
  Annotation[] annotations = method.getAnnotations();
  Type adapterType;
    //如果是携程的挂起方法
    //暂时先跳过不做分析 直接看else代码块
  if (isKotlinSuspendFunction) {
    Type[] parameterTypes = method.getGenericParameterTypes();
    Type responseType =
        Utils.getParameterLowerBound(
            0, (ParameterizedType) parameterTypes[parameterTypes.length - 1]);
    if (getRawType(responseType) == Response.class && responseType instanceof ParameterizedType) {
      // Unwrap the actual body type from Response<T>.
      responseType = Utils.getParameterUpperBound(0, (ParameterizedType) responseType);
      continuationWantsResponse = true;
    } else {
      // TODO figure out if type is nullable or not
      // Metadata metadata = method.getDeclaringClass().getAnnotation(Metadata.class)
      // Find the entry for method
      // Determine if return type is nullable or not
    }

    adapterType = new Utils.ParameterizedTypeImpl(null, Call.class, responseType);
    annotations = SkipCallbackExecutorImpl.ensurePresent(annotations);
  } else {
      //拿到方法的泛型返回类型
    adapterType = method.getGenericReturnType();
  }

    //根据泛型和返回类型拿到对应的CallAdapter
  CallAdapter<ResponseT, ReturnT> callAdapter =
      createCallAdapter(retrofit, method, adapterType, annotations);
  Type responseType = callAdapter.responseType();
  if (responseType == okhttp3.Response.class) {
    throw methodError(
        method,
        "'"
            + getRawType(responseType).getName()
            + "' is not a valid response body type. Did you mean ResponseBody?");
  }
  if (responseType == Response.class) {
    throw methodError(method, "Response must include generic type (e.g., Response<String>)");
  }
  // TODO support Unit for Kotlin?
  if (requestFactory.httpMethod.equals("HEAD") && !Void.class.equals(responseType)) {
    throw methodError(method, "HEAD method must use Void as response type.");
  }

  Converter<ResponseBody, ResponseT> responseConverter =
      createResponseConverter(retrofit, method, responseType);

  okhttp3.Call.Factory callFactory = retrofit.callFactory;
  if (!isKotlinSuspendFunction) {
      //不是协程的挂起方法的话应该返回CallAdapted
    return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);
  } else if (continuationWantsResponse) {
    //noinspection unchecked Kotlin compiler guarantees ReturnT to be Object.
    return (HttpServiceMethod<ResponseT, ReturnT>)
        new SuspendForResponse<>(
            requestFactory,
            callFactory,
            responseConverter,
            (CallAdapter<ResponseT, Call<ResponseT>>) callAdapter);
  } else {
    //noinspection unchecked Kotlin compiler guarantees ReturnT to be Object.
    return (HttpServiceMethod<ResponseT, ReturnT>)
        new SuspendForBody<>(
            requestFactory,
            callFactory,
            responseConverter,
            (CallAdapter<ResponseT, Call<ResponseT>>) callAdapter,
            continuationBodyNullable);
  }
}
static final class CallAdapted<ResponseT, ReturnT> extends HttpServiceMethod<ResponseT, ReturnT> {
  private final CallAdapter<ResponseT, ReturnT> callAdapter;

  CallAdapted(
      RequestFactory requestFactory,
      okhttp3.Call.Factory callFactory,
      Converter<ResponseBody, ResponseT> responseConverter,
      CallAdapter<ResponseT, ReturnT> callAdapter) {
    super(requestFactory, callFactory, responseConverter);
    this.callAdapter = callAdapter;
  }

  @Override
  protected ReturnT adapt(Call<ResponseT> call, Object[] args) {
    return callAdapter.adapt(call);
  }
}

当我们调用方法的时候,就会走到HttpServiceMethod的invoke方法

CallAdapted#invoke
@Override
final @Nullable ReturnT invoke(Object[] args) {
  Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
  return adapt(call, args);
}

CallAdapted是HttpServiceMethod的具体实现类

CallAdapted实现了adapt方法,我们先看看

Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);

可以看到这里创建了一个call对象,adapt对这个Call对象进行了适配又返回了一个新的call对象

我们先来看看第一个call的enqueue

@Override
public void enqueue(final Callback<T> callback) {
  Objects.requireNonNull(callback, "callback == null");

  okhttp3.Call call;
  Throwable failure;

  synchronized (this) {
    if (executed) throw new IllegalStateException("Already executed.");
    executed = true;

    call = rawCall;
    failure = creationFailure;
    if (call == null && failure == null) {
      try {
        call = rawCall = createRawCall();
      } catch (Throwable t) {
        throwIfFatal(t);
        failure = creationFailure = t;
      }
    }
  }

  if (failure != null) {
    callback.onFailure(this, failure);
    return;
  }

  if (canceled) {
    call.cancel();
  }

//Retrofit实际上是对OkHttp的封装,它底层的网络请求实际上还是使用Okhttp
  call.enqueue(
      new okhttp3.Callback() {
        @Override
        public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
          Response<T> response;
          try {
            response = parseResponse(rawResponse);
          } catch (Throwable e) {
            throwIfFatal(e);
            callFailure(e);
            return;
          }

          try {
            //请求到数据后将数据回调传出
            callback.onResponse(OkHttpCall.this, response);
          } catch (Throwable t) {
            throwIfFatal(t);
            t.printStackTrace(); // TODO this is not great
          }
        }

        @Override
        public void onFailure(okhttp3.Call call, IOException e) {
          callFailure(e);
        }

        private void callFailure(Throwable e) {
          try {
            callback.onFailure(OkHttpCall.this, e);
          } catch (Throwable t) {
            throwIfFatal(t);
            t.printStackTrace(); // TODO this is not great
          }
        }
      });
}

接着看到adapt方法

CallAdapted#adapt
@Override
protected ReturnT adapt(Call<ResponseT> call, Object[] args) {
  return callAdapter.adapt(call);
}

CallAdapter是在HttpServiceMethod#parseAnnotations里创建的

CallAdapter<ResponseT, ReturnT> callAdapter =
        createCallAdapter(retrofit, method, adapterType, annotations);
        
   private static <ResponseT, ReturnT> CallAdapter<ResponseT, ReturnT> createCallAdapter(
      Retrofit retrofit, Method method, Type returnType, Annotation[] annotations) {
    try {
      //noinspection unchecked
      return (CallAdapter<ResponseT, ReturnT>) retrofit.callAdapter(returnType, annotations);
    } catch (RuntimeException e) { // Wide exception range because factories are user code.
      throw methodError(method, e, "Unable to create call adapter for %s", returnType);
    }
  }

  public CallAdapter<?, ?> callAdapter(Type returnType, Annotation[] annotations) {
    return nextCallAdapter(null, returnType, annotations);
  }

  public CallAdapter<?, ?> nextCallAdapter(
      @Nullable CallAdapter.Factory skipPast, Type returnType, Annotation[] annotations) {
    Objects.requireNonNull(returnType, "returnType == null");
    Objects.requireNonNull(annotations, "annotations == null");

//skipPast默认为null,查看ArrayList的indexOf的源码可知会返回-1 则start == 0
    int start = callAdapterFactories.indexOf(skipPast) + 1;
    for (int i = start, count = callAdapterFactories.size(); i < count; i++) {
 //根据返回类型和注解拿到合适的CallAdapter
      CallAdapter<?, ?> adapter = callAdapterFactories.get(i).get(returnType, annotations, this);
      if (adapter != null) {
        return adapter;
      }
    }

    StringBuilder builder =
        new StringBuilder("Could not locate call adapter for ").append(returnType).append(".\n");
    if (skipPast != null) {
      builder.append("  Skipped:");
      for (int i = 0; i < start; i++) {
        builder.append("\n   * ").append(callAdapterFactories.get(i).getClass().getName());
      }
      builder.append('\n');
    }
    builder.append("  Tried:");
    for (int i = start, count = callAdapterFactories.size(); i < count; i++) {
      builder.append("\n   * ").append(callAdapterFactories.get(i).getClass().getName());
    }
    throw new IllegalArgumentException(builder.toString());
  }

DefaultCallAdapterFactory#get
  @Override
  public @Nullable CallAdapter<?, ?> get(
      Type returnType, Annotation[] annotations, Retrofit retrofit) {
    if (getRawType(returnType) != Call.class) {
      return null;
    }
    if (!(returnType instanceof ParameterizedType)) {
      throw new IllegalArgumentException(
          "Call return type must be parameterized as Call<Foo> or Call<? extends Foo>");
    }
    final Type responseType = Utils.getParameterUpperBound(0, (ParameterizedType) returnType);

    final Executor executor =
        Utils.isAnnotationPresent(annotations, SkipCallbackExecutor.class)
            ? null
            : callbackExecutor;

    return new CallAdapter<Object, Call<?>>() {
      @Override
      public Type responseType() {
        return responseType;
      }

      @Override
      public Call<Object> adapt(Call<Object> call) {
        return executor == null ? call : new ExecutorCallbackCall<>(executor, call);
      }
    };
  }

ExecutorCallbackCall的构造方法

final Executor callbackExecutor;
final Call<T> delegate;

ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) {
  this.callbackExecutor = callbackExecutor;
  this.delegate = delegate;
}

如果我们没有添加RxJava的CallAdapter,就会走到以下方法:

ExecutorCallbackCall#enqueue

@Override
public void enqueue(final Callback<T> callback) {
  Objects.requireNonNull(callback, "callback == null");

  delegate.enqueue(
      new Callback<T>() {
        @Override
        public void onResponse(Call<T> call, final Response<T> response) {
          callbackExecutor.execute(
              () -> {
                if (delegate.isCanceled()) {
                  // Emulate OkHttp's behavior of throwing/delivering an IOException on
                  // cancellation.
                  callback.onFailure(ExecutorCallbackCall.this, new IOException("Canceled"));
                } else {
                  callback.onResponse(ExecutorCallbackCall.this, response);
                }
              });
        }

        @Override
        public void onFailure(Call<T> call, final Throwable t) {
          callbackExecutor.execute(() -> callback.onFailure(ExecutorCallbackCall.this, t));
        }
      });
}

来看看callbackExecutor是什么

它也是在retrofit的build中创建的

Executor callbackExecutor = this.callbackExecutor;
if (callbackExecutor == null) {
  callbackExecutor = platform.defaultCallbackExecutor();
}

  @Nullable
  Executor defaultCallbackExecutor() {
    return null;
  }

可以看到如果我们如果不设置callbackExecutor默认为null

这个库的核心实现原理?

Retrofit 主要是在 create 方法中采用动态代理模式(通过访问代理对象的方式来间接访问目标对象)实现接口方法,这个过程构建了一个 ServiceMethod 对象,根据方法注解获取请求方式,参数类型和参数注解拼接请求的链接,当一切都准备好之后会把数据添加到 Retrofit 的 RequestBuilder 中。然后当我们主动发起网络请求的时候会调用 okhttp 发起网络请求,okhttp 的配置包括请求方式,URL等在 Retrofit 的 RequestBuilder 的 build()方法中实现,并发起真正的网络请求。

OkHttp

标准GET请求流程:

Request.Builder requestBuilder = new Request.Builder().url("https://www.wanandroid.com/article/list/0/json");
Request request = requestBuilder.build();
OkHttpClient okHttpClient = new OkHttpClient();
Call call = okHttpClient.newCall(request);
call.enqueue(new Callback() {
    @Override
    public void onFailure(@NonNull Call call, @NonNull IOException e) {
        Log.d(TAG, "onFailure: " + e.getMessage());
    }

    @Override
    public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
        Log.d(TAG, "onResponse: " + response.body().string());
    }
});

查看call.enqueue

interface Call : Cloneable {
...
fun enqueue(responseCallback: Callback)
...
}

发现是接口中的方法

于是看它的实现类,不难找到是RealCall这个类

RealCall#enqueue
override fun enqueue(responseCallback: Callback) {
    //检查是否已经在执行了,如果是,就不再执行,如果不是就标记为执行并继续往下面执行
  check(executed.compareAndSet(false, true)) { "Already Executed" }

    //做标记,对OkHttp的工作流程进行记录
  callStart()
      //真正的核心方法
  client.dispatcher.enqueue(AsyncCall(responseCallback))
}

Dispatcher#Queue

判断正在执行的任务未超过最大限制64,同时同一Host的请求不超过5个,则会将到正在执行队列,同时提交给线程池执行,否则添加到准备队列

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

每次执行完一个请求后,都会调用分发器的 finished 方法 在finished 方法中从执行队列中移除已经执行完的call,并且判断是否需要将准备队列中的call移动到执行队列

//异步请求调用
    void finished(AsyncCall call) {
        finished(runningAsyncCalls, call, true);
    }

    //同步请求调用 
    void finished(RealCall call) {
        finished(runningSyncCalls, call, false);
    }

    private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {
        int runningCallsCount;
        Runnable idleCallback;
        synchronized (this) { 
            //不管异步还是同步,执行完后都要从队列移除(runningSyncCalls/runningAsyncCalls) 
            if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
            if (promoteCalls) promoteCalls();
			//异步任务和同步任务正在执行的和
            runningCallsCount = runningCallsCount();
            idleCallback = this.idleCallback;
        }
			// 没有任务执行执行闲置任务 
        if (runningCallsCount == 0 && idleCallback != null) {
            idleCallback.run();
        }
    }
private void promoteCalls() {
//如果任务满了直接返回 
        if (runningAsyncCalls.size() >= maxRequests) return;
//没有等待执行的任务,返回 
        if (readyAsyncCalls.isEmpty()) return;
//遍历等待执行队列 
        for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
            AsyncCall call = i.next();
//等待任务想要执行,还需要满足:这个等待任务请求的Host不能已经存在5个了 
            if (runningCallsForHost(call) < maxRequestsPerHost) {
                i.remove();
                runningAsyncCalls.add(call);
                executorService().execute(call);
            }
            if (runningAsyncCalls.size() >= maxRequests) return;
// Reached max capacity. 
        }
    }

Dispatcher#ThreadPool

来看看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;
    }

核心线程为0,表示线程池不会一直为我们缓存线程,线程池中所有线程都是在60s内没有工作就会被回收。而最大线程Integer.MAX_VALUE与等待队列 SynchronousQueue 的组合能够得到最大的吞吐量。即当需要线程池执行任务时,如果不存在空闲线程不需要等待,马上新建线程执行任务!

InterceptChain

call 是一个runnable 接下来会在线程池里执行它,直接看到它的run方法

RealCall#run
override fun run() {
  threadName("OkHttp ${redactedUrl()}") {
    var signalledCallback = false
    timeout.enter()
    try {
        //通过拦截器链来得到网络响应
      val response = getResponseWithInterceptorChain()
      signalledCallback = true
        //网络响应成功回调
      responseCallback.onResponse(this@RealCall, response)
    } catch (e: IOException) {
      if (signalledCallback) {
        // Do not signal the callback twice!
        Platform.get().log("Callback failure for ${toLoggableString()}", Platform.INFO, e)
      } else {
          //失败的回调
        responseCallback.onFailure(this@RealCall, e)
      }
    } catch (t: Throwable) {
      cancel()
      if (!signalledCallback) {
        val canceledException = IOException("canceled due to $t")
        canceledException.addSuppressed(t)
          //失败的回调
        responseCallback.onFailure(this@RealCall, canceledException)
      }
      throw t
    } finally {
      client.dispatcher.finished(this)
    }
  }
}

通过拦截器链来得到网络响应

重试及重定向拦截器

第一个拦截器: RetryAndFollowUpInterceptor ,主要就是完成两件事情:重试与重定向

重试

请求阶段发生了 RouteException 或者 IOException会进行判断是否重新发起请求。

重试要求使用者在不禁止重试,并且存在更多的路由线路,则会尝试换条线路进行请求的重试,其中有些异常是不会进行重试的

比如协议异常,超时异常,SSL证书异常 因为在这种情况下重试没有意义

重定向

如果请求结束后没有发生异常并不代表当前获得的响应就是最终需要交给用户的,还需要进一步来判断是否需要重定向的判断。

注意:重定向最多发生20次

桥接拦截器

对用户构建的 请求 进行添加或者删除相关头部信息,以转化成符合网络请求规范的请求并交给下一个拦截器处理,并且回应如果响应体经过了GZIP压缩,会由桥接拦截器解压,再构建成用户可用的 Response 并返回.

缓存拦截器

在发出请求前,判断是否命中缓存。如果命中则可以不请求,直接使用缓存的响应

步骤为:

1、从缓存中获得对应请求的响应缓存

2、创建 CacheStrategy ,创建时会判断是否能够使用缓存,在 CacheStrategy 中存在两个成员: networkRequest与 cacheResponse 。

他们的组合如下:

image-20220228221010645

3、交给下一个责任链继续处理

4、后续工作,返回304则用缓存的响应;否则使用网络响应并缓存此次响应

具体流程

image-20220228221119950

1、如果从缓存获取的 Response 是null,那就需要使用网络请求获取响应;

2、如果是Https请求,但是又丢失了握手信息,那也不能使用缓存,需要进行网络请求;

3、如果判断响应码不能缓存且响应头有 no-store 标识,那就需要进行网络请求;

4、如果请求头有 no-cache 标识或者有 If-Modified-Since/If-None-Match ,那么需要进行网络请求;

5、如果响应头没有 no-cache 标识,且缓存时间没有超过极限时间,那么可以使用缓存,不需要进行网络请求;

6、如果缓存过期了,判断响应头是否设置 Etag/Last-Modified/Date ,没有那就直接使用网络请求否则需要考虑服务器返回304;并且,只要需要进行网络请求,请求头中就不能包含 only-if-cached ,否则框架直接返回504!

资料

res.zip

连接拦截器

打开与目标服务器的连接,并执行下一个拦截器

public final class ConnectInterceptor implements Interceptor {
    public final OkHttpClient client;

    public ConnectInterceptor(OkHttpClient client) {
        this.client = client;
    }

    @Override
    public Response intercept(Chain chain) throws IOException {
        RealInterceptorChain realChain = (RealInterceptorChain) chain;
        Request request = realChain.request();
        StreamAllocation streamAllocation = realChain.streamAllocation();
        // We need the network to satisfy this request. Possibly for validating a conditional GET. 
        boolean doExtensiveHealthChecks = !request.method().equals("GET");
        HttpCodec httpCodec = streamAllocation.newStream(client, chain, doExtensiveHealthChecks);
        RealConnection connection = streamAllocation.connection();
        return realChain.proceed(request, streamAllocation, httpCodec, connection);
    }
}

如果连接到达最大并发流或者连接不允许建立新的流

连接DNS、代理、SSL证书、服务器域名、端口完全相同则可复用

请求服务器拦截器

真正发起网络请求

OKHTTP常见面试题

OKHttp请求整体流程介绍

  • 通过构建者构建出OkHttpClient对象,再通过newCall方法获得RealCall请求对象.
  • 通过RealCall发起同步或异步请求
  • 当发起同步请求时会将请求加入到同步队列中依次执行,所以会阻塞UI线程,需要开启子线程执行.
  • 当发起异步请求时会使用线程池,并且判断请求队列是否大于最大请求队列64,同一主机数是否大于5,如果大于请求添加到异步等待队列中,否则添加到异步执行队列,并执行任务.
  • 任务执行完会调用finished方法将任务从执行队列中删除,并且对等待队列遍历判断是否需要放入执行队列中,判断条件还是和之前一样
  • 不管是同步请求还是起步请求内部最后都会执行到getResponseWithInterceptorChain()方法,这个方法里面依次经过用户自定义拦截器、重试和重定向拦截器、桥接拦截器、缓存拦截器、 连接拦截器和用户自定义网络拦截器以及请求服务器拦截器等拦截处理过程,来获取到一个响应并交给用户。

OKHttp框架中用到了哪些设计模式?

  1. 构建者模式:OkHttpClient 与 Request 的构建都用到了构建者模式
  2. 责任链模式: OKHttp 的核心就是责任链模式,通过5个默认拦截器构成的责任链完成请求的配置
  3. 享元模式: 享元模式的核心即池中复用, OKHttp 复用 TCP 连接时用到了连接池,同时在异步请求中也用到了线程池
  4. 单例模式(Platform 类,已经使用 Okhttp 时使用单例)

response.body().string() 为什么只能调用一次?

通过source拿到字节流以后,直接调用closeQuietly()方法关闭了,这样第二次再去通过source读取就直接流已关闭的异常了。

Okhttp 有哪些优势?

  1. 使用简单,在设计时使用了外观模式,将整个系统的复杂性给隐藏起来,将子系统接口通过一个客户端 OkHttpClient 统一暴露出来。
  2. 扩展性强,可以通过自定义应用拦截器与网络拦截器,完成用户各种自定义的需求
  3. 支持自动重试和重定向
  4. 支持数据缓存,减少重复的网络请求

应用拦截器和网络拦截器有什么区别?

从整个责任链路来看,应用拦截器是最先执行的拦截器,也就是用户自己设置 request 属性后的原始请求,而网络拦截器位于ConnectInterceptor 和 CallServerInterceptor 之间,此时网络链路已经准备好,只等待发送请求数据。它们主要有以下区别

  1. 首先,应用拦截器在 RetryAndFollowUpInterceptor 和 CacheInterceptor 之前,所以一旦发生错误重试或者网络重定向,网络拦截器可能执行多次,因为相当于进行了二次请求,但是应用拦截器永远只会触发一次。另外如果在 CacheInterceptor 中命中了缓存就不需要走网络请求了,因此会存在短路网络拦截器的情况。
  2. 其次,除了 CallServerInterceptor 之外,每个拦截器都应该至少调用一次 realChain.proceed 方法。实际上在应用拦截器这层可以多次调用 proceed 方法(本地异常重试)或者不调用 proceed 方法(中断),但是网络拦截器这层连接已经准备好,可且仅可调用一次 proceed 方法。
  3. 最后,从使用场景看,应用拦截器因为只会调用一次,通常用于统计客户端的网络请求发起情况;而网络拦截器一次调用代表了一定会发起一次网络通信,因此通常可用于统计网络链路上传输的数据。

OKHttp如何复用TCP连接?

# ExchangeFinder
//为承载新的数据流 寻找 连接。寻找顺序是 已分配的连接、连接池、新建连接
private RealConnection findConnection(int connectTimeout, int readTimeout, int writeTimeout,
    int pingIntervalMillis, boolean connectionRetryEnabled) throws IOException {
  synchronized (connectionPool) {
    // 1.尝试使用 已给数据流分配的连接.(例如重定向请求时,可以复用上次请求的连接)
    releasedConnection = transmitter.connection;
    result = transmitter.connection;

    if (result == null) {
      // 2. 没有已分配的可用连接,就尝试从连接池获取。(连接池稍后详细讲解)
      if (connectionPool.transmitterAcquirePooledConnection(address, transmitter, null, false)) {
        result = transmitter.connection;
      }
    }
  }

  synchronized (connectionPool) {
    if (newRouteSelection) {
      //3. 现在有了IP地址,再次尝试从连接池获取。可能会因为连接合并而匹配。(这里传入了routes,上面的传的null)
      routes = routeSelection.getAll();
      if (connectionPool.transmitterAcquirePooledConnection(address, transmitter, routes, false)) {
        foundPooledConnection = true;
        result = transmitter.connection;
      }
    }

  // 4.第二次没成功,就把新建的连接,进行TCP + TLS 握手,与服务端建立连接. 是阻塞操作
  result.connect(connectTimeout, readTimeout, writeTimeout, pingIntervalMillis,
      connectionRetryEnabled, call, eventListener);

  synchronized (connectionPool) {
    // 5. 最后一次尝试从连接池获取,注意最后一个参数为true,即要求 多路复用(http2.0)
    //意思是,如果本次是http2.0,那么为了保证 多路复用性,(因为上面的握手操作不是线程安全)会再次确认连接池中此时是否已有同样连接
    if (connectionPool.transmitterAcquirePooledConnection(address, transmitter, routes, true)) {
      // 如果获取到,就关闭我们创建里的连接,返回获取的连接
      result = transmitter.connection;
    } else {
      //最后一次尝试也没有的话,就把刚刚新建的连接存入连接池
      connectionPool.put(result);
    }
  }
 
  return result;
}
  1. 首先会尝试使用已给请求分配的连接。
  2. 若没有已分配的可用连接,就尝试从连接池中匹配获取。
  3. 若从连接池没有获取到,则进行 TCP + TLS 握手创建新连接并放入ConnectionPool。


标题:Retrofit+OkHttp框架源码分析
作者:OkAndGreat
地址:http://zhongtai521.wang/articles/2021/08/24/1629772534304.html

评论
发表评论
       
       
取消