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類型嗓袱。
主要用途是將原始請求參數(shù)的類型轉換為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);
          }
        });
  }

這是一個動態(tài)代理。
接著圆裕,使用代理生成的RequestApi對象調用getCall方法構建Call<JSONObject>對象广鳍。我們來分析一下這個動態(tài)代理內部是如何生成Call<JSONObject>對象的。
第一步調用loadServiceMethod方法來構建了一個ServiceMethod對象吓妆。下圖截取了ServiceMethod類中的域和主要方法赊时。


methodservice.png

它的主要作用是將一個接口方法的調用適配為一個http請求。
它的創(chuàng)建使用的也是建造者模式行拢。靜態(tài)類Builder的build方法里面會把我們寫在接口的請求方法里面的各種注解進行解析并且轉化ServiceMethod對象相應的域祖秒。
然后是一個toCall方法,使用ServiceMethod對象的域來創(chuàng)建一個okhttp3.Call舟奠,最終會使用該Call來進行網絡請求竭缝。

我們來看下他的Builder的build方法。方法比較長沼瘫,我省略了好多不重要的代碼抬纸,并且對每一段是干啥的加了注釋。

public ServiceMethod build() {
      //創(chuàng)建CallAdapter
      callAdapter = createCallAdapter();
      responseType = callAdapter.responseType();

      ...
      //創(chuàng)建返回結果轉換的Converter
      responseConverter = createResponseConverter();

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

      ...
      //解析參數(shù)上的注解
      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);
    }

看看創(chuàng)建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方法晓锻,發(fā)現(xiàn)它調用了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中依次進行遍歷,第一個符合條件的將被返回屑墨。
我們在創(chuàng)建Retrofit對象的時候可以向callAdapterFactories添加工廠躁锁,比如如果我們將RxJava和Retrofit聯(lián)合使用,想在interface方法中定義Observable作為返回類型卵史,就需要添加第三方庫中的RxJava2CallAdapterFactory战转。
Retrofit的build代碼中添加了一個默認的DefaultCallAdapterFactory,它響應的類型是Retrofit的Call接口以躯,所以如果我們不添加任何額外的CallAdapterFactory槐秧,那么interface方法中定義的返回類型只能為Call類型。

下面再來看創(chuàng)建返回結果轉換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方法刁标,跟進去,發(fā)現(xiàn)調用了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方法启搂,根據(jù)不同的注解類型傳入不同的參數(shù)。

    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等參數(shù)易核。

解析參數(shù)注解的部分匈织,和解析類注解的部分相似,會根據(jù)參數(shù)上的注解的不同牡直,生成不同的參數(shù)處理器保存到了parameterHandlers域中缀匕。

ServiceMethod的Builder的build方法主要做的事情就是這么多了,build方法結束之后碰逸,其構建的ServiceMethod對象中的域就都被賦值完成了乡小。

我們再回到之前的動態(tài)代理的方法,看看ServiceMethod構建完成之后饵史,構建的OkHttpCall是個啥满钟。

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

嗯胜榔,OkHttpCall實現(xiàn)了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返回了膀捷,什么都沒做。
所以我們在動態(tài)代理中返回的retrofit2.Call<JSONObject>的實際類型就是傳入的OkHttpCall削彬。
到這里担孔,動態(tài)代理的代碼就分析完了。

再來看看下一步吃警,執(zhí)行網絡請求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方法來創(chuàng)建一個okhttp3.Call拌消,然后調用該okhttp3.Call的enqueue方法。
繼續(xù)跟進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之前解析出來的參數(shù)安券,比如httpMethod, baseUrl, relativeUrl, headers等等墩崩,構建了一個RequestBuilder對象,又使用RequestBuilder對象build出了一個okhttp3.Request對象侯勉,進而又構建出一個okhttp3.Call對象鹦筹。這樣,最終就是調用創(chuàng)建的這個okhttp3.Call來進行網絡請求的址貌。

可以看到铐拐,Retrofit對我們寫在接口中的各種注解進行解析,得出請求參數(shù)练对,然后用這些參數(shù)構造出okhttp請求來請求網絡遍蟋。同時,它通過CallAdapter可以實現(xiàn)和RxJava的配合使用螟凭,通過Converter可以靈活地返回你需要的數(shù)據(jù)類型虚青。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市螺男,隨后出現(xiàn)的幾起案子棒厘,更是在濱河造成了極大的恐慌纵穿,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,542評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件奢人,死亡現(xiàn)場離奇詭異谓媒,居然都是意外死亡,警方通過查閱死者的電腦和手機达传,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評論 3 394
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來迫筑,“玉大人宪赶,你說我怎么就攤上這事「迹” “怎么了搂妻?”我有些...
    開封第一講書人閱讀 163,912評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長辕棚。 經常有香客問我欲主,道長,這世上最難降的妖魔是什么逝嚎? 我笑而不...
    開封第一講書人閱讀 58,449評論 1 293
  • 正文 為了忘掉前任扁瓢,我火速辦了婚禮,結果婚禮上补君,老公的妹妹穿的比我還像新娘引几。我一直安慰自己,他們只是感情好挽铁,可當我...
    茶點故事閱讀 67,500評論 6 392
  • 文/花漫 我一把揭開白布伟桅。 她就那樣靜靜地躺著,像睡著了一般叽掘。 火紅的嫁衣襯著肌膚如雪楣铁。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,370評論 1 302
  • 那天更扁,我揣著相機與錄音盖腕,去河邊找鬼。 笑死浓镜,一個胖子當著我的面吹牛赊堪,可吹牛的內容都是我干的。 我是一名探鬼主播竖哩,決...
    沈念sama閱讀 40,193評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼哭廉,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了相叁?” 一聲冷哼從身側響起遵绰,我...
    開封第一講書人閱讀 39,074評論 0 276
  • 序言:老撾萬榮一對情侶失蹤辽幌,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后椿访,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體乌企,經...
    沈念sama閱讀 45,505評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,722評論 3 335
  • 正文 我和宋清朗相戀三年成玫,在試婚紗的時候發(fā)現(xiàn)自己被綠了加酵。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,841評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡哭当,死狀恐怖猪腕,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情钦勘,我是刑警寧澤陋葡,帶...
    沈念sama閱讀 35,569評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站彻采,受9級特大地震影響腐缤,放射性物質發(fā)生泄漏。R本人自食惡果不足惜肛响,卻給世界環(huán)境...
    茶點故事閱讀 41,168評論 3 328
  • 文/蒙蒙 一岭粤、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧特笋,春花似錦绍在、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至霸奕,卻和暖如春溜宽,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背质帅。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評論 1 269
  • 我被黑心中介騙來泰國打工适揉, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人煤惩。 一個月前我還...
    沈念sama閱讀 47,962評論 2 370
  • 正文 我出身青樓嫉嘀,卻偏偏與公主長得像,于是被迫代替她去往敵國和親魄揉。 傳聞我的和親對象是個殘疾皇子剪侮,可洞房花燭夜當晚...
    茶點故事閱讀 44,781評論 2 354

推薦閱讀更多精彩內容