Retrofit源码理解(一)

OKHttp+Retrofit+RxJava的android 网络请求三件套用了不少时间了,但却没有真正进入源码了解过背后的真正原理,所以打算分几部分分别理解一下。

从使用retrofit的初始化代码开始吧。

初始化的代码量不多,初始化的调用是一个建造者模式

    val retrofit:Retrofit = Retrofit.Builder()
            .addConverterFactory(GsonConverterFactory.create(gson))
            .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
            .client(okHttpClient)
            .baseUrl(BuildConfig.BASE_URL)
            .build()
    httpService:HttpService = retrofit.create(HttpService::class.java)
  • 先看一下Retrofit类的内部
public final class Retrofit {
  private final Map<Method, ServiceMethod<?>> serviceMethodCache = new ConcurrentHashMap<>();

  final okhttp3.Call.Factory callFactory;
  final HttpUrl baseUrl;
  final List<Converter.Factory> converterFactories;
  final List<CallAdapter.Factory> callAdapterFactories;
  final @Nullable Executor callbackExecutor;
  final boolean validateEagerly;
  • ServiceMethod是包含泛型T一个抽象类
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);
}
  • 不难猜测我们声明的请求相关的接口HttpService在Retrofit中是通过反射和动态代理生成具体的实现类的
interface HttpService {
    @FormUrlEncoded
    @Headers(TokenInterceptor.headerNoNeedToken)
    @POST
    fun doPost(@Url path: String, @FieldMap params: HashMap<String, String>): Observable<ResponseBody>

    @FormUrlEncoded
    @Headers(TokenInterceptor.headerNeedToken)
    @POST
    fun doTokenPost(@Url path: String, @FieldMap params: HashMap<String, String>): Observable<ResponseBody>

}

public <T> T create(final Class<T> service) {
    Utils.validateServiceInterface(service);
    if (validateEagerly) {
      eagerlyValidateMethods(service);
    }
    return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
        new InvocationHandler() {
          private final Platform platform = Platform.get();

          @Override public 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);
            }
            if (platform.isDefaultMethod(method)) {
              return platform.invokeDefaultMethod(method, service, proxy, args);
            }
            ServiceMethod<Object, Object> serviceMethod =
                (ServiceMethod<Object, Object>) loadServiceMethod(method);
            OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
            return serviceMethod.callAdapter.adapt(okHttpCall);
          }
        });
  }
  • 看一下eagerlyValidateMethods(service)方法的调用,
private void eagerlyValidateMethods(Class<?> service) {
    Platform platform = Platform.get();
    for (Method method : service.getDeclaredMethods()) {
      if (!platform.isDefaultMethod(method)) {
        loadServiceMethod(method);
      }
    }
  }
  • 这里通过传入的service就是Retrofit.create时传入的HttpService.class也就是Class类型的变量
  • 然后判断是否是platform的default方法关于default的话,在Platform类的内部类Java8中的isDefault方法中
  • 一路追溯可以在Method->abstrcat Executable中的Modifier.DEFAULT里看到这段注释:
 // Android-added: DEFAULT to support DEX-defined modifier flag.
    /**
     * Default methods are marked with a synthetic access flag
     * to speed up class loading and invocation target lookup.
     * Implies INTERFACE, not-ABSTRACT, and not-STATIC.
     *
     * @hide
     */
    public static final int DEFAULT = 0x00400000;
  • 简单了解一下就是会把HttpService类中声明的方法存到serviceMethodCache这个ConcurrentHashMap在并发场景中使用的HashMap中

concurrent
英 [kən’kʌr(ə)nt] 美 [kən’kɝənt]
adj. 并发的;一致的;同时发生的
n. [数] 共点;同时发生的事件

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

    synchronized (serviceMethodCache) {
      result = serviceMethodCache.get(method);
      if (result == null) {
        result = new ServiceMethod.Builder<>(this, method).build();
        serviceMethodCache.put(method, result);
      }
    }
    return result;
  }
  • loadServiceMethod(Method method)方法返回了一个ServiceMethod类型的实例,点进去看到

/** Adapts an invocation of an interface method into an HTTP call. */
final class ServiceMethod<R, T> {
  // Upper and lower characters, digits, underscores, and hyphens, starting with a character.
  static final String PARAM = "[a-zA-Z][a-zA-Z0-9_-]*";
  static final Pattern PARAM_URL_REGEX = Pattern.compile("\\{(" + PARAM + ")\\}");
  static final Pattern PARAM_NAME_REGEX = Pattern.compile(PARAM);

  final okhttp3.Call.Factory callFactory;
  final CallAdapter<R, T> callAdapter;

