Retrofit開發(fā)踩的巨坑垫卤!

是時(shí)候客觀評(píng)價(jià)下Retrofit了,retrofit客觀存在的問(wèn)題的你必須要知道出牧!在用retrofit開發(fā)很久的朋友或多或少采了巨坑穴肘,閱讀源碼和實(shí)踐后發(fā)現(xiàn)并不是我們認(rèn)為的那么靈活!

優(yōu)勢(shì)

  • 編程思想:減少解耦舔痕,降低耦合评抚,讓我的接口開發(fā)靈活豹缀,不同api之間互相不干擾,

  • 代碼風(fēng)格:使用注解方式慨代,代碼簡(jiǎn)潔邢笙,易懂,易上手

  • 設(shè)計(jì)思想:采用建造者模式侍匙,開發(fā)構(gòu)建簡(jiǎn)便氮惯!

    具體優(yōu)勢(shì)讀者請(qǐng)閱讀之前系列文章,顯而易見想暗!那今天就來(lái)吐槽一下不足筐骇,至少我覺(jué)得egg pains的地方!

常規(guī)問(wèn)題歸總

1 url被轉(zhuǎn)義

   http://api.myapi.com/
   http%3A%2F%2Fapi.mysite.com%2Fuser%2Flist

請(qǐng)將@path改成@url

   public interface APIService { 
    @GET Call<Users> getUsers(@Url String url);}

或者:

  public interface APIService {
    @GET("{fullUrl}")
    Call<Users> getUsers(@Path(value = "fullUrl", encoded = true) String fullUrl);
}

Method方法找不到

java.lang.IllegalArgumentException: Method must not be null

請(qǐng)指定具體請(qǐng)求類型@get @post等

   public interface APIService { 

   @GET Call<Users> getUsers(@Url String url);
}

Url編碼不對(duì)江滨,@fieldMap parameters must be use FormUrlEncoded

如果用fieldMap加上FormUrlEncoded編碼

@POST()
@FormUrlEncoded
Observable<ResponseBody> executePost (@FieldMap Map<String, Object> maps);

上層需要轉(zhuǎn)換將自己的map轉(zhuǎn)換為FieldMap

 @FieldMap(encoded = true) Map<String, Object> parameters,

4 paht和url一起使用

Using @Path and @Url paramers together with retrofit2

java.lang.IllegalArgumentException: @Path parameters may not be used with @Url. (parameter #4

如果你是這樣的:

 @GET
Call<DataResponse> getOrder(@Url String url, @Path("id") int id);

請(qǐng)?jiān)谀愕膗rl指定占位符.url:

www.mylist.com/get{Id}

不支持或缺陷

Url不能為空

由于我的需求場(chǎng)景是固定的域是動(dòng)態(tài)的嗎铛纬,有時(shí)候我用www.myapi.com,有時(shí)候是www.youapi.com. 因此我決定在構(gòu)建retrofit時(shí)候不加入baseUrl;

Retrofit retrofit = new Retrofit.Builder()
        .addConverterFactory(GsonConverterFactory.create())
        .build();

結(jié)果報(bào)異常了

Base URL required

源碼中發(fā)現(xiàn)構(gòu)建時(shí)候check Url,如果為空就異常

