Retrofit实现原理分析

948 阅读8分钟

在深入探究 Retrofit 的实现原理之前,我们先提出几个问题,后面带着这个几个问题去研究 Retrofit 的源码 ​

  1. Retrofit 如何解析我们编写的注解接口,将其封装成网络请求
  2. Retrofit 默认接口返回类型是 Call<ResponseBoddy> ,配置 json convert 之后如何将返回类型替换成 Call<Bean> {addConverterFactory}
  3. 在引入Kotlin协程之后,接口方法以 suspend 修饰,返回值直接修改成 Bean 类型,内部是如何实现的 {addCallAdapterFactory}

1. Retrofit的基本使用

Retrofit的官网提供的Retrofit使用的基本步骤 ​

  • 定义请求接口
public interface GitHubService {
  @GET("users/{user}/repos")
  Call<List<Repo>> listRepos(@Path("user") String user);
}
  • 构建Retrofit实例, 获取接口实现类
Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com/")
    .build();

GitHubService service = retrofit.create(GitHubService.class);
  • 调用接口方法
Call<List<Repo>> repos = service.listRepos("octocat");
  • 调用返回的Call实例的execute或者enqueue进行同步或异步请求

2. 原理分析

2.1 JDK动态代理

通过 JDK 提供的动态代理机制为我们的接口生成了具体的实现类。

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);
        }
      })

生成的代理类会将所有方法交给内部的 InvocationHandler的inovke方法进行处理。 ​

2.2 ServiceMethod

抽象类,代表我们定义的接口中的一个方法

  • parseAnnotations 静态方法
    • 解析方法的注解,获取 RequestFactory
    • 通过 HttpServiceMethod 的静态方法进一步解析返回 ServiceMethod 的具体实现

2.3 HttpServiceMethod

抽象类,继承 HttpServiceMethod

  • parseAnnotations 静态方法
    • 获取 callAdapter
    • 获取 responseConvert
    • 创建 HttpServiceMethod 的最终实现类

2.3.1 CallAdapter

接口,对返回的结果进行包装,将内部传入的 Call (其实就是OkHttpCall) 转换成其他类型。

  • Type responseType()
    • 响应的类型 eg: Call的响应类型为Repo
  • adapt(Call) T

默认为 DefaultCallAdapterFactory 中的匿名CallAdapter实现

public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
    if (getRawType(returnType) != Call.class) {
        return null;
    } else if (!(returnType instanceof ParameterizedType)) {
        throw new IllegalArgumentException("Call return type must be parameterized as Call<Foo> or Call<? extends Foo>");
    } else {
        final Type responseType = Utils.getParameterUpperBound(0, (ParameterizedType)returnType);
        // 有没有SkipCallbackExecutor注解修饰 (debug 发现默认就有, annotations里面有这个注解)
        // 有的话executor为null, 否则为 this.callbackExecutor
        // 主要是用来设定回调的执行线程
        final Executor executor = Utils.isAnnotationPresent(annotations, SkipCallbackExecutor.class) ? null : this.callbackExecutor;
        return new CallAdapter<Object, Call<?>>() {
            public Type responseType() {
                return responseType;
            }

            public Call<Object> adapt(Call<Object> call) {
                // executor为null返回原来的call
                // executor不为null, 返回返回包装过的 ExecutorCallbackCall
                // ExecutorCallbackCall 持有原本的call, 将回调Call.enqueue中的回调方法executor中执行, Android平台是
                return (Call)(executor == null ? call : new DefaultCallAdapterFactory.ExecutorCallbackCall(executor, call));
            }
        };
    }
}

2.3.2 ResponseConvert

默认为 BuiltInConverters 处理 ResponseBody 返回。在构建Retrofit实例的时候可以通过addConverterFactory方法进行添加。 ​

后面会分析到 Convert 作用的时机。

2.4 接口调用流程

根据上面的流程来看,Retrofit 会为每个接口生成一个代理对象,调用接口方法时进入代理对象内的 InvocationHandler 的 invoke 方法中,该方法中为调用的接口方法生成了 ServiceMethod 对象,并进行缓存,最后调用 ServiceMethod 对象的 invoke 方法获取结果。
ServiceMethod 的 invoke 方法为抽象方法,具体实现在 HttpServiceMethod 中。

2.4.1 HttpServiceMethod#invoke