  private final HttpUrl baseUrl;
  private final Converter<ResponseBody, R> responseConverter;
  private final String httpMethod;
  private final String relativeUrl;
  private final Headers headers;
  private final MediaType contentType;
  private final boolean hasBody;
  private final boolean isFormEncoded;
  private final boolean isMultipart;
  private final ParameterHandler<?>[] parameterHandlers;
  • 注释告诉我们,他的作用是把一个接口方法的调用适配到Http Call中
    剩下的就是return 出来的T实例
	Builder(Retrofit retrofit, Method method) {
      this.retrofit = retrofit;
      this.method = method;
      this.methodAnnotations = method.getAnnotations();
      this.parameterTypes = method.getGenericParameterTypes();
      this.parameterAnnotationsArray = method.getParameterAnnotations();
    }
  • ServiceMethod.Builder的构造函数里保存了Method的所有方法注释、泛型参数和参数注释
    以及方法上下文就是我们最开始生成的Retrofit的实例
    再看ServiceMethod.Builder的build()方法做了什么
public ServiceMethod build() {
      callAdapter = createCallAdapter();
      responseType = callAdapter.responseType();
      if (responseType == Response.class || responseType == okhttp3.Response.class) {
        throw methodError("'"
            + Utils.getRawType(responseType).getName()
            + "' is not a valid response body type. Did you mean ResponseBody?");
      }
      responseConverter = createResponseConverter();

      for (Annotation annotation : methodAnnotations) {
        parseMethodAnnotation(annotation);
      }

      if (httpMethod == null) {
        throw methodError("HTTP method annotation is required (e.g., @GET, @POST, etc.).");
      }

      if (!hasBody) {
        if (isMultipart) {
          throw methodError(
              "Multipart can only be specified on HTTP methods with request body (e.g., @POST).");
        }
        if (isFormEncoded) {
          throw methodError("FormUrlEncoded can only be specified on HTTP methods with "
              + "request body (e.g., @POST).");
        }
      }

      int parameterCount = parameterAnnotationsArray.length;
      parameterHandlers = new ParameterHandler<?>[parameterCount];
      for (int p = 0; p < parameterCount; p++) {
        Type parameterType = parameterTypes[p];
        if (Utils.hasUnresolvableType(parameterType)) {
          throw parameterError(p, "Parameter type must not include a type variable or wildcard: %s",
              parameterType);
        }

        Annotation[] parameterAnnotations = parameterAnnotationsArray[p];
        if (parameterAnnotations == null) {
          throw parameterError(p, "No Retrofit annotation found.");
        }

        parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);
      }

      if (relativeUrl == null && !gotUrl) {
        throw methodError("Missing either @%s URL or @Url parameter.", httpMethod);
      }
      if (!isFormEncoded && !isMultipart && !hasBody && gotBody) {
        throw methodError("Non-body HTTP method cannot contain @Body.");
      }
      if (isFormEncoded && !gotField) {
        throw methodError("Form-encoded method must contain at least one @Field.");
      }
      if (isMultipart && !gotPart) {
        throw methodError("Multipart method must contain at least one @Part.");
      }

      return new ServiceMethod<>(this);
    }
  • 粗略看一眼可以知道是对ServiceMethod的一系列初始化,先不去细究这个初始化方法里究竟做了什么,
    回头看继续看Retrofit.Create()方法
return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
        new InvocationHandler() {
          private final Platform platform = Platform.get();

          @Override public 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);
            }
            if (platform.isDefaultMethod(method)) {
              return platform.invokeDefaultMethod(method, service, proxy, args);
            }
            ServiceMethod<Object, Object> serviceMethod =
                (ServiceMethod<Object, Object>) loadServiceMethod(method);
            OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
            return serviceMethod.callAdapter.adapt(okHttpCall);
          }
        });
  • 看到InvocationHandler就能明白,这里是Java的动态代理模式,HttpService这个接口类实际上是一个动态代理
    那么他实际是代理的什么方法,奥秘其实就是在invoke代码里了:
   // If the method is a method from Object then defer to normal invocation.
            if (method.getDeclaringClass() == Object.class) {
              return method.invoke(this, args);
            }
  • 结合注释这几行代码不难理解,如果是Object类中声明的方法,就通过动态代理实例本身去调用
    Platform这里留个坑,还是不太明白,感觉是对JDK1.8的兼容。
	ServiceMethod<Object, Object> serviceMethod =
    (ServiceMethod<Object, Object>) loadServiceMethod(method);
    OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
    return serviceMethod.callAdapter.adapt(okHttpCall);
  • 根据动态代理的经验,可知我们的HttpService代理里的实际网络请求逻辑是由serviceMethod.callAdapter.adapt(okHttpCall)来实现的,也就是说接口中申明的返回结果例如:
 	@FormUrlEncoded
    @Headers(TokenInterceptor.HEADER_NEED_TOKEN)
    @POST()
    Observable<ResponseBody> doTokenPost(@Url String path, @FieldMap HashMap<String, String> params);
    
	@GET("users")
    Call<List<User>> getUsersBySort(@Query("sortby") String sort);
  • 因为一般来说一次调用Retrofit对应的接口中的某一个方法都是进行一次请求,
  • 所以匿名实现类new InvocationHandler()中invoke的时候都生成一个OkHttpCall实例是合理的。

到这里,为什么能通过Retrofit.create()生成的实例调用声明的XXX接口的方法请求,已经有一个大体上的了解了

对动态代理的思考。

这次阅读源码能够看出我们用的如此得心应手的Retrofit框架本身是对OkHttp的一个动态代理,实际的请求和响应都是交给OkHttpClient处理的,当然Retrofit里也用到了适配器模式,虽然没有仔细研读初始化里AddAdatper的代码,但可以简单知道的是Gson解析和Interface里可以返回ResponseBody类型的结果是初始化时gson、RxJava那两个adapter发挥作用的。Retrofit是一个对java动态代理模式很好诠释的一个实现和应用,通过接口和注释规范了OkHttp请求时的参数和方法形式,值得借鉴。由此我能想到的对动态代理应用的场景是有些功能已经实现但是我们需要扩展的时候,可以参考Retrofit实现的形式通过动态代理来实现增强。

你可能感兴趣的:(知识点归纳)