public Retrofit build() {
  if (baseUrl == null) {
    throw new IllegalStateException("Base URL required.");
  }

后來(lái)雖然對(duì)動(dòng)態(tài)改Url很好解決唬滑,用@url代替告唆,但我我怎么也不明白為何要限制!

@GET 
Call getOrder(@Url String url,  @Path(“id”) int id);

Delete不支持body

Retrofit @Delete with body,Non-body HTTP method cannot contain @Body ##

使用retrofit進(jìn)行delete請(qǐng)求時(shí)晶密,后臺(tái)接口定會(huì)了以body的格式擒悬!
于是乎我開心的定義了一下接口:

@DELETE("/user/delete")
Call<Void> remove (@Body HashMap<String,String> content);

結(jié)果一個(gè)異常蒙蔽了:

java.lang.IllegalArgumentException:Non-body HTTP method cannot contain @Body

最后官網(wǎng)發(fā)現(xiàn)其并不支持向服務(wù)器傳body,會(huì)報(bào)這個(gè)異常java.lang.IllegalArgumentException:Non-body HTTP method cannot contain @Body 稻艰,gtihub作者也表示不支持body懂牧,最后發(fā)現(xiàn)了答案 用自定義注解,如需向服務(wù)器傳body可以這么寫

@HTTP(method = "DELETE",path = "/user/delete",hasBody = true)
Call<Void> remove (@Body HashMap<String,String> content);

接口實(shí)例不支持T

我們每次用retrofit去執(zhí)行一次網(wǎng)絡(luò)請(qǐng)求尊勿,必定要定義一個(gè)ApiServie,而制定的接口必須要加入一個(gè)具體是實(shí)例僧凤!

public interface ApiService {

@GET
Call<DataResponse> get(@Url String url, @Query("id") int id);
}

接著就去構(gòu)建apiService實(shí)例!

Retrofit retrofit = new Retrofit.Builder()
        .baseUrl("http://localhost:8080/")
        .addConverterFactory(GsonConverterFactory.create())
        .build();

構(gòu)建Api

ApiServicer apiService = retrofit.create(
ApiService.class);

開發(fā)者很多時(shí)候遇到接口眾多情況下 想寫個(gè)一個(gè)baseApiService,然后不同模塊的api去繼承這個(gè)baseApiService,那么會(huì)去按常規(guī)的aop思想去繼承構(gòu)建一個(gè)baseService, 其他他的子類實(shí)現(xiàn)這個(gè)方法元扔,看看下面方法躯保,具體返回對(duì)象被寫成T,是沒(méi)毛才煊铩途事!

public interface BaseApiService {

  @GET
  Call<T> get(@Url String url,  @Path("id") int id);

}

當(dāng)我遇到一個(gè)登錄和一個(gè)退出場(chǎng)景時(shí)候,不想寫到一個(gè)ApiService中擅羞,很有可能想去構(gòu)建一個(gè)loginApiService和LoginOutApiService:

public class loginApiService implements BaseApiService {

  @GET
  Call<User> get(@Url String url, @Query("id") int id)   {
  // ......
  }   

 }

ApiServicer apiService = retrofit.create(loginApiService.class);

結(jié)果出問(wèn)題了尸变,我的天哪! 我這有錯(cuò)嗎 我寫個(gè)接口减俏,用實(shí)現(xiàn)類去執(zhí)行召烂,java告訴我這樣不行了嗎。蒙蔽了垄懂,拋異常了骑晶!

API declarations must be interfaces.

image.png

源碼:

static <T> void validateServiceInterface(Class<T> service) {
if (!service.isInterface()) {
  throw new IllegalArgumentException("API declarations must be interfaces.");
}

好的 作者意圖很明顯 用接口類型痛垛,你說(shuō)用接口,好 我照著做桶蛔!

public interface loginApiService extends BaseApiService {

  @GET
  Call<T> get(@Url String url,@Query("id") int id)      

 }

結(jié)果:

T is not a valid response body type. Did you mean ResponseBody?

我感覺(jué)我一定要解決,我強(qiáng)制更改了父類的返回值匙头,以為能通過(guò)!

public interface loginApiService extends BaseApiService {

  @GET
  Call<User> get(@Url String url, @Query("id") int id)      
 }

結(jié)果都編譯不過(guò)仔雷,我的天哪!不用泛型蹂析,我開始蒙逼了,難道讓我每個(gè)請(qǐng)求接口都寫一個(gè)Api方法碟婆,雖然通過(guò)九牛二虎之力电抚,用反射解決了,但我我真想說(shuō) :nnD

image.png

為了寫個(gè)通用接口我不得不:

  @GET
  Call<ResponseBody> get(@Url String url, @Map<String, String> mapsid)      
 

這樣我的登錄登出可以用一個(gè)接口竖共,但每次返回的實(shí)體需要我自己解析蝙叛,于是乎反射用上了

  private List<Type> MethodHandler(Type[] types) {
    Log.d(TAG, "types size: " + types.length);
    List<Type> needtypes = new ArrayList<>();
    Type needParentType = null;
    for (Type paramType : types) {
        // if Type is T
        if (paramType instanceof ParameterizedType) {
            Type[] parentypes = ((ParameterizedType) paramType).getActualTypeArguments();

            for (Type childtype : parentypes) {
                needtypes.add(childtype);
                if (childtype instanceof ParameterizedType) {
                    Type[] childtypes = ((ParameterizedType) childtype).getActualTypeArguments();
                    for (Type type : childtypes) {
                        needtypes.add(type);
                        //needChildType = type;
                        Log.d(TAG, "type:" + childtype);
                    }
                }
            }
        }
    }
    return types;
}

接著我在Retroift成功的的回調(diào)中反序列化實(shí)體:

 User user = new Gson().fromJson(ResponseBody.body.toString(), mType);

mType就是我用反射出來(lái)的上層傳入的user對(duì)象,尼瑪呀 我真不知道作者為何這么設(shè)計(jì),egg pains

image.png

參數(shù)不支持空

上面的問(wèn)題我不說(shuō)啥,現(xiàn)在到了我無(wú)法忍受的地方剑按,比如我們定義一個(gè)api

@GET("/path")
Call<ResponseBody> get (@QueryMap Map<String, String> mapsid)      

我設(shè)計(jì)本意是上層可以動(dòng)態(tài)傳慘,而且這個(gè)參數(shù)可能不固定

構(gòu)建參數(shù)時(shí):

 Map<String, String> parameters = new HashMap<>();
    parameters.put("apikey", "27b6fb21f2b42e9d70cd722b2ed038a9");
    parameters.put("Accept", "application/json");

運(yùn)行程序肺然,api 結(jié)果沒(méi)啥問(wèn)題,到此我以為所有的參數(shù)都可以這么加入腿准,于是我下一個(gè)免登陸場(chǎng)景使用了此方案际起,token是服務(wù)器返回的字符串。每次請(qǐng)求加上去吐葱,如果本地沒(méi)有就不加街望,首次肯定是沒(méi)有的;構(gòu)建參數(shù):

    Map<String, String> parameters = new HashMap<>();
    parameters.put("token", getToken());
    parameters.put("Accept", "application/json");

構(gòu)建:

  Call<LoginResult> call = apiService.get(parameters);
  call.enqueue(new Callback<User>() {
   @Override
   public void onResponse(Call<User> call, Response<LoginResult> response) {

   }

   @Override
   public void onFailure(Call<user> call, Throwable t) {

   }

結(jié)果運(yùn)行唇撬,我擦磊它匕,這樣也報(bào)錯(cuò),顯示token不能為空窖认,難道我在不確定一個(gè)值的時(shí)候value還不能加入空,我不得不用下面方式構(gòu)建參數(shù)告希,

   Map<String, String> parameters = new HashMap<>();
    parameters.put("token", getToken() == Null扑浸?gettoken() :" " );
    parameters.put("Accept", "application/json");

最后讀取源碼發(fā)現(xiàn)了@QueryMap k-v不能為空,好吧我醉了燕偶!

攔截默認(rèn)異常

Retrofit攔截Okhttp默認(rèn)error喝噪,如果web端默認(rèn)在code在200或者300時(shí)候是正常msg信息,走onResponse()指么。

如果web定義的成功碼如果是在< 200 并且 > 300時(shí)候酝惧,就不走成功 榴鼎。并且服務(wù)器如果已定義的結(jié)果碼和系統(tǒng)的默認(rèn)int沖突情況,自定義的msg也無(wú)法回調(diào)到onError()中晚唇,結(jié)果被retrofit主動(dòng)獲取了super Throw的Msg信息巫财。

image.png

文章轉(zhuǎn)自:是時(shí)候客觀評(píng)價(jià)Retrofit了,Retrofit這幾點(diǎn)你必須明白
Tamic/CSDN 尊重原創(chuàng):http://blog.csdn.net/sk719887916/article/details/53613263

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末哩陕,一起剝皮案震驚了整個(gè)濱河市平项,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌悍及,老刑警劉巖闽瓢,帶你破解...
    沈念sama閱讀 211,042評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異心赶,居然都是意外死亡扣讼,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,996評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門缨叫,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)届谈,“玉大人,你說(shuō)我怎么就攤上這事弯汰〖枭剑” “怎么了?”我有些...
    開封第一講書人閱讀 156,674評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵咏闪,是天一觀的道長(zhǎng)曙搬。 經(jīng)常有香客問(wèn)我,道長(zhǎng)鸽嫂,這世上最難降的妖魔是什么纵装? 我笑而不...
    開封第一講書人閱讀 56,340評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮据某,結(jié)果婚禮上橡娄,老公的妹妹穿的比我還像新娘。我一直安慰自己癣籽,他們只是感情好挽唉,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,404評(píng)論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著筷狼,像睡著了一般瓶籽。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上埂材,一...
    開封第一講書人閱讀 49,749評(píng)論 1 289
  • 那天塑顺,我揣著相機(jī)與錄音,去河邊找鬼俏险。 笑死严拒,一個(gè)胖子當(dāng)著我的面吹牛扬绪,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播裤唠,決...
    沈念sama閱讀 38,902評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼挤牛,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了巧骚?” 一聲冷哼從身側(cè)響起赊颠,我...
    開封第一講書人閱讀 37,662評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎劈彪,沒(méi)想到半個(gè)月后竣蹦,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,110評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡沧奴,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評(píng)論 2 325
  • 正文 我和宋清朗相戀三年痘括,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片滔吠。...
    茶點(diǎn)故事閱讀 38,577評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡纲菌,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出疮绷,到底是詐尸還是另有隱情翰舌,我是刑警寧澤,帶...
    沈念sama閱讀 34,258評(píng)論 4 328
  • 正文 年R本政府宣布冬骚,位于F島的核電站椅贱,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏只冻。R本人自食惡果不足惜庇麦,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,848評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望喜德。 院中可真熱鬧山橄,春花似錦、人聲如沸舍悯。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,726評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)贱呐。三九已至丧诺,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間奄薇,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,952評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工抗愁, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留馁蒂,地道東北人呵晚。 一個(gè)月前我還...
    沈念sama閱讀 46,271評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像沫屡,于是被迫代替她去往敵國(guó)和親饵隙。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,452評(píng)論 2 348

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