Retrofit源碼分析

關(guān)于Retrofit的使用這里就不再贅述了,還不太了解Retrofit使用的同學(xué)
Retrofit簡(jiǎn)介
今天我們來(lái)聊一聊Retrofit的內(nèi)部實(shí)現(xiàn)眼滤,通過(guò)源碼來(lái)分析Retrofit的整個(gè)執(zhí)行順序爹耗。

在沒(méi)有框架的時(shí)候爹谭,做一次網(wǎng)絡(luò)請(qǐng)求大概會(huì)經(jīng)過(guò)五個(gè)步奏:

  • 構(gòu)建request的參數(shù)
  • 手動(dòng)開啟子線程做http請(qǐng)求
  • 在子線程的run方法中具體請(qǐng)求
  • 拿到返回的response數(shù)據(jù)后介时,回調(diào)給上一層
  • 在主線程中更新UI

如果沒(méi)有網(wǎng)絡(luò)請(qǐng)求框架报咳,然后對(duì)多線程又不是太熟悉椎咧,就會(huì)出現(xiàn)很多問(wèn)題玖详。比如,請(qǐng)求是否異步勤讽,這里就會(huì)出現(xiàn)android中常見的錯(cuò)誤:Null pointer exception蟋座。
所以,在我們還沒(méi)有造輪子的能力的時(shí)候脚牍,多去看看優(yōu)秀框架的源碼是有好處的向臀,整個(gè)框架的搭建,運(yùn)用了哪些設(shè)計(jì)模式诸狭,容錯(cuò)處理等等券膀。君纫。

Retrofit 獲取實(shí)例

  • 首先通過(guò)構(gòu)建者模式去獲得Retrofit實(shí)例
 Retrofit retrofit = new Retrofit
                .Builder()
                .baseUrl(ApiContancts.BASE_URL)
                .addConverterFactory(GsonConverterFactory.create())
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                .build();
  • 我們來(lái)看看build()方法中具體做了什么
 public Retrofit build() {
      if (baseUrl == null) {
        throw new IllegalStateException("Base URL required.");
      }

      okhttp3.Call.Factory callFactory = this.callFactory;
      if (callFactory == null) {
        callFactory = new OkHttpClient();
      }

      Executor callbackExecutor = this.callbackExecutor;
      if (callbackExecutor == null) {
        callbackExecutor = platform.defaultCallbackExecutor();
      }

      // Make a defensive copy of the adapters and add the default Call adapter.
      List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories);
      adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));

      // Make a defensive copy of the converters.
      List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories);

      return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
          callbackExecutor, validateEagerly);
    }

不難看出build()方法中主要做了判空處理:
1.baseurl為空,拋出異常芹彬;
2.callFactory為空蓄髓,默認(rèn)使用OKhttp3的callFactory,這一點(diǎn)也可以看出Retrofit是基于okhttp3做的封裝舒帮。不會(huì)支持URLConnection和OkHttpClient了会喝。
3.callbackExecutor為空,會(huì)通過(guò)Platform這個(gè)類使用defaultCallbackExecutor

 static class Android extends Platform {
    @Override public Executor defaultCallbackExecutor() {
      return new MainThreadExecutor();
    }

    @Override CallAdapter.Factory defaultCallAdapterFactory(Executor callbackExecutor) {
      return new ExecutorCallAdapterFactory(callbackExecutor);
    }

    static class MainThreadExecutor implements Executor {
      private final Handler handler = new Handler(Looper.getMainLooper());

      @Override public void execute(Runnable r) {
        handler.post(r);
      }
    }
  }

跟蹤源碼會(huì)發(fā)現(xiàn)会前,最終會(huì)通過(guò)Handler去做操作好乐。
4.第一個(gè)List集合是存儲(chǔ)剛剛那個(gè)callbackExecutor匾竿,第二個(gè)List集合是存儲(chǔ)將Json轉(zhuǎn)換成實(shí)體類的一個(gè)工廠或者說(shuō)是適配器瓦宜。
5.最后返回一個(gè)Retrofit的實(shí)例。

Retrofit實(shí)例的create()方法

