Retrofit 中關(guān)于Convert模塊的源碼解析

0. 前言

Retrofit中關(guān)于 [ 數(shù)據(jù)結(jié)構(gòu) <----> Model ] 之間的相互轉(zhuǎn)換包括兩個方面:
即我們進行源碼分析的兩個突破口

  • 請求, 即 通過@Body注解添加的自定義 Model --> RequestBody
  • 響應, 即 ResponseBody --> 自定義Model

這里的數(shù)據(jù)結(jié)構(gòu)包含常用的json, xml , 最關(guān)鍵的是以上兩個操作都是自動完成的, diao的不行, 根本不需要我們手動參與 自己進行解析轉(zhuǎn)換;
那到底是如何做到的呢? 本文以json為例進行詳細分析~

以上兩個方面對應的http request和response 中的header 中的
content-type都為 application/json

1. 用法

Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(BuildConfig.HOST)
                .client(httpClient)
                .addConverterFactory(GsonConverterFactory.create())
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                .build();

2. Convert 的簡要說明

關(guān)于json轉(zhuǎn)換部分 具體是通過addConverterFactory()傳入我們需要的ConverFactory;
也就是說, 最終通過ConverterFactory創(chuàng)建convert 完成從responseBody-> Model的轉(zhuǎn)換; 以下是Converter 接口以及Converter.Factory的源碼

public interface Converter<F, T> {
  T convert(F value) throws IOException;

  abstract class Factory {
 
    public @Nullable Converter<ResponseBody, ?> responseBodyConverter(Type type,
        Annotation[] annotations, Retrofit retrofit) {
      return null;
    }

    public @Nullable Converter<?, RequestBody> requestBodyConverter(Type type,
        Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
      return null;
    }

    public @Nullable Converter<?, String> stringConverter(Type type, Annotation[] annotations,
        Retrofit retrofit) {
      return null;
    }

    protected static Type getParameterUpperBound(int index, ParameterizedType type) {
      return Utils.getParameterUpperBound(index, type);
    }

    protected static Class<?> getRawType(Type type) {
      return Utils.getRawType(type);
    }
  }
}

其實看了以上convert接口的源碼就明確了本文的最終目的, 就是convert.convert()方法具體是在哪調(diào)用的???

關(guān)于Convert的具體實現(xiàn), 如下有很多, 支持市面上大部分開源庫:


image.png

我們這里用GsonConverterFactory.

3.源碼分析

(非核心代碼用...忽略)

3.1 Retrofit 核心代碼

Retrofit.java中最終用來創(chuàng)建service的create方法

  public <T> T create(final Class<T> service) {
    ...
    //通過動態(tài)代理創(chuàng)建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 {
            ...
            //1. 創(chuàng)建并緩存ServiceMethod ,這個對象用來解析并包裝我們在service中自定義的各種方法
            ServiceMethod<Object, Object> serviceMethod = (ServiceMethod<Object, Object>) loadServiceMethod(method);
            //2. call 的包裝類
            OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
            //3. 用我們傳入的callAdapter 進行方法返回值的轉(zhuǎn)換
            return serviceMethod.callAdapter.adapt(okHttpCall);
          }
        });
  }

這里使用了動態(tài)代理 創(chuàng)建了service接口的代理對象, 因此每次調(diào)用service對象中的方法, 本質(zhì)就是執(zhí)行 invocationHandler.invoke() 方法 (關(guān)于動態(tài)代理的原理這暫且不詳談);
因此每個方法執(zhí)行的核心代碼就上面123 三句話;

而關(guān)于ResponseBody/RequestBody <---> Model的轉(zhuǎn)換就在OkHttpCallexecute()中;

@Override public Response<T> execute() throws IOException {
    okhttp3.Call call;
    ...
    synchronized (this) {
      call = rawCall;
      if (call == null) {
          //注釋1. request的創(chuàng)建從這里開始
          call = rawCall = createRawCall();
      }
    }
    ...
    //注釋2. response的創(chuàng)建在這里
    return parseResponse(call.execute());
  }

3.2 Model --> RequestBody的轉(zhuǎn)換

