Retrofit源碼筆記

Retrofit是一款java平臺的http client工具墩虹,常用于Android砌们。主要基于OkHttp做應(yīng)用層封裝,把http域名轉(zhuǎn)換成java方法,可以自動轉(zhuǎn)換json結(jié)果為javabean寞宫。github鏈接官方教程

Retrofit turns your HTTP API into a Java interface.

簡單使用流程介紹:

  1. 先構(gòu)建okhttpclient
  2. Builder模式構(gòu)建Retrofit
  3. 編寫interface捧灰,通過注解寫域名淆九,把http請求轉(zhuǎn)化為java方法
  4. Retrofit#createService創(chuàng)建實例,返回一個Call
  5. call.enqueue(callback)
  6. callback接收java bean結(jié)果

構(gòu)建OkHttpClient

  • 配置cache
  • 配置攔截器
    主要就是上面的配置

構(gòu)建Retrofit

Builder

  • callFactory(okhttp3.Call.Factory) 傳入一個call毛俏,默認(rèn)是Okhttpclient
  interface Factory {
    Call newCall(Request request);
  }
  • baseurl 傳入基準(zhǔn)域名
  • addConvertFactory 序列化工廠
public interface Converter<F, T> {
  T convert(F value) throws IOException;

  /** Creates {@link Converter} instances based on a type and target usage. */
  abstract class Factory {
    //ResponseBody converter的工廠炭庙,這個converter負(fù)責(zé)ReponseBody和bean的轉(zhuǎn)換
    public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
        Retrofit retrofit) {
      return null;
    }
    //RequestBody converter的工廠,這個converter用于RequestBody和bean的轉(zhuǎn)換煌寇,一般是@Body焕蹄,@Part,@PartMap
    public Converter<?, RequestBody> requestBodyConverter(Type type,
        Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
      return null;
    }

    //負(fù)責(zé)string和bean的轉(zhuǎn)換阀溶,一般是@Field腻脏,@Header鸦泳,@Path,@Query以及它們對應(yīng)的map注解
    public Converter<?, String> stringConverter(Type type, Annotation[] annotations,
        Retrofit retrofit) {
      return null;
    }
  }
}

converterFactory需要顯式指定永品,使用fastjson或者gson對應(yīng)的converter即可做鹰,具體可參考Retrofit的wiki

  • addCallAdapterFactory 主要是將Retrofit的Call<T>轉(zhuǎn)為其他方式,如RxJava鼎姐,默認(rèn)是ExecutorCallAdapterFactory
public interface CallAdapter<R, T> {
  Type responseType();

 T adapt(Call<R> call);

  abstract class Factory {
    
    public abstract CallAdapter<?, ?> get(Type returnType, Annotation[] annotations,
        Retrofit retrofit);

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

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


//Call如何在Observable運行
  protected void subscribeActual(Observer<? super Response<T>> observer) {
    // Since Call is a one-shot type, clone it for each new observer.
    Call<T> call = originalCall.clone();
    CallDisposable disposable = new CallDisposable(call);
    observer.onSubscribe(disposable);
    if (disposable.isDisposed()) {
      return;
    }

    boolean terminated = false;
    try {
      Response<T> response = call.execute();
      if (!disposable.isDisposed()) {
        observer.onNext(response);
      }
      if (!disposable.isDisposed()) {
        terminated = true;
        observer.onComplete();
      }
    } catch (Throwable t) {
      Exceptions.throwIfFatal(t);
      if (terminated) {
        RxJavaPlugins.onError(t);
      } else if (!disposable.isDisposed()) {
        try {
          observer.onError(t);
        } catch (Throwable inner) {
          Exceptions.throwIfFatal(inner);
          RxJavaPlugins.onError(new CompositeException(t, inner));
        }
      }
    }
  }

  • callbackExecutor 傳入一個Executor钾麸,用于回調(diào)執(zhí)行

創(chuàng)建請求interface

  • http方法注解,用于標(biāo)識請求的方法 GET炕桨,POST饭尝,PUT,DELETE献宫,HEAD钥平,OPTIONS,PATCH姊途,
  • 域名替換注解涉瘾,在url用{}包含,Path吭净,Query睡汹,QueryMap
@GET("group/{id}/users")
Call<List<User>> groupList(@Path("id") int groupId, @Query("sort") String sort);
  • 表單和multipart ,F(xiàn)ormUrlEncoded寂殉,F(xiàn)ield,Multipart原在,Part
@FormUrlEncoded
@POST("user/edit")
Call<User> updateUser(@Field("first_name") String first, @Field("last_name") String last);

@Multipart
@PUT("user/photo")
Call<User> updateUser(@Part("photo") RequestBody photo, @Part("description") RequestBody description);
  • Header友扰,Body

createService

 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();
          private final Object[] emptyArgs = new Object[0];