Call<CookListModel> cook = retrofit.create(ApiService.class).getCook(1,20);

通過(guò)調(diào)用create()方法

 public <T> T create(final Class<T> service) {
    Utils.validateServiceInterface(service);
    if (validateEagerly) {
      eagerlyValidateMethods(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, 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);
            }
            if (platform.isDefaultMethod(method)) {
              return platform.invokeDefaultMethod(method, service, proxy, args);
            }
            ServiceMethod<Object, Object> serviceMethod =
                (ServiceMethod<Object, Object>) loadServiceMethod(method);
            OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
            return serviceMethod.callAdapter.adapt(okHttpCall);
          }
        });
  }

其實(shí)我看到這里也很懵逼岭妖,我們的Http請(qǐng)求方法临庇、參數(shù)是通過(guò)注解的方式創(chuàng)建的,它是怎么轉(zhuǎn)換成實(shí)體對(duì)象的昵慌?
不急假夺,我們一步一步的看下去

ServiceMethod<?, ?> loadServiceMethod(Method method) {
    ServiceMethod<?, ?> result = serviceMethodCache.get(method);
    if (result != null) return result;

    synchronized (serviceMethodCache) {
      result = serviceMethodCache.get(method);
      if (result == null) {
        result = new ServiceMethod.Builder<>(this, method).build();
        serviceMethodCache.put(method, result);
      }
    }
    return result;
  }

這里也用了構(gòu)建者模式,build()方法中調(diào)用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("HEAD", ((HEAD) annotation).value(), false);
        if (!Void.class.equals(responseType)) {
          throw methodError("HEAD method must use Void as response type.");
        }
      } else if (annotation instanceof PATCH) {
        parseHttpMethodAndPath("PATCH", ((PATCH) annotation).value(), true);
      } else if (annotation instanceof POST) {
        parseHttpMethodAndPath("POST", ((POST) annotation).value(), true);
      } else if (annotation instanceof PUT) {
        parseHttpMethodAndPath("PUT", ((PUT) annotation).value(), true);
      } else if (annotation instanceof OPTIONS) {
        parseHttpMethodAndPath("OPTIONS", ((OPTIONS) annotation).value(), false);
      } else if (annotation instanceof HTTP) {
        HTTP http = (HTTP) annotation;
        parseHttpMethodAndPath(http.method(), http.path(), http.hasBody());
      } else if (annotation instanceof retrofit2.http.Headers) {
        String[] headersToParse = ((retrofit2.http.Headers) annotation).value();
        if (headersToParse.length == 0) {
          throw methodError("@Headers annotation is empty.");
        }
        headers = parseHeaders(headersToParse);
      } else if (annotation instanceof Multipart) {
        if (isFormEncoded) {
          throw methodError("Only one encoding annotation is allowed.");
        }
        isMultipart = true;
      } else if (annotation instanceof FormUrlEncoded) {
        if (isMultipart) {
          throw methodError("Only one encoding annotation is allowed.");
        }
        isFormEncoded = true;
      }
    }

那么斋攀,Retrofit到底是怎么把我們定義的接口轉(zhuǎn)換成http請(qǐng)求的呢已卷?
我們來(lái)看看ServiceMethod這個(gè)類。

Adapts an invocation of an interface method into an HTTP call.

ServiceMethod這個(gè)類相當(dāng)于一個(gè)適配器淳蔼,將interface轉(zhuǎn)換成http的請(qǐng)求侧蘸,再通過(guò)OkHttp去做網(wǎng)絡(luò)請(qǐng)求。
所以說(shuō)鹉梨,真正的關(guān)鍵代碼是:

ServiceMethod<Object, Object> serviceMethod =
                (ServiceMethod<Object, Object>) loadServiceMethod(method);
            OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);

