Retrofit源码分析

分析源码之前先介绍几个Retrofit中的概念。

Call,Request,Response,因为Retrofit中也是使用OkHttp来进行网络请求的,这几个概念和OkHttp中的基本类似,可以看下我在OkHttp源码分析一篇中开头对这几个概念的介绍。

CallAdapter<R, T>

Adapts a {@link Call} with response type {@code R} into the type of {@code T}.

将一个返回类型为R的Call适配为一个T。
Retrofit默认的为Call类型,比方说如果我们将Retrofit和RxJava结合使用,需要返回RxJava的Observable类型,那么我们就要提供相应的CallAdapter来将Call转化为Observable。

Converter<F, T>

Convert objects to and from their representation in HTTP.

将F类型转换为T类型。
主要用途是将原始请求参数的类型转换为okhttp3.RequestBody,以及将okhttp3.ResponseBody转换为你想要的返回类型。

本篇使用的Retrofit版本是2.4.0,上代码,一个Retrofit请求。

        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("http://fanyi.youdao.com/")
                .addConverterFactory(GsonConverterFactory.create())
                .build();
        RequestApi request = retrofit.create(RequestApi.class);
        retrofit2.Call<JSONObject> call = request.getCall(word);
        call.enqueue(new retrofit2.Callback<JSONObject>() {
            @Override
            public void onResponse(retrofit2.Call<JSONObject> call, retrofit2.Response<JSONObject> response) {
                if (200 == response.code()) {
                    JSONObject result = response.body();
                }
            }

            @Override
            public void onFailure(@NonNull retrofit2.Call<JSONObject> call, Throwable t) {
                System.out.println("请求失败");
                System.out.println(t.getMessage());
            }
        });

RequestApi类

public interface RequestApi {
    @POST("translate?doctype=json&jsonversion=&type=&keyfrom=&model=&mid=&imei=&vendor=&screen=&ssid=&network=&abtest=")
    @FormUrlEncoded
    Call<JSONObject> getCall(@Field("i") String targetSentence);
}

首先,使用建造者模式完成Retrofit的构建。
然后调用Retrofit的create方法构建了RequestApi对象,我们看一下create方法。

  public <T> T create(final Class<T> 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 {
            ...
            ServiceMethod<Object, Object> serviceMethod =
                (ServiceMethod<Object, Object>) loadServiceMethod(method);
            OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
            return serviceMethod.adapt(okHttpCall);
          }
        });
  }

这是一个动态代理。
接着,使用代理生成的RequestApi对象调用getCall方法构建Call<JSONObject>对象。我们来分析一下这个动态代理内部是如何生成Call<JSONObject>对象的。
第一步调用loadServiceMethod方法来构建了一个ServiceMethod对象。下图截取了ServiceMethod类中的域和主要方法。


methodservice.png

它的主要作用是将一个接口方法的调用适配为一个http请求。
它的创建使用的也是建造者模式。静态类Builder的build方法里面会把我们写在接口的请求方法里面的各种注解进行解析并且转化ServiceMethod对象相应的域。
然后是一个toCall方法,使用ServiceMethod对象的域来创建一个okhttp3.Call,最终会使用该Call来进行网络请求。

我们来看下他的Builder的build方法。方法比较长,我省略了好多不重要的代码,并且对每一段是干啥的加了注释。

public ServiceMethod build() {
      //创建CallAdapter
      callAdapter = createCallAdapter();
      responseType = callAdapter.responseType();

      ...
      //创建返回结果转换的Converter
      responseConverter = createResponseConverter();

      //解析方法上的注解
      for (Annotation annotation : methodAnnotations) {
        parseMethodAnnotation(annotation);
      }

      ...
      //解析参数上的注解
      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);
      }
      ...

      return new ServiceMethod<>(this);
    }

看看创建CallAdapter的createCallAdapter()方法。

   private CallAdapter<T, R> createCallAdapter() {
      Type returnType = method.getGenericReturnType();
      ...
      Annotation[] annotations = method.getAnnotations();
      try {
        //noinspection unchecked
        return (CallAdapter<T, R>) retrofit.callAdapter(returnType, annotations);
      } catch (RuntimeException e) { // Wide exception range because factories are user code.
        throw methodError(e, "Unable to create call adapter for %s", returnType);
      }
    }

