Retrofit是如何工作的?

注:本文基于 Retrofit2.0版本,并配合 RxJava 來分析。
com.squareup.retrofit2:retrofit:2.0.0
com.squareup.retrofit2:converter-gson:2.0.0
com.squareup.retrofit2:adapter-rxjava:2.0.0

?

? Retrofit adapts a Java interface to HTTP calls by using annotations on the declared methods to how requests are made.

本文主要通過分析 Retrofit 與 RxJava 的合作流程 來深入理解 Retrofit的工作原理,并且解答自己心中的疑惑。

疑惑

  1. 我們調(diào)用接口的方法后是怎么發(fā)送請(qǐng)求的?這背后發(fā)生了什么?
  2. Retrofit 與 OkHttp 是怎么合作的?
  3. Retrofit 中的數(shù)據(jù)究竟是怎么處理的?它是怎么返回 RxJava.Observable 的?

Retrofit 的基本使用

public interface ApiService{
  @GET("data/Android/"+ GankConfig.PAGE_COUNT+"/{page}")
    Observable<GankResponse> getAndroid(@Path("page") int page);
}

// Builder 模式來構(gòu)建 retrofit
Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(baseUrl)
                .addConverterFactory(GsonConverterFactory.create(new GsonBuilder().create()))
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                .client(okHttpClient)
                .build();
// 通過 retrofit.create 方法來生成 service(非四大組件中的 Service)
ApiService service = retrofit.create(ApiService.class);
// 發(fā)起請(qǐng)求 獲取數(shù)據(jù)
Observable<GankResponse> observable= service.getAndroid(1);
observable....

Retrofit 就這樣經(jīng)過簡(jiǎn)單的配置后就可以向服務(wù)器請(qǐng)求數(shù)據(jù)了,超級(jí)簡(jiǎn)單。

Retrofit.create 方法分析

Retrofit的create方法作為 Retrofit 的入口,當(dāng)然得第一個(gè)分析。

  public <T> T create(final Class<T> service) {
    //驗(yàn)證接口是否合理
    Utils.validateServiceInterface(service);
    //默認(rèn) false
    if (validateEagerly) {
      eagerlyValidateMethods(service);
    }
    // 動(dòng)態(tài)代理
    return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
        new InvocationHandler() {
          // 平臺(tái)的抽象,指定默認(rèn)的 CallbackExecutor CallAdapterFactory用,  這里 Android 平臺(tái)是 Android (Java8 iOS 咱不管)
          private final Platform platform = Platform.get();
          //ApiService 中的方法調(diào)用都會(huì)走到這里
          @Override public Object invoke(Object proxy, Method method, Object... args)
              throws Throwable {
            // If the method is a method from Object then defer to normal invocation.
            // 注釋已經(jīng)說明 Object 的方法不管
            if (method.getDeclaringClass() == Object.class) {
              return method.invoke(this, args);
            }
            // java8 的默認(rèn)方法,Android暫不支持默認(rèn)方法,所以暫時(shí)也不需要管
            if (platform.isDefaultMethod(method)) {
              return platform.invokeDefaultMethod(method, service, proxy, args);
            }
            // 重點(diǎn)了 后面分析
            // 為 Method 生成一個(gè) ServiceMethod
            ServiceMethod serviceMethod = loadServiceMethod(method);
            // 再包裝成 OkHttpCall
            OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);      // 請(qǐng)求
            return serviceMethod.callAdapter.adapt(okHttpCall);
          }
        });
  }

在上面的代碼中可以看到,Retrofit 的主要原理是利用了 Java 的動(dòng)態(tài)代理技術(shù),把 ApiService 的方法調(diào)用集中到了InvocationHandler.invoke,再構(gòu)建了ServiceMethod ,OKHttpCall,返回 callAdapter.adapt 的結(jié)果。

要弄清楚,還需要分析那最后三行代碼。

一步一步來。

ServiceMethod的職責(zé)以及 loadServiceMethod分析

我認(rèn)為 ServiceMethod 是接口具體方法的抽象,它主要負(fù)責(zé)解析它對(duì)應(yīng)的 method 的各種參數(shù)(它有各種如 parseHeaders 的方法),比如注解(@Get),入?yún)?,另外還負(fù)責(zé)獲取 callAdapter,responseConverter等Retrofit配置,好為后面的okhttp3.Request做好參數(shù)準(zhǔn)備,它的toRequest為 OkHttp 提供 Request,可以說它承載了后續(xù) Http 請(qǐng)求所需的一切參數(shù)。

再分析loadServiceMethod,比較簡(jiǎn)單。

