2、Retrofit框架解析

Retrofit请求时序图:

1、Retrofit框架是什么

Retrofit是一个框架,它封装了网络请求功能,将网络请求的一整套流程都封装好了

  • 用来解决什么问题?
    • 使用OkHttp进行网络请求时,需要封装Request请求,发起请求,处理响应结果,如json格式数据转换,和线程切换
    • 而使用Retrofit框架将上面的操作都封装好了,可直接拿来使用。

2、Retrofit的使用

2.1、添加依赖
java 复制代码
com.squareup.retrofit2:retrofit:2.9.0
2.2、创建一个请求接口
  • 该接口封装了所有需要发起的请求

  • 解析:

    • GitHubService是一个接口,发起请求时,会拿到该接口的实现类调用其方法(listRepos)
    • 请求方法listRepos,通过注解设置了请求的参数(如method,url和入参),之前使用OkHttp时,需要创建一个Request对象实例,这里采用注解方式进行数据设置
less 复制代码
public interface GitHubService {

  @GET("users/{user}/repos")
  Call<List<Repo>> listRepos(@Path("user") String user);
}
2.3、创建Retrofit单例
  • 通过baseUrl方法,设置请求的前缀,为何这样做呢?
  • 因为每个应用请求时都是访问自己本公司的域名接口,且这个域名是唯一的,所以多个请求的url前缀都一样,为节省url前缀写法,统一在baseUrl方法中设置,至于每个接口的不同部分,则在每个请求接口中设置
  • client方法可设置自定义的OkHttpClient对象实例,不调用的话内部有默认的对象创建 retrofit.create(GitHubService.class); 调用retrofit的create方法会创建请求接口GitHubService的实现类
ini 复制代码
Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com/")
    .client(initOkHttpClient())
    .build();
 
GitHubService service = retrofit.create(GitHubService.class);
2.4、发起请求,获取响应数据
  • 调用设置的接口请求方法,获取响应数据
kotlin 复制代码
Call<List<Repo>> reposCall = service.listRepos("octocat");

reposCall.enqueue(object : Callback<List<Repo>> {
      override fun onResponse(call: Call<List<Repo>>, response: Response<List<Repo>>) {

       }

       override fun onFailure(call: Call<List<Repo>>, t: Throwable) {
       }
})

3、流程解析

3.1、Retrofit对象创建
  • 通过Builder构建者模式设置参数,最后调用build方法创建Retrofit对象实例
java 复制代码
retrofit2.Retrofit.Builder

  public static final class Builder {
    private final Platform platform;
    private @Nullable okhttp3.Call.Factory callFactory;
    private @Nullable HttpUrl baseUrl;
    private final List<Converter.Factory> converterFactories = new ArrayList<>();
    private final List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>();
    private @Nullable Executor callbackExecutor;
    private boolean validateEagerly;

    public Retrofit build() {
      if (baseUrl == null) {
        throw new IllegalStateException("Base URL required.");
      }
		
		// 创建默认的OkHttpClient对象
      okhttp3.Call.Factory callFactory = this.callFactory;
      if (callFactory == null) {
        callFactory = new OkHttpClient();
      }

		// 数据获取到后,线程切换回调处理器callbackExecutor(实现类是retrofit2.Platform.Android.MainThreadExecutor,内部也是通过Handler进行线程切换)
      Executor callbackExecutor = this.callbackExecutor;
      if (callbackExecutor == null) {
        callbackExecutor = platform.defaultCallbackExecutor();
      }

      // 添加请求适配器到集合中 callAdapterFactories,默认值为(platform.defaultCallAdapterFactories(callbackExecutor))
      List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
      callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor));

      // 添加转换器到集合中 converterFactories
      List<Converter.Factory> converterFactories =
          new ArrayList<>(
              1 + this.converterFactories.size() + platform.defaultConverterFactoriesSize());

      converterFactories.add(new BuiltInConverters());
      converterFactories.addAll(this.converterFactories);
      converterFactories.addAll(platform.defaultConverterFactories());
		// 创建Retrofit对象实例
      return new Retrofit(
          callFactory,
          baseUrl,
          unmodifiableList(converterFactories),
          unmodifiableList(callAdapterFactories),
          callbackExecutor,
          validateEagerly);
    }
  }
}
3.2、GitHubService接口实现类
  • retrofit2.Retrofit#create