这里的method就是我们调用的接口中的相关请求方法,在我们上面的代码中就是getCall方法。将它的类型和方法注解传入retrofit的callAdapter方法来获取合适的CallAdapter。
跟进retrofit的callAdapter方法,发现它调用了nextCallAdapter方法。

 public CallAdapter<?, ?> nextCallAdapter(@Nullable CallAdapter.Factory skipPast, Type returnType,
      Annotation[] annotations) {
    ...
    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;
      }
    }
    ...
  }

这里的callAdapterFactories是一个CallAdapter的工厂列表,代码里面的逻辑是,在callAdapterFactories中依次进行遍历,第一个符合条件的将被返回。
我们在创建Retrofit对象的时候可以向callAdapterFactories添加工厂,比如如果我们将RxJava和Retrofit联合使用,想在interface方法中定义Observable作为返回类型,就需要添加第三方库中的RxJava2CallAdapterFactory。
Retrofit的build代码中添加了一个默认的DefaultCallAdapterFactory,它响应的类型是Retrofit的Call接口,所以如果我们不添加任何额外的CallAdapterFactory,那么interface方法中定义的返回类型只能为Call类型。

下面再来看创建返回结果转换Converter的createResponseConverter方法。

    private Converter<ResponseBody, T> createResponseConverter() {
      Annotation[] annotations = method.getAnnotations();
      try {
        return retrofit.responseBodyConverter(responseType, annotations);
      } catch (RuntimeException e) { // Wide exception range because factories are user code.
        throw methodError(e, "Unable to create converter for %s", responseType);
      }
    }

调用了Retrofit的responseBodyConverter方法,跟进去,发现调用了nextResponseBodyConverter方法。

  public <T> Converter<ResponseBody, T> nextResponseBodyConverter(
      @Nullable Converter.Factory skipPast, Type type, Annotation[] annotations) {
    ...
    int start = converterFactories.indexOf(skipPast) + 1;
    for (int i = start, count = converterFactories.size(); i < count; i++) {
      Converter<ResponseBody, ?> converter =
          converterFactories.get(i).responseBodyConverter(type, annotations, this);
      if (converter != null) {
        //noinspection unchecked
        return (Converter<ResponseBody, T>) converter;
      }
    }
    ...
  }

这个和获取CallAdapter的逻辑简直一模一样。converterFactories是一个Converter.Factory列表,一次遍历选取第一个符合条件的进行返回。

再看解析方法注解的方法parseMethodAnnotation

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);
      } else if (annotation instanceof HEAD) {
      ...
    }

调用了parseHttpMethodAndPath方法,根据不同的注解类型传入不同的参数。

    private void parseHttpMethodAndPath(String httpMethod, String value, boolean hasBody) {
      if (this.httpMethod != null) {
        throw methodError("Only one HTTP method is allowed. Found: %s and %s.",
            this.httpMethod, httpMethod);
      }
      this.httpMethod = httpMethod;
      this.hasBody = hasBody;

      if (value.isEmpty()) {
        return;
      }

      // Get the relative URL path and existing query string, if present.
      int question = value.indexOf('?');
      if (question != -1 && question < value.length() - 1) {
        // Ensure the query string does not have any named parameters.
        String queryParams = value.substring(question + 1);
        Matcher queryParamMatcher = PARAM_URL_REGEX.matcher(queryParams);
        if (queryParamMatcher.find()) {
          throw methodError("URL query string \"%s\" must not have replace block. "
              + "For dynamic query parameters use @Query.", queryParams);
        }
      }

      this.relativeUrl = value;
      this.relativeUrlParamNames = parsePathParameters(value);
    }

在这里解析出来httpMethod,hasBody,relativeUrl,relativeUrlParamNames等参数。

解析参数注解的部分,和解析类注解的部分相似,会根据参数上的注解的不同,生成不同的参数处理器保存到了parameterHandlers域中。

ServiceMethod的Builder的build方法主要做的事情就是这么多了,build方法结束之后,其构建的ServiceMethod对象中的域就都被赋值完成了。