// 1. 创建一个 OkHttpCall (Retrofit)中的
// 2. 调用 adapt 方法
final @Nullable ReturnT invoke(Object[] args) {
  Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
  return adapt(call, args);
}
// 抽象方法 (具体子类中实现)
protected abstract @Nullable ReturnT adapt(Call<ResponseT> call, Object[] args);

HttpServiceMethod 类中有三个 static final 的子类,主要是非 Kotlin 协程 和 Kotlin 协程下的不同支持 ​

2.4.2 CallAdapted

// 非Kotlin协程的接口
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) {
      // 就是调用 CallAdapter 的 adapt 方法
      return callAdapter.adapt(call);
    }
}

从上文介绍的 CallAdapter 可知,在没有手动 addCallAdapterFactory 的情况下,默认的 CallAdapter 是 DefaultCallAdapterFactory中创建的匿名对象,默认返回传入的 call。 ​

2.4.3 OkHttpCall

实现 Retrofit 提供的 Call 接口,在未使用 Kotlin 协程的情况下调用接口方法返回的 Call 对象的实际类型。 ​

  • 成员变量
// okHttp中的Call
private @Nullable okhttp3.Call rawCall;
  • enqueue(Callback callback)
// callback 是 Retrofit 提供的回调
public void enqueue(final Callback<T> callback) {
  Objects.requireNonNull(callback, "callback == null");

  // OkHttp的Call
  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 {
        // 1. 创建 OkHttp 中的Call, 返回 RealCall
        call = rawCall = createRawCall();
      } catch (Throwable t) {
        throwIfFatal(t);
        failure = creationFailure = t;
      }
    }
  }

  // ......

  // 2. 利用OKHttp完成网络请求
  call.enqueue(
      new okhttp3.Callback() {
         // 2.1 okHttp的成功回调
        @Override
        public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
          Response<T> response;
            
          // 2.1.1 解析返回数据
          try {
            response = parseResponse(rawResponse);
          } catch (Throwable e) {
              // ...
          }
          // 2.1.2 调用 Retrofit 的回调接口
          try {
            callback.onResponse(OkHttpCall.this, response);
          } catch (Throwable t) {
              // ...
          }
        }
        
        // 2.2 OkHttp的失败回调
        @Override
        public void onFailure(okhttp3.Call call, IOException e) {
          callFailure(e);
        }
        // 2.2.1 调用 Retrofit 的回调接口
        private void callFailure(Throwable e) {
          try {
            callback.onFailure(OkHttpCall.this, e);
          } catch (Throwable t) {
              //...
          }
        }
      });
}
  • createRawCall
private okhttp3.Call createRawCall() throws IOException {
    // callFactory 就是 OkHttpClient
    // newCall 返回 RealCall 实例
    // requestFactory 构建 OkHttp 中的 Request 实例
    // requestFactory 是 ServiceMethod 中静态方法 parseAnnotations 的返回结果
    okhttp3.Call call = callFactory.newCall(requestFactory.create(args));
    if (call == null) {
      throw new NullPointerException("Call.Factory returned null.");
    }
    return call;
  }
  • parseResponse
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 {
      // Buffer the entire body to avoid future I/O.
      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 {
    // 调用 responseConverter 解析返回数据
    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;
  }
}

从 OKHttpCall 的实现可以看出,Retrofit 将底层的网路请求交给 OkHttp 进行处理,在 OkHttp 的 Callback 中解析并转换返回数据,同时回调用户通过 Retrofit Api 传递的网络请求回调。上文2.2.3中介绍的Convert就作用在这个解析的阶段。 ​

2.4.4 协程suspend接口的处理

Retrofit 是使用Java编写的库,使用AS将我们编写的Kotlin接口进行反编译。 AS 中 code --> Decompile Kotlin to Java

// Kotlin suspend 接口
@FormUrlEncoded
@POST("book/borrow")
suspend fun borrowBook(
    @Field("cardId") cardId: Long?,
    @Field("bookIsbn") bookIsbn: String
): ApiResult<ApiResponse<BorrowBookResponse>>

