是時(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.
源碼:
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
為了寫個(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
參數(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信息巫财。
文章轉(zhuǎn)自:是時(shí)候客觀評(píng)價(jià)Retrofit了,Retrofit這幾點(diǎn)你必須明白
Tamic/CSDN 尊重原創(chuàng):http://blog.csdn.net/sk719887916/article/details/53613263