          @Override public Object invoke(Object proxy, Method method, @Nullable 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);
            }
            return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
          }
        });
  }
  • Retrofit的所有工作都是為了createService這一步,通過動態(tài)代理返回一個實例庶柿,通過RequestFactory解析注解構(gòu)建Request村怪。Call,Callback浮庐,Response都有一層封裝來隔離甚负。調(diào)用callFactory,傳入Request返回Call审残,調(diào)用Call返回Response梭域,然后通過Converter來逆序列化body為entity
  • 解析方法注解和參數(shù)注解,ServiceMethod搅轿,RequestFactory
  • HttpServiceMethod#createCallAdapter病涨,#createResponseConverter,通過Retrofit里的factory創(chuàng)建對應(yīng)的實例
  • HttpServiceMethod#invoke返回結(jié)果璧坟。
 @Override ReturnT invoke(Object[] args) {
    return callAdapter.adapt(
        new OkHttpCall<>(requestFactory, args, callFactory, responseConverter));
  }
  • OkHttpCall負(fù)責(zé)構(gòu)建Call
  • 最終調(diào)的是DefaultCallAdapterFactory#ExecutorCallbackCall既穆,默認(rèn)把Call轉(zhuǎn)化并代理赎懦,目前其實只干了一件事,因為OkHttp默認(rèn)在非UI線程回調(diào)callback幻工,ExecutorCallbackCall在UI線程分發(fā)

call#enqueue(callback)

public interface Callback<T> {
  void onResponse(Call<T> call, Response<T> response);

  void onFailure(Call<T> call, Throwable t);
}

reference

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末励两,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子囊颅,更是在濱河造成了極大的恐慌伐蒋,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,817評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件迁酸,死亡現(xiàn)場離奇詭異先鱼,居然都是意外死亡,警方通過查閱死者的電腦和手機奸鬓,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,329評論 3 385
  • 文/潘曉璐 我一進(jìn)店門焙畔,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人串远,你說我怎么就攤上這事宏多。” “怎么了澡罚?”我有些...
    開封第一講書人閱讀 157,354評論 0 348
  • 文/不壞的土叔 我叫張陵伸但,是天一觀的道長。 經(jīng)常有香客問我留搔,道長更胖,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,498評論 1 284
  • 正文 為了忘掉前任隔显,我火速辦了婚禮却妨,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘括眠。我一直安慰自己彪标,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 65,600評論 6 386
  • 文/花漫 我一把揭開白布掷豺。 她就那樣靜靜地躺著捞烟,像睡著了一般。 火紅的嫁衣襯著肌膚如雪当船。 梳的紋絲不亂的頭發(fā)上题画,一...
    開封第一講書人閱讀 49,829評論 1 290
  • 那天,我揣著相機與錄音生年,去河邊找鬼婴程。 笑死,一個胖子當(dāng)著我的面吹牛抱婉,可吹牛的內(nèi)容都是我干的档叔。 我是一名探鬼主播桌粉,決...
    沈念sama閱讀 38,979評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼衙四!你這毒婦竟也來了铃肯?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,722評論 0 266
  • 序言:老撾萬榮一對情侶失蹤传蹈,失蹤者是張志新(化名)和其女友劉穎押逼,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體惦界,經(jīng)...
    沈念sama閱讀 44,189評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡挑格,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,519評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了沾歪。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片漂彤。...
    茶點故事閱讀 38,654評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖灾搏,靈堂內(nèi)的尸體忽然破棺而出挫望,到底是詐尸還是另有隱情,我是刑警寧澤狂窑,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布媳板,位于F島的核電站,受9級特大地震影響泉哈,放射性物質(zhì)發(fā)生泄漏蛉幸。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,940評論 3 313
  • 文/蒙蒙 一旨巷、第九天 我趴在偏房一處隱蔽的房頂上張望巨缘。 院中可真熱鬧,春花似錦采呐、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,762評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至仲器,卻和暖如春煤率,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背乏冀。 一陣腳步聲響...
    開封第一講書人閱讀 31,993評論 1 266
  • 我被黑心中介騙來泰國打工蝶糯, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人辆沦。 一個月前我還...
    沈念sama閱讀 46,382評論 2 360
  • 正文 我出身青樓昼捍,卻偏偏與公主長得像识虚,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子妒茬,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,543評論 2 349

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