切面编程,动态代理
  • 在Retrofit的create方法中,通过Proxy.newProxyInstance动态代理的方式创建GitHubService接口实现类
  • 后面在使用该接口实现类调用方法时,会进入InvocationHandler的invoke方法,请求方法的名称和参数信息会在invoke方法的参数中传入
typescript 复制代码
  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);
              }
            });
  }
3.3、发起请求
ini 复制代码
Call<List<Repo>> repos = service.listRepos("octocat");
  • 当调用GitHubService的listRepos方法时,最终会调用到创建动态代理对象InvocationHandler的invoke方法
  • 接着调用loadServiceMethod(method).invoke(args);并返回数据
  • loadServiceMethod:获取ServiceMethod对象
  • retrofit2.Retrofit#loadServiceMethod
    • 该方法中,会先去缓存中根据method为key值进行查找
    • 缓存中没有调用ServiceMethod.parseAnnotations(this, method);创建一个ServiceMethod实例
    • 最后将该实例对象保存到缓存serviceMethodCache中,这样下次再调用该请求方法时就不用另新创建ServiceMethod实例对象了
sql 复制代码
  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;
  }
retrofit2.ServiceMethod#parseAnnotations
  • 通过RequestFactory.parseAnnotations()方法创建RequestFactory 对象
  • 最终调用HttpServiceMethod.parseAnnotations()返回HttpServiceMethod继承类
java 复制代码
abstract class ServiceMethod<T> {
  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);
  }

  abstract @Nullable T invoke(Object[] args);
}
3.4、HttpServiceMethod
核心类
  • retrofit2.HttpServiceMethod#parseAnnotations
ini 复制代码
  static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
      Retrofit retrofit, Method method, RequestFactory requestFactory) {
    boolean isKotlinSuspendFunction = requestFactory.isKotlinSuspendFunction;
    boolean continuationWantsResponse = false;
    boolean continuationBodyNullable = false;

	// 获取请求方法上设置的所有注解,通过這些注解可以知道请求的method和请求url
    Annotation[] annotations = method.getAnnotations();
    Type adapterType;
    if (isKotlinSuspendFunction) {	// 判断该请求方法是否是Kotlin类型的挂起函数(先不考虑)
      ...
      annotations = SkipCallbackExecutorImpl.ensurePresent(annotations);
    } else {
		// 获取请求方法返回的数据类型,当前返回类型是Call,如果和RxJava配合使用就会返回Observer等其他类型,需要通过converterFactorie类型转换处理
      adapterType = method.getGenericReturnType();	
    }

	// 获取合适的请求适配器 CallAdapter对象
    CallAdapter<ResponseT, ReturnT> callAdapter =
        createCallAdapter(retrofit, method, adapterType, annotations);
	// 获取请求方法的返回值类型
    Type responseType = callAdapter.responseType();

    ...
		// 根据返回类型获取响应数据转换器
    Converter<ResponseBody, ResponseT> responseConverter =
        createResponseConverter(retrofit, method, responseType);

    okhttp3.Call.Factory callFactory = retrofit.callFactory;	// callFactory 为OkHttpClient对象实例
	
	// 非kotlin挂起函数,将参数requestFactory, callFactory, responseConverter, callAdapter封装为CallAdapted对象
    if (!isKotlinSuspendFunction) {
      return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);
    } 
	...		// 挂起函数处理逻辑
  }

3.5、CallAdapted:

  • parseAnnotations方法最终返回了HttpServiceMethod内部子类CallAdapted的实例
  • 所以loadServiceMethod方法,最开始创建了CallAdapted对象实例并保存到缓存中,接着调用invoke方法,调用的是retrofit2.HttpServiceMethod#invoke
  • invoke方法中封装了OkHttpCall请求类,该类的作用域OkHttp框架中的RealCall类型,其中也实现了同步请求
  • 异步请求的方法
    • 继续调用adapter方法,会调用子类CallAdapted的adapt方法