okHttpCall.execute()方法中注釋1reateRawCall();創(chuàng)建call

  private okhttp3.Call createRawCall() throws IOException {
    //核心代碼
    Request request = serviceMethod.toRequest(args);
    //這里的callFactory取得就是retrofit中的callFactory, 即httpClient
    okhttp3.Call call = serviceMethod.callFactory.newCall(request);
    ...
    return call;
  }

以上核心代碼便是ServiceMethod中關(guān)于request的創(chuàng)建,

/** Builds an HTTP request from method arguments. */
  Request toRequest(@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;

    int argumentCount = args != null ? args.length : 0;
    ...

    //在這里, 遍歷每個ParameterHandler對 service的方法參數(shù)進行處理
    for (int p = 0; p < argumentCount; p++) {
      handlers[p].apply(requestBuilder, args[p]);
    }

    return requestBuilder.build();
  }

既然我們用的是@Body注解, 那么直接看Body 這個ParameterHandler的實現(xiàn)

static final class Body<T> extends ParameterHandler<T> {
    private final Converter<T, RequestBody> converter;

    Body(Converter<T, RequestBody> converter) {
      this.converter = converter;
    }

    @Override void apply(RequestBuilder builder, @Nullable T value) {
      ...
      RequestBody body;
      //找到了
      body = converter.convert(value);

      builder.setBody(body);
    }
  }

那這個converter又是哪里來的? 其實就是最初我們構(gòu)建retrofit的時候, 通過addConverterFactory()傳進去的GsonConverterFactory創(chuàng)建的GsonRequestBodyConverter;
以上~
至于ParameterHandler的數(shù)組又是如何初始化的, 各種注解怎么解析的, 都在ServiceMethod這個類里, 慢慢欣賞~
這里只貼一下解析@Body的源碼

else if (annotation instanceof Body) {
        if (isFormEncoded || isMultipart) {
          throw parameterError(p,
              "@Body parameters cannot be used with form or multi-part encoding.");
        }
        if (gotBody) {
          throw parameterError(p, "Multiple @Body method annotations found.");
        }

        Converter<?, RequestBody> converter;
        try {
          //看, 偉大的requestBodyConverter
          converter = retrofit.requestBodyConverter(type, annotations, methodAnnotations);
        } catch (RuntimeException e) {
          // Wide exception range because factories are user code.
          throw parameterError(e, p, "Unable to create @Body converter for %s", type);
        }
        gotBody = true;
        return new ParameterHandler.Body<>(converter);
  }

3.3 ResponseBody --> Model的轉(zhuǎn)換

okHttpCall.execute()方法中的最后一句parseResponse(Response rawResp)創(chuàng)建了Rerofit2.Response<T>,
看到這個泛型 T 了吧, 其實里面封裝了T body變量, 也就是我們最終拿到的model
(注意區(qū)別于okhttp中的Response)

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();
    ...

    ExceptionCatchingRequestBody catchingBody = new ExceptionCatchingRequestBody(rawBody);
    try {
      //核心代碼
      T body = serviceMethod.toResponse(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;
    }
  }

下面來看T body = serviceMethod.toResponse(catchingBody);的實現(xiàn)

  /** Builds a method return value from an HTTP response body. */
  R toResponse(ResponseBody body) throws IOException {
    return responseConverter.convert(body);
  }

那上面這個responseConverter又是怎么來的呢? 具體彎彎繞繞就不看了, 其實就是我們在創(chuàng)建retrofit的時候,通過addConverterFactory()傳進去的GsonConverterFactory創(chuàng)建的GsonResponseBodyConverter;
下面來看直接來看GsonResponseBodyConverter

final class GsonResponseBodyConverter<T> implements Converter<ResponseBody, T> {
  private final Gson gson;
  private final TypeAdapter<T> adapter;

  GsonResponseBodyConverter(Gson gson, TypeAdapter<T> adapter) {
    this.gson = gson;
    this.adapter = adapter;
  }

  @Override public T convert(ResponseBody value) throws IOException {
    JsonReader jsonReader = gson.newJsonReader(value.charStream());
    try {
      return adapter.read(jsonReader);
    } finally {
      value.close();
    }
  }
}

