Retrofit源码阅读

  • 关于动态代理,写在前面
    分两点动态指的是在运行期,而代理指的是实现了某个接口的具体类,称之为代理,会调用了 InvocationHandler 的 invoke方法。
    Retrofit 中的动态代理:
    在代码运行中,会动态创建 GitHubApiService 接口的实现类,作为代理对象,代理接口的方法
    在我们调用GitHubApiService 接口的实现类的 listRepos方法时,会调用了 InvocationHandler 的 invoke方法。
    本质上是在运行期,生成了 GitHubApiService 接口的实现类,调用了 InvocationHandler 的 invoke方法。
  1. Retrofit.Builder()
 public static final class Builder {
//储存默认的calladapter和converter,执行的build()的时候和add方法添加的一起传入建立的retrofit实例
    private final Platform platform;
//根据request生成okhttp3.newCall
    private @Nullable okhttp3.Call.Factory callFactory;
    private @Nullable HttpUrl baseUrl;
//储存add方法中加入的converter和calladapter
    private final List<Converter.Factory> converterFactories = new ArrayList<>();
    private final List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>();
    private @Nullable Executor callbackExecutor;
    private boolean validateEagerly;
  1. retrofit.create(service)
    通过动态代理创建service的实例
  public <T> T create(final Class<T> service) {
//先检查传入的参数是否满足要求,然后遍历注解,只执行了loadServiceMethod()方法,将其储存在serviceMethodCache中,并没有invoke()。
    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);
              }
            });
  }

其中 loadServiceMethod()

 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;
  }
  1. 解析注解
  static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
//此方法可以实现method的复用,下次可以直接用不用解析注解,见源码的解析
//Inspects the annotations on an interface method to construct 
//a reusable service method. This requires potentially-expensive 
//reflection so it is best to build each service method only once 
//and reuse it. Builders cannot be reused.
    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.");
    }
//此方法实现对注解进一步解析成http method,同时因为方法用了消耗大的反射,因此实现了复用,下次请求方法可以直接使用
    return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
  }
  1. CallAdapter详细介绍

定义:网络请求执行器(Call)的适配器

Call在Retrofit里默认是OkHttpCall
在Retrofit中提供了四种CallAdapterFactory: ExecutorCallAdapterFactory(默认)、GuavaCallAdapterFactory、Java8CallAdapterFactory、RxJavaCallAdapterFactory

作用:将默认的网络请求执行器(OkHttpCall)转换成适合被不同平台来调用的网络请求执行器形式

如:一开始Retrofit只打算利用OkHttpCall通过ExecutorCallbackCall切换线程;但后来发现使用Rxjava更加方便(不需要Handler来切换线程)。想要实现Rxjava的情况,那就得使用RxJavaCallAdapterFactoryCallAdapter将OkHttpCall转换成Rxjava(Scheduler):

// 把response封装成rxjava的Observeble,然后进行流式操作

Retrofit.Builder.addCallAdapterFactory(newRxJavaCallAdapterFactory().create()); 
// 关于RxJava的使用这里不作更多的展开

Retrofit还支持java8、Guava平台。

好处:用最小代价兼容更多平台,即能适配更多的使用场景

  1. ConverterFactory

  2. 网络请求,调用了okhttp3

  // Create an instance of our GitHub API interface.
    GitHub github = retrofit.create(GitHub.class);

    // Create a call instance for looking up Retrofit contributors.
    Call<List<Contributor>> call = github.contributors("square", "retrofit");

    // Fetch and print a list of the contributors to the library.
    List<Contributor> contributors = call.execute().body();
  1. 关于协程
    因为在代码编译的过程中会自动为带有suspend的函数添加一个Continuation类型的参数,并将其添加到最后面。所以上面的协程真正的面目是这样的:
@GET("/v2/news")
fun newsGet(@QueryMap params: Map<String, String>, c: Continuation<NewsResponse>): NewsResponse

所以在提取参数的注解的时候会专门将最后一个参数标注出来。将解析参数注解方法的allowContinuation参数设置为true,当result为Null即没有注解时如果时最后一个参数则判断是否是协程的参数类型

 private @Nullable ParameterHandler<?> parseParameter(
        int p, Type parameterType, @Nullable Annotation[] annotations, boolean allowContinuation) {
      ParameterHandler<?> result = null;
      if (annotations != null) {
        for (Annotation annotation : annotations) {
          //1.解析方法参数的注解,并验证它们的合法性
          ParameterHandler<?> annotationAction =
              parseParameterAnnotation(p, parameterType, annotations, annotation);

          if (annotationAction == null) {
            continue;
          }

          //每个参数都只能有一个注解
          if (result != null) {
            throw parameterError(method, p,
                "Multiple Retrofit annotations found, only one allowed.");
          }

          result = annotationAction;
        }
      }

      //2.判断是否是协程 
      if (result == null) {
        if (allowContinuation) {
          try {
//判断是否是协程的参数类型
            if (Utils.getRawType(parameterType) == Continuation.class) {
              isKotlinSuspendFunction = true;
              return null;
            }
          } catch (NoClassDefFoundError ignored) {
          }
        }
        throw parameterError(method, p, "No Retrofit annotation found.");
      }

      return result;
    }

