是时候客观评价Retrofit了,这几点你必须明白!

Tamic/简书
尊重原创:http://www.jianshu.com/p/1f10d5477566

是时候客观评价下Retrofit了,retrofit客观存在的问题的你必须要知道!在用retrofit开发很久的朋友或多或少踩了巨坑,阅读源码和实践后发现并不是我们认为的那么灵活!

无耻的广告又来了:

导读:

优势

  • 编程思想:减少解耦,降低耦合,让我的接口开发灵活,不同api之间互相不干扰,

  • 代码风格:使用注解方式,代码简洁,易懂,易上手

  • 设计思想:采用多种设计模式,开发构建简便!

具体优势读者请阅读之前系列文章,显而易见!那今天就来吐槽一下不足,至少我觉得egg pains的地方!

常规问题归总

1 url被转义

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

请将@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

请指定具体请求类型@get @post等

   public interface APIService { 

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

Url编码不对,@fieldMap parameters must be use FormUrlEncoded

如果用fieldMap加上FormUrlEncoded编码

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

上层需要转换将自己的map转换为FieldMap

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

@path和@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);

请在你的url指定占位符.url:

www.mylist.com/get{Id}

不支持或缺陷

Url不能为空

由于我的需求场景是固定的域是动态的,有时候我的host是用www.myapi.com,有时候却是www.youapi.com. 因此我决定在构建retrofit时候不加入baseUrl;

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

结果报异常了

Base URL required

源码中发现构建时候check Url,如果为空就异常

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

后来虽然对动态改Url也能很好解决(下面代码片段),用@url代替,但我怎么也不明白为何加入此要限制!

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

Delete不支持body

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

使用retrofit进行delete请求时,后台接口定会了以body的格式!
于是乎我开心的定义了一下接口:

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

结果一个异常蒙蔽了:

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

最后官网发现其并不支持向服务器传body,会报这个异常java.lang.IllegalArgumentException:Non-body HTTP method cannot contain @Body

gtihub作者也表示不支持body,最后发现了答案 用自定义注解,如需向服务器传body可以这么写

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

接口实例不支持T

我们每次用retrofit去执行一次网络请求,必定要定义一个ApiServie,而制定的接口必须要加入一个具体是实例!如下:DataRespone就是具体的模型。

public interface ApiService {

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

接着就去构建apiService实例!

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

构建Api

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

开发者很多时候遇到接口众多情况下 想写个一个baseApiService,然后不同模块的api去继承这个baseApiService,那么会去按常规的aop思想去继承构建一个baseService, 其他他的子类实现这个方法,看看下面方法,具体返回对象被写成T,是没毛病!

public interface BaseApiService {

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

当我遇到一个登录和一个退出场景时候,不想写到一个ApiService中,很有可能想去构建一个loginApiService和LoginOutApiService:

public class loginApiService implements BaseApiService {

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

 }

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

结果出问题了,我的天哪! 我这有错吗 我写个接口,用实现类去执行,java告诉我这样不行了吗。蒙蔽了,抛异常了!

API declarations must be interfaces.

这里写图片描述

源码:

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

好的 ,意思很明显,必须要用接口类型,你说用接口,好 我照着做!

public interface loginApiService extends BaseApiService {

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

结果:

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

我一定要解决, 我强制更改了父类的返回值,以为能通过!

public interface loginApiService extends BaseApiService {

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

结果都编译不过,我的天哪!不用泛型,我开始蒙逼了,难道让我每个请求接口都写一个Api方法,虽然通过九牛二虎之力,用反射解决了,但我我真想说 :nnD

这里写图片描述

为了写个通用接口我不得不:

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

这样我的登录登出可以用一个接口,但每次返回的实体需要我自己解析,于是乎反射用上了

  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成功的的回调中反序列化实体地方

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

mType就是我用反射出来的上层传入的user对象,尼玛呀 我真不知道作者为何这么设计,egg pains

这里写图片描述

参数不支持空

上面的问题我不说啥,现在到了我无法忍受的地方,比如我们定义一个api

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

我设计本意是上层可以动态传惨,而且这个参数可能不固定

构建参数时:

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

运行程序,api 结果没啥问题,到此我以为所有的参数都可以这么加入,于是我下一个免登陆场景使用了此方案,token是服务器返回的字符串。每次请求加上去,如果本地没有就不加,首次肯定是没有的;构建参数:

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

构建:

  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) {

       
   }

结果运行,我擦磊,这样也报错,显示token不能为空,难道我在不确定一个值的时候value还不能加入空,我不得不用下面方式构建参数,

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

最后笔者阅读源码发现了@QueryMap k-v不能为空,好吧我醉了!

拦截默认异常

Retrofit拦截Okhttp默认error,如果web端默认在code在200或者300时候是正常msg信息,走onResponse()。

如果web定义的成功码如果是在< 200 并且 > 300时候,就不走成功 。并且服务器如果已定义的结果码和系统的默认int冲突情况,自定义的msg也无法回调到onError()中,结果被retrofit主动获取了super Throw的Msg信息。


这里写图片描述

Tamic/简书
尊重原创:http://www.jianshu.com/p/1f10d5477566

本文已在版权印备案,如需转载请访问版权印

http://www.banquanyin.com/C1017011317002013851

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容