less 复制代码
  @Override
  final @Nullable ReturnT invoke(Object[] args) {
    Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
    return adapt(call, args);
  }
retrofit2.HttpServiceMethod.CallAdapted#CallAdapted
  • adapt方法中,真实调用的类是,Retrofit中设置的请求适配器callAdapter对象
scala 复制代码
  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);
    }
  }

3.6、callAdapter的真实实现类为:DefaultCallAdapterFactory

  • 获取callAdapter的方法是:retrofit2.HttpServiceMethod#createCallAdapter
  • 最终会调用retrofit2.Retrofit#callAdapter
kotlin 复制代码
  public CallAdapter<?, ?> callAdapter(Type returnType, Annotation[] annotations) {
    return nextCallAdapter(null, returnType, annotations);
  }

  /**
   * 从集合callAdapterFactories最后一个元素开始获取设置的CallAdapter实现类,返回的值不为空,即为所求
   */
  public CallAdapter<?, ?> nextCallAdapter(
      @Nullable CallAdapter.Factory skipPast, Type returnType, Annotation[] annotations) {
    Objects.requireNonNull(returnType, "returnType == null");
    Objects.requireNonNull(annotations, "annotations == null");

    int start = callAdapterFactories.indexOf(skipPast) + 1;
    for (int i = start, count = callAdapterFactories.size(); i < count; i++) {
      CallAdapter<?, ?> adapter = callAdapterFactories.get(i).get(returnType, annotations, this);
      if (adapter != null) {
        return adapter;
      }
    }
  • 那保存的地方又在哪里呢?
    • 在Retrofit的build构建函数中设置:retrofit2.Retrofit.Builder#build
    • 在platform.defaultCallAdapterFactories方法中,(hasJava8Types为true)会将CompletableFutureCallAdapterFactory和DefaultCallAdapterFactory组成集合返回
    • 且DefaultCallAdapterFactory是集合尾部加入,所以在获取的时候也是先拿到DefaultCallAdapterFactory对象判断
less 复制代码
    public Retrofit build() {
      ..
      List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
      callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor));
}

  List<? extends CallAdapter.Factory> defaultCallAdapterFactories(
      @Nullable Executor callbackExecutor) {
    DefaultCallAdapterFactory executorFactory = new DefaultCallAdapterFactory(callbackExecutor);
    return hasJava8Types
        ? asList(CompletableFutureCallAdapterFactory.INSTANCE, executorFactory)
        : singletonList(executorFactory);
  }
retrofit2.DefaultCallAdapterFactory
  • 其中get方法,就是HttpServiceMethod.parseAnnotations每次在解析方法注解时调用createCallAdapter方法获取CallAdapter对象最终的实现
3.7、ExecutorCallbackCall

-get方法返回的是CallAdapter接口的匿名实现类,其中adapter方法最终返回的ExecutorCallbackCall<>(executor, call)

  • 其中executor对象是Retrofit构造函数时,传入的MainThreadExecutor对象,用于进行线程切换
  • 则最终调用listRepos方法返回的Call结果是ExecutorCallbackCall实现类
  • 继续调用enqueue方法,会交给delegate进行实现,也就是OkHttpCall
  • 且OkHttpCall的请求结果回调,会交给 callbackExecutor.execute进行线程切换处理到主线程
java 复制代码
final class DefaultCallAdapterFactory extends CallAdapter.Factory {
  private final @Nullable Executor callbackExecutor;

  DefaultCallAdapterFactory(@Nullable Executor callbackExecutor) {
    this.callbackExecutor = callbackExecutor;
  }

  @Override
  public @Nullable CallAdapter<?, ?> get(
      Type returnType, Annotation[] annotations, Retrofit retrofit) {
	// 判断请求方法返回类型是否是Call,非Call类型返回null,这样的话就会继续从CompletableFutureCallAdapterFactory进行查看
    if (getRawType(returnType) != Call.class) {
      return null;
    }
    ...
    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);
      }
    };

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