我们再回到之前的动态代理的方法,看看ServiceMethod构建完成之后,构建的OkHttpCall是个啥。

final class OkHttpCall<T> implements Call<T> {
  ...
  @GuardedBy("this")
  private @Nullable okhttp3.Call rawCall;
  ...
  }

嗯,OkHttpCall实现了Retrofit的Call接口,但是其内部持有一个okhttp3.Call
,调用网络请求方法,实际是调用的okhttp3.Call的网络请求方法。

构建完OkHttpCall之后,调用了serviceMethod.adapt(okHttpCall)来返回指定的返回类型,也就是我们代码里面的retrofit2.Call<JSONObject>对象。

  T adapt(Call<R> call) {
    return callAdapter.adapt(call);
  }

这个callAdapter的来历我们在ServiceMethod的build方法里面已经分析过了。由于我们的返回类型是retrofit2.Call<JSONObject>,所以这里的callAdapter会是默认的DefaultCallAdapterFactory返回的CallAdapter

final class DefaultCallAdapterFactory extends CallAdapter.Factory {
  static final CallAdapter.Factory INSTANCE = new DefaultCallAdapterFactory();

  @Override
  public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
    if (getRawType(returnType) != Call.class) {
      return null;
    }

    final Type responseType = Utils.getCallResponseType(returnType);
    return new CallAdapter<Object, Call<?>>() {
      @Override public Type responseType() {
        return responseType;
      }

      @Override public Call<Object> adapt(Call<Object> call) {
        return call;
      }
    };
  }
}

如上所示,DefaultCallAdapterFactory中返回的CallAdapter的adapt方法,只是简单地把传入的Call返回了,什么都没做。
所以我们在动态代理中返回的retrofit2.Call<JSONObject>的实际类型就是传入的OkHttpCall。
到这里,动态代理的代码就分析完了。

再来看看下一步,执行网络请求call.enqueue。
由于这里的call就是OkHttpCall,我们再来OkHttpCall的enqueue方法。

@Override 
public void enqueue(final Callback<T> callback) {
    ...
    okhttp3.Call call;
    ...
      call = rawCall = createRawCall();
    ...

    call.enqueue(new okhttp3.Callback() {
      @Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
        Response<T> response;
        ...
          response = parseResponse(rawResponse);
        ...
          callback.onResponse(OkHttpCall.this, response);
        ...
      }

      @Override public void onFailure(okhttp3.Call call, IOException e) {
        callFailure(e);
      }
      ...
    });

调用了createRawCall方法来创建一个okhttp3.Call,然后调用该okhttp3.Call的enqueue方法。
继续跟进createRawCall方法

  private okhttp3.Call createRawCall() throws IOException {
    okhttp3.Call call = serviceMethod.toCall(args);
    if (call == null) {
      throw new NullPointerException("Call.Factory returned null.");
    }
    return call;
  }

调用了serviceMethod.toCall方法

  /** Builds an HTTP request from method arguments. */
  okhttp3.Call toCall(@Nullable Object... args) throws IOException {
    RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl, headers,
        contentType, hasBody, isFormEncoded, isMultipart);

    @SuppressWarnings("unchecked") // It is an error to invoke a method with the wrong arg types.
    ParameterHandler<Object>[] handlers = (ParameterHandler<Object>[]) parameterHandlers;
    ...

    for (int p = 0; p < argumentCount; p++) {
      handlers[p].apply(requestBuilder, args[p]);
    }

    return callFactory.newCall(requestBuilder.build());
  }

使用了ServiceMethod之前解析出来的参数,比如httpMethod, baseUrl, relativeUrl, headers等等,构建了一个RequestBuilder对象,又使用RequestBuilder对象build出了一个okhttp3.Request对象,进而又构建出一个okhttp3.Call对象。这样,最终就是调用创建的这个okhttp3.Call来进行网络请求的。

可以看到,Retrofit对我们写在接口中的各种注解进行解析,得出请求参数,然后用这些参数构造出okhttp请求来请求网络。同时,它通过CallAdapter可以实现和RxJava的配合使用,通过Converter可以灵活地返回你需要的数据类型。

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

推荐阅读更多精彩内容