如此, T 也就是我們自定義的Model最終轉(zhuǎn)換完成;
至于okHttpCall.execute()又是在何時被誰調(diào)用的在這里就不繼續(xù)深究了, 因為不同的callAdapter有不同的實現(xiàn), 已超出本文探究范圍, 要具體探究的話可以看invoke()中的第3句核心代碼serviceMethod.callAdapter.adapt(okHttpCall);
ps. RxJava的相關(guān)實現(xiàn)在RxJavaCallAdapter

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末倾芝,一起剝皮案震驚了整個濱河市赠群,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖冰单,帶你破解...
    沈念sama閱讀 218,284評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件纹腌,死亡現(xiàn)場離奇詭異霎终,居然都是意外死亡,警方通過查閱死者的電腦和手機升薯,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評論 3 395
  • 文/潘曉璐 我一進店門莱褒,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人涎劈,你說我怎么就攤上這事广凸。” “怎么了蛛枚?”我有些...
    開封第一講書人閱讀 164,614評論 0 354
  • 文/不壞的土叔 我叫張陵谅海,是天一觀的道長。 經(jīng)常有香客問我蹦浦,道長扭吁,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,671評論 1 293
  • 正文 為了忘掉前任盲镶,我火速辦了婚禮侥袜,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘溉贿。我一直安慰自己枫吧,他們只是感情好,可當我...
    茶點故事閱讀 67,699評論 6 392
  • 文/花漫 我一把揭開白布宇色。 她就那樣靜靜地躺著九杂,像睡著了一般闽寡。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上尼酿,一...
    開封第一講書人閱讀 51,562評論 1 305
  • 那天爷狈,我揣著相機與錄音,去河邊找鬼裳擎。 笑死涎永,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的鹿响。 我是一名探鬼主播羡微,決...
    沈念sama閱讀 40,309評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼惶我!你這毒婦竟也來了妈倔?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,223評論 0 276
  • 序言:老撾萬榮一對情侶失蹤绸贡,失蹤者是張志新(化名)和其女友劉穎盯蝴,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體听怕,經(jīng)...
    沈念sama閱讀 45,668評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡捧挺,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,859評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了尿瞭。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片闽烙。...
    茶點故事閱讀 39,981評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖声搁,靈堂內(nèi)的尸體忽然破棺而出黑竞,到底是詐尸還是另有隱情,我是刑警寧澤疏旨,帶...
    沈念sama閱讀 35,705評論 5 347
  • 正文 年R本政府宣布很魂,位于F島的核電站,受9級特大地震影響充石,放射性物質(zhì)發(fā)生泄漏莫换。R本人自食惡果不足惜霞玄,卻給世界環(huán)境...
    茶點故事閱讀 41,310評論 3 330
  • 文/蒙蒙 一骤铃、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧坷剧,春花似錦惰爬、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,904評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽陵叽。三九已至,卻和暖如春丛版,著一層夾襖步出監(jiān)牢的瞬間巩掺,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,023評論 1 270
  • 我被黑心中介騙來泰國打工页畦, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留胖替,地道東北人。 一個月前我還...
    沈念sama閱讀 48,146評論 3 370
  • 正文 我出身青樓豫缨,卻偏偏與公主長得像独令,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子好芭,可洞房花燭夜當晚...
    茶點故事閱讀 44,933評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 適配器模式上一篇文章我們已經(jīng)分析了Retrofit解析注解封裝進ServiceMethod的流程燃箭,讀者在這里要記住...
    andcoder閱讀 657評論 0 2
  • 簡介 剛接觸Retrofit的時候,就寫了一篇簡單的使用介紹:Retrofit 2.0基本使用方法,算是對Retr...
    Whyn閱讀 2,844評論 4 24
  • 整體Retrofit內(nèi)容如下: 1舍败、Retrofit解析1之前哨站——理解RESTful 2招狸、Retrofit解析...
    隔壁老李頭閱讀 3,957評論 1 3
  • 最近非常流行 Retrofit+RxJava+OkHttp 這一整套的網(wǎng)絡請求和異步操作的開源框架,從 Jake ...
    慌不要慌閱讀 1,974評論 1 7
  • 博文出處:Retrofit源碼解析邻薯,歡迎大家關(guān)注我的博客瓢颅,謝謝! Header 之前對 OkHttp 進行過源碼分...
    俞其榮閱讀 609評論 0 6