// serviceMethodCache 的定義
private final Map<Method, ServiceMethod> serviceMethodCache = new LinkedHashMap<>();
 // 獲取method對(duì)應(yīng)的 ServiceMethod
 ServiceMethod loadServiceMethod(Method method) {
    ServiceMethod result;
    synchronized (serviceMethodCache) {
      // 先從緩存去獲取
      result = serviceMethodCache.get(method);
      if (result == null) {
        //緩存中沒有 則新建,并存入緩存
        result = new ServiceMethod.Builder(this, method).build();
        serviceMethodCache.put(method, result);
      }
    }
    return result;
  }

loadServiceMethod方法,負(fù)責(zé) 為 method 生成一個(gè) ServiceMethod ,并且給 ServiceMethod 做了緩存。

動(dòng)態(tài)代理是有一定的性能損耗的,并且ServiceMethod 的創(chuàng)建伴隨著各種注解參數(shù)解析,這也是耗時(shí)間的,在加上一個(gè) App 調(diào)用接口是非常頻繁的,如果每次接口請(qǐng)求都需要重新生成那么有浪費(fèi)資源損害性能的可能,所以這里做了一份緩存來提高效率。

OkHttpCall

再接下去往后看OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);,是再為 ServiceMethod 以及 args(參數(shù))生成了一個(gè) OkHttpCall

OkHttpCall 這個(gè)名字來看就能猜到,它是對(duì) OkHttp3.Call 的組合包裝,事實(shí)上,它也確實(shí)是。(OkHttpCall中有一個(gè)成員okhttp3.Call rawCall)。

callAdapter.adapt流程分析

最后return serviceMethod.callAdapter.adapt(okHttpCall) 似乎是走到了最后一步。

如果說前面的都是準(zhǔn)備的話,那么到這里就是真的要行動(dòng)了。

來分析一下,這里涉及到的 callAdapter,是由我們配置 Retrofit 的 addCallAdapterFactory方法中傳入的RxJavaCallAdapterFactory.create()生成,實(shí)例為RxJavaCallAdapterFactory。

實(shí)例的生成大致流程為:

ServiceMethod.Bulider.Build()

->ServiceMethod.createCallAdapter()

->retrofit.callAdapter()

->adapterFactories遍歷

? ->最終到RxJavaCallAdapterFactory.get()#getCallAdapter()

? ->return return new SimpleCallAdapter(observableType, scheduler);

由于使用了 RxJava ,我們最終得到的 callAdapterSimpleCallAdapter,所以接下去分析SimpleCallAdapteradapt 方法:

這里涉及到的 CallOnSubscriber 后面有給出:

    @Override public <R> Observable<R> adapt(Call<R> call) {
      // 這里的 call 是 OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args) 生成的 okHttpCall
      Observable<R> observable = Observable.create(new CallOnSubscribe<>(call)) //
          .flatMap(new Func1<Response<R>, Observable<R>>() {
            @Override public Observable<R> call(Response<R> response) {
              if (response.isSuccessful()) {
                return Observable.just(response.body());
              }
              return Observable.error(new HttpException(response));
            }
          });
      if (scheduler != null) {
        return observable.subscribeOn(scheduler);
      }
      return observable;
    }
  }
  static final class CallOnSubscribe<T> implements Observable.OnSubscribe<Response<T>> {
    private final Call<T> originalCall;

    CallOnSubscribe(Call<T> originalCall) {
      this.originalCall = originalCall;
    }

    @Override public void call(final Subscriber<? super Response<T>> subscriber) {
      // Since Call is a one-shot type, clone it for each new subscriber.
      final Call<T> call = originalCall.clone();
      // Attempt to cancel the call if it is still in-flight on unsubscription.
      // 當(dāng)我們?nèi)∠嗛喌臅r(shí)候 會(huì)取消請(qǐng)求 棒棒噠
      subscriber.add(Subscriptions.create(new Action0() {
        @Override public void call() {
          call.cancel();
        }
      }));

      try {
        // call 是 OkHttpCall 的實(shí)例
        Response<T> response = call.execute();
        if (!subscriber.isUnsubscribed()) {
          subscriber.onNext(response);
        }
      } catch (Throwable t) {
        Exceptions.throwIfFatal(t);
        if (!subscriber.isUnsubscribed()) {
          subscriber.onError(t);
        }
        return;
      }

      if (!subscriber.isUnsubscribed()) {
        subscriber.onCompleted();
      }
    }
  }