Retrofit內(nèi)部大致的執(zhí)行流程就是這樣讳癌,如果想要更深入的去研究Retrofit的話,建議大家利用debug一步一步的跟蹤源碼分析存皂,這樣的話會(huì)更加容易理解一點(diǎn)晌坤。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市旦袋,隨后出現(xiàn)的幾起案子骤菠,更是在濱河造成了極大的恐慌,老刑警劉巖疤孕,帶你破解...
    沈念sama閱讀 216,496評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件娩怎,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡胰柑,警方通過(guò)查閱死者的電腦和手機(jī)截亦,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門爬泥,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人崩瓤,你說(shuō)我怎么就攤上這事袍啡。” “怎么了却桶?”我有些...
    開封第一講書人閱讀 162,632評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵境输,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我颖系,道長(zhǎng)嗅剖,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,180評(píng)論 1 292
  • 正文 為了忘掉前任嘁扼,我火速辦了婚禮信粮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘趁啸。我一直安慰自己强缘,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,198評(píng)論 6 388
  • 文/花漫 我一把揭開白布不傅。 她就那樣靜靜地躺著旅掂,像睡著了一般。 火紅的嫁衣襯著肌膚如雪访娶。 梳的紋絲不亂的頭發(fā)上商虐,一...
    開封第一講書人閱讀 51,165評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音崖疤,去河邊找鬼秘车。 笑死,一個(gè)胖子當(dāng)著我的面吹牛戳晌,可吹牛的內(nèi)容都是我干的鲫尊。 我是一名探鬼主播,決...
    沈念sama閱讀 40,052評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼沦偎,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼疫向!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起豪嚎,我...
    開封第一講書人閱讀 38,910評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤搔驼,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后侈询,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體舌涨,經(jīng)...
    沈念sama閱讀 45,324評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,542評(píng)論 2 332
  • 正文 我和宋清朗相戀三年扔字,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了囊嘉。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片温技。...
    茶點(diǎn)故事閱讀 39,711評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖扭粱,靈堂內(nèi)的尸體忽然破棺而出舵鳞,到底是詐尸還是另有隱情,我是刑警寧澤琢蛤,帶...
    沈念sama閱讀 35,424評(píng)論 5 343
  • 正文 年R本政府宣布蜓堕,位于F島的核電站,受9級(jí)特大地震影響博其,放射性物質(zhì)發(fā)生泄漏套才。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,017評(píng)論 3 326
  • 文/蒙蒙 一慕淡、第九天 我趴在偏房一處隱蔽的房頂上張望背伴。 院中可真熱鬧,春花似錦儡率、人聲如沸挂据。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至掷倔,卻和暖如春眉孩,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背勒葱。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工浪汪, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人凛虽。 一個(gè)月前我還...
    沈念sama閱讀 47,722評(píng)論 2 368
  • 正文 我出身青樓死遭,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親凯旋。 傳聞我的和親對(duì)象是個(gè)殘疾皇子呀潭,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,611評(píng)論 2 353

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

  • 最近非常流行 Retrofit+RxJava+OkHttp 這一整套的網(wǎng)絡(luò)請(qǐng)求和異步操作的開源框架,從 Jake ...
    慌不要慌閱讀 1,972評(píng)論 1 7
  • 簡(jiǎn)介 剛接觸Retrofit的時(shí)候至非,就寫了一篇簡(jiǎn)單的使用介紹:Retrofit 2.0基本使用方法,算是對(duì)Retr...
    Whyn閱讀 2,844評(píng)論 4 24
  • 前言 使用Retrofit已經(jīng)一段時(shí)間了钠署,這貨挺好用的,還很特別荒椭,特別是使用接口來(lái)定義請(qǐng)求方式谐鼎,這用法讓我對(duì)它的源...
    帶心情去旅行閱讀 3,358評(píng)論 3 21
  • 前言 在上一周學(xué)習(xí)了一下 Retrofit 的執(zhí)行流程。接下來(lái)的文章要更為深入地學(xué)習(xí) Retrofit 的各個(gè)類趣惠,...
    野生西瓜閱讀 1,669評(píng)論 0 10
  • 中午與幾位好友吃飯狸棍,閑聊聊到吃水果這個(gè)事身害。朋友的兒子中午飯吃的不多,他媽媽說(shuō)他回家還會(huì)吃水果草戈,什么榴蓮题造、橙子之類,...
    望遠(yuǎn)登高閱讀 383評(píng)論 0 0