// 反编译后的 Java 接口
public interface BookService {
    @FormUrlEncoded
    @POST(value="book/borrow")
    @Nullable
    public Object borrowBook(
    @Field(value="cardId") @Nullable Long var1, 
    @Field(value="bookIsbn") @NotNull String var2, 
    @NotNull Continuation<? super ApiResult<ApiResponse<BorrowBookResponse>>> var3);

可以看到其在最后给我们加了一个 Continuation 类型的参数,来标识这是一个 Kotlin 协程的接口。 在前文介绍的 ServiceMethod 的 parseAnnotations 静态方法进行解析的过程中,会对接口方法的参数进行解析,其中就会判断方法的参数类型是否为 Continuation 类型,如果是的话会将 isKotlinSuspendFunction 设置为true, 在后续的 HttpServiceMethod 类的 parseAnnotations 方法中会根据该值去构造具体的实现类。

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 (result == null) {
        if (allowContinuation) {
          try {
              // 判断参数类型是否为 Continuation
            if (Utils.getRawType(parameterType) == Continuation.class) {
              isKotlinSuspendFunction = true;
              return null;
            }
          } catch (NoClassDefFoundError ignored) {
          }
        }
        throw parameterError(method, p, "No Retrofit annotation found.");
      }

      return result;
    }

HttpServiceMethod#parseAnnotations方法的相关实现

// callFactory 其实就是 OkHttpClient
okhttp3.Call.Factory callFactory = retrofit.callFactory;
if (!isKotlinSuspendFunction) {
  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);
}

可以看到在 isKotlinSuspendFunction 为false的情况下,构造的是前面介绍的 CallAdapted。而当其为 true 时有两个具体实现,主要区别是一个返回的包含 Response,另一个直接是 Body 的值,一般我们直接返回数据对象时都是 SuspendForBody。下面分析分析一下其内部实现。主要分析其 adapt 方法的实现

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

  Continuation<ResponseT> continuation = (Continuation<ResponseT>) args[args.length - 1];

  try {
    return isNullable
        ? KotlinExtensions.awaitNullable(call, continuation)
        : KotlinExtensions.await(call, continuation);
  } catch (Exception e) {
    return KotlinExtensions.suspendAndThrow(e, continuation);
  }
}

// 接收者 Call
suspend fun <T : Any> Call<T>.await(): T {
  return suspendCancellableCoroutine { continuation ->
    continuation.invokeOnCancellation {
      cancel()
    }
    // 内部调用 Call 的 enqueue 方法
    enqueue(object : Callback<T> {
      override fun onResponse(call: Call<T>, response: Response<T>) {
        if (response.isSuccessful) {
            // 获取到请求结果
          val body = response.body()
          if (body == null) {
            val invocation = call.request().tag(Invocation::class.java)!!
            val method = invocation.method()
            val e = KotlinNullPointerException("Response from " +
                method.declaringClass.name +
                '.' +
                method.name +
                " was null but response body type was declared as non-null")
            continuation.resumeWithException(e)
          } else {
              // 返回请求结果
            continuation.resume(body)
          }
        } else {
          continuation.resumeWithException(HttpException(response))
        }
      }

      override fun onFailure(call: Call<T>, t: Throwable) {
        continuation.resumeWithException(t)
      }
    })
  }
}

可以看到使用了 kotlin 协程之后框架帮我们调用的 Call 对象的enqueue方法并将响应的 body 返回给我们。 ​

3. 问题解析

  • Retrofit如何解析我们编写的请求接口?
    • 在 ServiceMethod 类的 parseAnnotations 静态方法中调用 RequestFactory.parseAnnotations方法完成解析,并封装成 RequestFactory。
  • Retrofit中的我们添加的Json Convert是如何生效的?
    • 在 OkHttpCall 的 enqueue 方法中,内部会先调用 OkHttp 的 Call (实际为RealCall)完成网络请求,并在 RealCall 的回调方法中,调用了 Convert 的方法对网络返回的数据进行解析,此外还调用了 Retrofit中的传递的Callback方法。
  • Retrofit中的CallAdapter的作用?
    • CallAdapter 可以用来修改调用接口方法返回值类型,默认为 Call 类型,可以修改成其他类型,如 RxJava 中返回 Observable 类型(RxJava本人不了解)。
    • 实现时可以提供一个自己的对象,内部持有 Retrofit 的 OkHttpCall,实际的请求仍然由 OkHttpCall 完成。
    • 一般我们可以提供一个自己实现的Call类型,内部持有 OkHttpCall,在OkHttpCall的回调方法中对请求结果进行统一的封装之后,在调用外部 Callback 的 onResponse 和 onFailure 回调将封装后的结果带出去。
  • 结合kotlin写成之后如何直接返回结果,不返回Call类型的?
    • 结合上面的分析可知,框架内部帮我们用 Call 调用过 enqueue 进行请求了。

参考文献