SimpleCallAdapter.adapt 很簡(jiǎn)單,創(chuàng)建一個(gè) Observable獲取CallOnSubscribe中的Response<T> 通過 flatMap轉(zhuǎn)成Observable<R>后返回。這里去發(fā)送請(qǐng)求獲取數(shù)據(jù)的任務(wù)在CallOnSubscribe.call 方法之中。并且最后走到了 okHttpCall.execute 中去了。

  // OkHttpCall.execute 
  
  @Override public Response<T> execute() throws IOException {
    okhttp3.Call call;

    synchronized (this) {
      //同一個(gè)請(qǐng)求 不能執(zhí)行兩次
      if (executed) throw new IllegalStateException("Already executed.");
      executed = true;
     // ...省略 Execption 處理
     
      call = rawCall;
      if (call == null) {
        try {
          // 創(chuàng)建 okhttp3.call 
          call = rawCall = createRawCall();
        } catch (IOException | RuntimeException e) {
          creationFailure = e;
          throw e;
        }
      }
    }
    if (canceled) {
      call.cancel();
    }
    // 請(qǐng)求并解析response 這個(gè) call 是 okhttp3.call 是真交給 OkHttp 去發(fā)送請(qǐng)求了 
    return parseResponse(call.execute());
  }

// 解析 response
  Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
    //... 省略一些處理 只顯示關(guān)鍵代碼
    try {
      T body = serviceMethod.toResponse(catchingBody);
      return Response.success(body, rawResponse);
    } catch (RuntimeException e) {
      catchingBody.throwIfCaught();
      throw e;
    }
  }

// serviceMethod.toResponse
  T toResponse(ResponseBody body) throws IOException {
    // 還記得嗎?這就是我們配置Retrofit時(shí)候的 converter
    return responseConverter.convert(body);
  }

經(jīng)過一連串的處理,最終在 OkHttpCall.execute() 的方法中生成 okhttp3.call 交給 OkHttpClient 去發(fā)送請(qǐng)求,再由我們配置的 Converter(本文為GsonConverterFactory) 處理 Response,返回給SimpleCallAdapter處理,返回我們最終所需要的Observable。

流程分析流程圖總結(jié)

總體的流程圖整理如下:

解答疑問

對(duì)于之前的疑問可以作答了。

第一個(gè)疑問: 我們調(diào)用接口的方法后是怎么發(fā)送請(qǐng)求的?這背后發(fā)生了什么?

Retrofit 使用了動(dòng)態(tài)代理給我們定義的接口設(shè)置了代理,當(dāng)我們調(diào)用接口的方法時(shí),Retrofit 會(huì)攔截下來,然后經(jīng)過一系列處理,比如解析方法的注解等,生成了 Call Request 等OKHttp所需的資源,最后交給 OkHttp 去發(fā)送請(qǐng)求, 此間經(jīng)過 callAdapter,convertr 的處理,最后拿到我們所需要的數(shù)據(jù)。

第二個(gè)疑問: Retrofit 與 OkHttp 是怎么合作的?

在Retrofit 中,ServiceMethod 承載了一個(gè) Http 請(qǐng)求的所有參數(shù),OkHttpCall 為 okhttp3.call 的組合包裝,由它們倆合作,生成用于 OkHttp所需的 Request以及okhttp3.Call,交給 OkHttp 去發(fā)送請(qǐng)求。(在本文環(huán)境下具體用的是 call.execute())

可以說 Retrofit 為 OkHttp 再封裝了一層,并增添了不少功能以及擴(kuò)展,減少了開發(fā)使用成本。

第三個(gè)疑問: Retrofit 中的數(shù)據(jù)究竟是怎么處理的?它是怎么返回 RxJava.Observable 的?

Retrofit 中的數(shù)據(jù)其實(shí)是交給了 callAdapter 以及 converter 去處理,callAdapter 負(fù)責(zé)把 okHttpCall 轉(zhuǎn)成我們所需的 Observable類型(本文環(huán)境),converter負(fù)責(zé)把服務(wù)器返回的數(shù)據(jù)轉(zhuǎn)成具體的實(shí)體類。

小結(jié)

Retrofit 的源碼其實(shí)非常好跟也非常好理解,不像看 framework 的代碼,跟著跟著就不見了。

另外 Retrofit的代碼確實(shí)非常漂亮,將設(shè)計(jì)模式運(yùn)用的可以說是爐火純青,非常值得學(xué)習(xí)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,837評(píng)論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,196評(píng)論 3 414
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,688評(píng)論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,654評(píng)論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,456評(píng)論 6 406
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 54,955評(píng)論 1 321
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,044評(píng)論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,195評(píng)論 0 287
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,725評(píng)論 1 333
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,608評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,802評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,318評(píng)論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,048評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,422評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,673評(píng)論 1 281
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,424評(píng)論 3 390
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,762評(píng)論 2 372

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