Retrofit2.9.0功能&源码

1. Retrofit功能

Retrofit 是一个广泛用于处理网络请求的库,主要用于在 Android 和 Java 应用中进行 HTTP 请求。以下是 Retrofit 的一些主要功能:

  1. 声明式 API 定义: Retrofit 允许你使用简单的注解来定义 API 接口,使得你可以通过接口声明来描述 HTTP 请求。

    less 复制代码
    javaCopy code
    public interface ApiService {
        @GET("user/{id}")
        Call<User> getUser(@Path("id") int userId);
    }
  2. 强大的 URL 拼接: Retrofit 提供了简单而强大的 URL 拼接功能,使得构建复杂的请求 URL 变得容易。

    less 复制代码
    javaCopy code
    @GET("user/list")
    Call<List<User>> getUserList(@Query("page") int page, @Query("size") int size);
  3. 同步和异步请求: Retrofit 支持同步和异步的请求方式,通过 Call 类来执行同步或异步请求。

    less 复制代码
    javaCopy code
    Call<User> call = apiService.getUser(1);
    // 同步请求
    Response<User> response = call.execute();
    // 异步请求
    call.enqueue(new Callback<User>() {
        @Override
        public void onResponse(Call<User> call, Response<User> response) {
            // 处理响应
        }
    
        @Override
        public void onFailure(Call<User> call, Throwable t) {
            // 处理失败
        }
    });
  4. 灵活的数据转换: Retrofit 支持使用不同的转换器(Converter)来处理请求和响应的数据格式,例如 JSON、XML、ProtoBuf 等。

    scss 复制代码
    javaCopy code
    // 使用 Gson 转换器
    Retrofit retrofit = new Retrofit.Builder()
        .baseUrl("https://api.example.com/")
        .addConverterFactory(GsonConverterFactory.create())
        .build();
  5. 动态 URL: Retrofit 允许你在运行时动态地修改请求的 URL,这对于构建动态参数的请求非常有用。

    less 复制代码
    javaCopy code
    @GET
    Call<User> getUser(@Url String url);
    `
  6. 支持文件上传和下载: Retrofit 提供了方便的方式来处理文件上传和下载。

    less 复制代码
    javaCopy code
    @Multipart
    @POST("upload")
    Call<ResponseBody> uploadFile(@Part MultipartBody.Part filePart);
  7. 协程的支持:在Retrofit2.9中默认添加了协程的支持,可以定义接口函数为挂起函数。

2 Retrofit设计模式

  1. 动态代理模式
  2. 创建者模式
  3. 工厂模式
  4. 适配器模式
  5. 策略模式

3. Retrofit源码解析

3.1 方法上的注解如何解析(函数注解+函数参数注解)

  1. 解析的入口是ServiceMethod的parseAnnotations方法
java 复制代码
static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
  RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);

  Type returnType = method.getGenericReturnType();
  //.....
  return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
}
  1. 通过RequestFactory.parseAnnotations方法获取注解的一系列参数,包括函数注解(GET,POST等),函数参数注解(Path,Query,Url等)。最终返回一个RequestFactory对象,这个对象内部解析并记录了这些参数。 内部Buidler类记录了需要解析的数据:
dart 复制代码
static final class Builder {
// ....
    final Retrofit retrofit;
    final Method method;
    final Annotation[] methodAnnotations;// 函数注解
    final Annotation[][] parameterAnnotationsArray; //函数参数注解
    final Type[] parameterTypes;//参数类型
    // ....
}

函数注解和函数参数注解处理过程:

java 复制代码
RequestFactory build() {
  for (Annotation annotation : methodAnnotations) {
    parseMethodAnnotation(annotation); //处理函数注解
  }
  

private void parseMethodAnnotation(Annotation annotation) {
  if (annotation instanceof DELETE) {
    parseHttpMethodAndPath("DELETE", ((DELETE) annotation).value(), false);
  } else if (annotation instanceof GET) {
    parseHttpMethodAndPath("GET", ((GET) annotation).value(), false);
  }
  .....
}
 

int parameterCount = parameterAnnotationsArray.length;
parameterHandlers = new ParameterHandler<?>[parameterCount];
for (int p = 0, lastParameter = parameterCount - 1; p < parameterCount; p++) {
  parameterHandlers[p] =
      parseParameter(p, parameterTypes[p], parameterAnnotationsArray[p], p == lastParameter);
}

// 处理函数参数注解
private @Nullable ParameterHandler<?> parseParameter(
    int p, Type parameterType, @Nullable Annotation[] annotations, boolean allowContinuation) {
  ParameterHandler<?> result = null;
  if (annotations != null) {
    for (Annotation annotation : annotations) {
      ParameterHandler<?> annotationAction =
          parseParameterAnnotation(p, parameterType, annotations, annotation);
      result = annotationAction;
    }
  }

3.2 创建Okhttp请求过程,在RequestFactory中实现

ini 复制代码
okhttp3.Request create(Object[] args) throws IOException {

  ParameterHandler<Object>[] handlers = (ParameterHandler<Object>[]) parameterHandlers;

  int argumentCount = args.length;

  RequestBuilder requestBuilder =
      new RequestBuilder(
          httpMethod,
          baseUrl,
          relativeUrl,
          headers,
          contentType,
          hasBody,
          isFormEncoded,
          isMultipart);

  if (isKotlinSuspendFunction) {
    // The Continuation is the last parameter and the handlers array contains null at that index.
    argumentCount--;
  }

  List<Object> argumentList = new ArrayList<>(argumentCount);
  for (int p = 0; p < argumentCount; p++) {
    argumentList.add(args[p]);
    // 之前函数参数的注解处理过程,将所有的注解都处理成一个ParameterHandler对象,每个特定注解都会继承ParameterHandler类,实现apply方法
    handlers[p].apply(requestBuilder, args[p]);
  }

  return requestBuilder.get().tag(Invocation.class, new Invocation(method, argumentList)).build();
}

例如,Header注解最终处理交给了Header类,实现的apply方法作用是添加请求的头部信息,以注解的参数为key,注解的值为value。

java 复制代码
static final class Header<T> extends ParameterHandler<T> {
  private final String name;
  private final Converter<T, String> valueConverter;

  Header(String name, Converter<T, String> valueConverter) {
    this.name = Objects.requireNonNull(name, "name == null");
    this.valueConverter = valueConverter;
  }

  @Override
  void apply(RequestBuilder builder, @Nullable T value) throws IOException {
    if (value == null) return; // Skip null values.

    String headerValue = valueConverter.convert(value);
    if (headerValue == null) return; // Skip converted but null values.

    builder.addHeader(name, headerValue);
  }
}

3.3 CallAdapter作用以及实现

CallAdapter的作用是将Call对象转换成自定义的对象,成应用程序中所需要的其他类型,通常是某种回调、响应类型或者异步操作,比如Rxjava的观察者。CallAdapter接口中转换方法的定义:

java 复制代码
public interface CallAdapter<R, T> {
    Type responseType();
    T adapt(Call<R> call);
   
    abstract class Factory {

      public abstract @Nullable CallAdapter<?, ?> get(
          Type returnType, Annotation[] annotations, Retrofit retrofit);

      protected static Type getParameterUpperBound(int index, ParameterizedType type) {
        return Utils.getParameterUpperBound(index, type);
      }

      protected static Class<?> getRawType(Type type) {
        return Utils.getRawType(type);
      }
   }
}

默认的实现是DefaultCallAdapterFactory。执行流程是在Retofit中loadServiceMethod(service, method).invoke(args)会执行HttpServiceMethod的invoke方法,再执行adapt方法,后在实现类CallAdapted中调用CallAdapter的adapt方法。

3.4 Converter转换成JavaBean的过程

在HttpServiceMethod中注册了Converter<ResponseBody, ResponseT> responseConverter对象,通过上述CallAdapter的转换最终执行到OkHttpCall类的parseResponse方法中,该方法执行T body = responseConverter.convert(catchingBody);会执行转换成javabean的逻辑。 转换器Converter接口定义如下:

java 复制代码
public interface Converter<F, T> {
  @Nullable
  T convert(F value) throws IOException;

  abstract class Factory {

    public @Nullable Converter<ResponseBody, ?> responseBodyConverter(
        Type type, Annotation[] annotations, Retrofit retrofit) {
      return null;
    }

    public @Nullable Converter<?, RequestBody> requestBodyConverter(
        Type type,
        Annotation[] parameterAnnotations,
        Annotation[] methodAnnotations,
        Retrofit retrofit) {
      return null;
    }

    public @Nullable Converter<?, String> stringConverter(
        Type type, Annotation[] annotations, Retrofit retrofit) {
      return null;
    }

    protected static Type getParameterUpperBound(int index, ParameterizedType type) {
      return Utils.getParameterUpperBound(index, type);
    }

    protected static Class<?> getRawType(Type type) {
      return Utils.getRawType(type);
    }
  }
}

3.5 动态代理使用

Java 动态代理通过运行时生成代理类和对象,使得可以在运行时动态地为被代理的对象添加额外的行为,实现了一些灵活的编程模式。在执行被代理方法的时候,会执行InvocationHandler的invoke方法,并传入被代理对象和Method对象和参数表。 这里的动态代理是将所有定义在service接口中方法都代理到下面的invoke方法中,在逐步解析注解,创建请求,解析转换成javabean。

java 复制代码
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 {
              // 如果是Object中定义的方法直接走原来实现
              if (method.getDeclaringClass() == Object.class) {
                return method.invoke(this, args);
              }
              args = args != null ? args : emptyArgs;
              // 排除掉java8,java8接口默认可以有实现
              return platform.isDefaultMethod(method)
                  ? platform.invokeDefaultMethod(method, service, proxy, args)
                  : loadServiceMethod(method).invoke(args);
            }
          });
}

3.6 协程处理

如果是挂起函数,会执行下面的SuspendForResponse类来处理CallAdapterSuspendForResponseHttpServiceMethod的子类重写了adapt方法。由于挂起函数实际原理是将协程对象加入到最后一位函数参数中,也就是kotlin.coroutines.Continuation对象。这里获取这各对象再执行awaitResponse方法,这是一个扩充类方法,实现最终的enqueue请求。

java 复制代码
static final class SuspendForResponse<ResponseT> extends HttpServiceMethod<ResponseT, Object> {
  private final CallAdapter<ResponseT, Call<ResponseT>> callAdapter;

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

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

    //noinspection unchecked Checked by reflection inside RequestFactory.
    Continuation<Response<ResponseT>> continuation =
        (Continuation<Response<ResponseT>>) args[args.length - 1];

    // See SuspendForBody for explanation about this try/catch.
    try {
      return KotlinExtensions.awaitResponse(call, continuation);
    } catch (Exception e) {
      return KotlinExtensions.suspendAndThrow(e, continuation);
    }
  }
}

awaitResponse方法源码

kotlin 复制代码
suspend fun <T> Call<T>.awaitResponse(): Response<T> {
  return suspendCancellableCoroutine { continuation ->
    continuation.invokeOnCancellation {
      cancel()
    }
    enqueue(object : Callback<T> {
      override fun onResponse(call: Call<T>, response: Response<T>) {
        continuation.resume(response)
      }

      override fun onFailure(call: Call<T>, t: Throwable) {
        continuation.resumeWithException(t)
      }
    })
  }
}
相关推荐
Java指南修炼2 天前
一个开源的大语言模型(LLM)服务工具,支持Llama 3.1、Phi 3、Mistral、Gemma 2 等, 87.4k star你必须拥有(附源码)
人工智能·后端·语言模型·开源·源码
一 乐3 天前
英语学习交流平台|基于java的英语学习交流平台系统小程序(源码+数据库+文档)
java·数据库·vue.js·学习·小程序·源码
IT毕设梦工厂4 天前
计算机毕业设计选题推荐-推拿知识互动平台-Java/Python项目实战
java·spring boot·python·django·毕业设计·源码·课程设计
工业互联网专业4 天前
基于ssm+vue+uniapp的智能停车场管理系统小程序
vue.js·小程序·uni-app·毕业设计·ssm·源码·课程设计
IT毕设梦工厂5 天前
计算机毕业设计选题推荐-勤工俭学兼职系统-助学兼职系统-Java/Python项目实战(亮点:手机验证码验证+数据可视化)
java·spring boot·python·django·毕业设计·源码·课程设计
一 乐5 天前
在线小说|基于java的小说阅读系统小程序(源码+数据库+文档)
java·开发语言·数据库·小程序·源码
IT毕设梦工厂5 天前
计算机毕业设计选题推荐-项目评审系统-Java/Python项目实战
java·spring boot·python·django·毕业设计·源码·课程设计
IT研究室7 天前
计算机毕业设计选题推荐-校园车辆管理系统-Java/Python项目实战(亮点:数据可视化分析、账号锁定)
java·spring boot·python·django·毕业设计·源码·课程设计
一 乐7 天前
在线考试|基于java的模拟考试系统小程序(源码+数据库+文档)
java·spring boot·小程序·apache·源码·模拟考试