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配合使用
相关推荐
旧林84318 分钟前
第八章 利用CSS制作导航菜单
前端·css
yngsqq29 分钟前
c#使用高版本8.0步骤
java·前端·c#
Myli_ing1 小时前
考研倒计时-配色+1
前端·javascript·考研
大白要努力!1 小时前
Android opencv使用Core.hconcat 进行图像拼接
android·opencv
余道各努力,千里自同风1 小时前
前端 vue 如何区分开发环境
前端·javascript·vue.js
软件小伟1 小时前
Vue3+element-plus 实现中英文切换(Vue-i18n组件的使用)
前端·javascript·vue.js
醉の虾2 小时前
Vue3 使用v-for 渲染列表数据后更新
前端·javascript·vue.js
张小小大智慧2 小时前
TypeScript 的发展与基本语法
前端·javascript·typescript
hummhumm2 小时前
第 22 章 - Go语言 测试与基准测试
java·大数据·开发语言·前端·python·golang·log4j
天空中的野鸟2 小时前
Android音频采集
android·音视频