OkHttpCall.enqueue发起请求,获取response响应并完成数据解析

  • retrofit2.OkHttpCall#enqueue
java 复制代码
final class OkHttpCall<T> implements Call<T> {

	...
  private okhttp3.Call createRawCall() throws IOException {
    okhttp3.Call call = callFactory.newCall(requestFactory.create(args));
    if (call == null) {
      throw new NullPointerException("Call.Factory returned null.");
    }
    return call;
  }

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

    okhttp3.Call call;
    Throwable failure;

    ...
	// call对象是OkHttpClient调用newCall方法获取到的RealCall实现类
	 call = rawCall = createRawCall();
	
	// 接下来就是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
            }
          }
        });
  }
}

4、响应数据解析

  • retrofit2.OkHttpCall#parseResponse
  • 先判断响应码,响应码在小于200,或大于等于300时,说明请求出错
  • 最后调用响应转换器的convert方法进行数据解析转换,我们最终的目的是将返回的数据流解析为Bean封装对象
scss 复制代码
Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
    ResponseBody rawBody = rawResponse.body();

    // Remove the body's source (the only stateful object) so we can pass the response along.
    rawResponse =
        rawResponse
            .newBuilder()
            .body(new NoContentResponseBody(rawBody.contentType(), rawBody.contentLength()))
            .build();

    int code = rawResponse.code();
    if (code < 200 || code >= 300) {
      try {
        ResponseBody bufferedBody = Utils.buffer(rawBody);
        return Response.error(bufferedBody, rawResponse);
      } finally {
        rawBody.close();
      }
    }

    if (code == 204 || code == 205) {
      rawBody.close();
      return Response.success(null, rawResponse);
    }

    ExceptionCatchingResponseBody catchingBody = new ExceptionCatchingResponseBody(rawBody);
    try {
      T body = responseConverter.convert(catchingBody);
      return Response.success(body, rawResponse);
    } catch (RuntimeException e) {
      // If the underlying source threw an exception, propagate that rather than indicating it was
      // a runtime exception.
      catchingBody.throwIfCaught();
      throw e;
    }
  }

4.1、Gson数据解析

  • retrofit2.converter.gson.GsonResponseBodyConverter#convert
  • 最后通过调用google的Gson库对象响应数据进行解析
java 复制代码
final class GsonResponseBodyConverter<T> implements Converter<ResponseBody, T> {
  private final Gson gson;
  private final TypeAdapter<T> adapter;

  GsonResponseBodyConverter(Gson gson, TypeAdapter<T> adapter) {
    this.gson = gson;
    this.adapter = adapter;
  }

  @Override
  public T convert(ResponseBody value) throws IOException {
    JsonReader jsonReader = gson.newJsonReader(value.charStream());
    try {
      T result = adapter.read(jsonReader);
      if (jsonReader.peek() != JsonToken.END_DOCUMENT) {
        throw new JsonIOException("JSON document was not fully consumed.");
      }
      return result;
    } finally {
      value.close();
    }
  }
}
  • 未完待续
    • Retrofit框架中使用到的设计模式
    • 与RxJava配合使用
相关推荐
Pedantic28 分钟前
SwiftUI 手势层级(Gesture Hierarchy)详解
前端
飘尘44 分钟前
前端转型全栈(Java后端)的快速上手指引
前端·后端·全栈
一颗烂土豆1 小时前
Meshopt 压缩深度解析,为什么它比 Draco 更快
前端·javascript·webgl
浏览器工程师2 小时前
AI Agent 接浏览器任务,先别让它一路点到底
前端·后端
雨季mo浅忆2 小时前
VSCode自动格式化三要素
前端
爱勇宝3 小时前
深扒 Anthropic 1680 位工程师简历:应届生几乎没机会,AI 公司最缺的不是博士
前端·后端·程序员
kyriewen3 小时前
同事每天催我 Code Review,我写了个脚本让 AI 替我 review PR——现在他反过来催 AI 了
前端·javascript·ai编程
user20585561518136 小时前
Windows 项目安装时报 `node-sass` 错误,如何快速处理
前端
LiaCode6 小时前
Redis 在生产项目的使用
前端·后端