请求返回时

  static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
      Retrofit retrofit, Method method, RequestFactory requestFactory) {
    boolean isKotlinSuspendFunction = requestFactory.isKotlinSuspendFunction;
    boolean continuationWantsResponse = false;
    boolean continuationBodyNullable = false;

    Annotation[] annotations = method.getAnnotations();
    Type adapterType;
    // 1. 是协程
    if (isKotlinSuspendFunction) {
      Type[] parameterTypes = method.getGenericParameterTypes();
      Type responseType = Utils.getParameterLowerBound(0,
          (ParameterizedType) parameterTypes[parameterTypes.length - 1]);
      // 2. 判断接口方法返回的类型是否是Response
      if (getRawType(responseType) == Response.class && responseType instanceof ParameterizedType) {
        // Unwrap the actual body type from Response<T>.
        responseType = Utils.getParameterUpperBound(0, (ParameterizedType) responseType);
        continuationWantsResponse = true;
      } else {
        // TODO figure out if type is nullable or not
        // Metadata metadata = method.getDeclaringClass().getAnnotation(Metadata.class)
        // Find the entry for method
        // Determine if return type is nullable or not
      }

      // 3. 注意:将方法返回类型伪装成Call类型,并将SkipCallbackExecutor注解添加到annotations中,跳过创建Executor,协程不需要Executor来切换线程
      adapterType = new Utils.ParameterizedTypeImpl(null, Call.class, responseType);
      annotations = SkipCallbackExecutorImpl.ensurePresent(annotations);
    } else {
      adapterType = method.getGenericReturnType();
    }

    // 4. 创建CallAdapter,适配call,将其转化成需要的类型
    CallAdapter<ResponseT, ReturnT> callAdapter =
        createCallAdapter(retrofit, method, adapterType, annotations);
    Type responseType = callAdapter.responseType();
    // 5. 创建Converter,将响应的数据转化成对应的model类型
    Converter<ResponseBody, ResponseT> responseConverter =
        createResponseConverter(retrofit, method, responseType);

    okhttp3.Call.Factory callFactory = retrofit.callFactory;
    // 6. 接口方法不是协程
    if (!isKotlinSuspendFunction) {
      return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);
    } else if (continuationWantsResponse) {
      // 7. 接口方法是协程,同时返回类型是Response类型
      //noinspection unchecked Kotlin compiler guarantees ReturnT to be Object.
      return (HttpServiceMethod<ResponseT, ReturnT>) new SuspendForResponse<>(requestFactory,
          callFactory, responseConverter, (CallAdapter<ResponseT, Call<ResponseT>>) callAdapter);
    } else {
      // 8. 接口方法是协程,同时返回类型是body,即自定义的model类型
      //noinspection unchecked Kotlin compiler guarantees ReturnT to be Object.
      return (HttpServiceMethod<ResponseT, ReturnT>) new SuspendForBody<>(requestFactory,
          callFactory, responseConverter, (CallAdapter<ResponseT, Call<ResponseT>>) callAdapter,
          continuationBodyNullable);
    }
  }

将Call转化成对应的Model

public SuspendForBody<ResponseT> extends HttpServiceMethod<ResponseT, Object> {

.....

 @Override protected Object adapt(Call<ResponseT> call, Object[] args) {
      // 1. 获取适配的Call
      call = callAdapter.adapt(call);

      //noinspection unchecked Checked by reflection inside RequestFactory.
      // 2. 获取协程的Continuation
      Continuation<ResponseT> continuation = (Continuation<ResponseT>) args[args.length - 1];
      return isNullable
          ? KotlinExtensions.awaitNullable(call, continuation)
          : KotlinExtensions.await(call, continuation);
    }
}

对于伪装成call是在retrofit默认的callAdapter的开始有一个判断,如果是协程会被识别出来并跳过

//此为retrofit默认的callAdapter方法,即SuspendForBod.adpat()开始调用的方法
@Override public @Nullable CallAdapter<?, ?> get(
      Type returnType, Annotation[] annotations, Retrofit retrofit) {
 if (getRawType(returnType) != Call.class) {
      return null;
    }
...
}

同时在协程自己的SuspendForBody()系列方法中实现了自己的adapt()方法,实现返回类型的转换

suspend fun <T : Any> Call<T>.await(): T {

...

  // 1. 拿到body
          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")
            // 2. body为空,唤起协程,抛出异常
            continuation.resumeWithException(e)
          } else {
            // 3. 唤起协程,返回body
            continuation.resume(body)
          }
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,616评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,020评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,078评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,040评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,154评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,265评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,298评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,072评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,491评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,795评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,970评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,654评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,272评论 3 318
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,985评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,223评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,815评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,852评论 2 351

推荐阅读更多精彩内容