Tamic/CSDN
尊重原创:http://blog.csdn.net/sk719887916/article/details/53613263

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

无耻的广告又来了:

导读:

  • Retrofit 2.0(一) 超能实践,完美支持Https传输

  • Retrofit2.0(二) 完美同步Cookie实现免登录

  • Retrofit 2.0 超能实践(三),轻松实现文件/图片上传

  • Retrofit 2.0 超能实践(四),完成大文件断点下载

  • 基于Retrofit2.0+RxJava 封装的超好用的RetrofitClient工具类(六)

  • 玩转IOC,教你徒手实现自定义的Retrofit框架(七)

  • Retrofit,Okhttp对每个Request统一动态添加header和参数(五)

  • Rxjava和Retrofit 需要掌握的几个实用技巧,缓存问题和统一对有无网络处理问题(八)

  • Novate:对Retrofit2.0的又一次完美改进加强!(九)

#优势

  • 编程思想:减少解耦,降低耦合,让我的接口开发灵活,不同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,

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

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

www.mylist.com/get{Id}

#不支持或缺陷

##Url不能为空

由于我的需求场景是固定的域是动态的吗,有时候我用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 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,而制定的接口必须要加入一个具体是实例!

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 Tif (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>() {@Overridepublic void onResponse(Call<User> call, Response<LoginResult> response) {}@Overridepublic 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/CSDN
尊重原创:http://blog.csdn.net/sk719887916/article/details/53613263

更多原创关注 个人公众号:开发者技术前线 (覆盖8W读者)

欢迎关注我微信公众号!

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

  1. 给ADAS泼冷水?不,是客观评价

    给ADAS泼冷水?不,是客观评价 周纯粼 发表于: 2015/02/10 16:19:31 来源:盖世汽车网 23 0 这些驾驶辅助系统到底有多可靠?它们值得依赖吗?近日,有外媒指出,驾驶辅助系统也会 ...

  2. 讲解视频质量,视频质量测试,主观客观评价视频质量

    1 从模拟信号到数字信号 2 客观视频质量 3 主观视频质量 4 参见 5 参考 视频质量是量化一段视频通过视频传输/处理系统时画面质量变化(通常是下降)程度的方法.由于视频处理系统可能会导致一定的视 ...

  3. 图像质量评价方法中客观评价方法又可以分为三类

    早期的图像处理是随由于通讯方面的要求而发展起来的,随着图像处理技术的发展,数字图像处理技术与理论已经成为计算机应用的一个重要领域,广泛应用于众多的科学与工程应用,如遥感.医学.气象.通信等.然而随着图 ...

  4. 十种客观评价语音质量的方法

    目录 1. 基于SNR的语音质量评价标准 1.1 SNR和分段SNR 1.2 频域SNR 2. 基于LPC系数的语音质量评价标准 2.1 对数似然比距离 2.2 Itakura–Saito距离 2.3 ...

  5. matlab语音信号的变速_十种客观评价语音质量的方法

    语音客观评价标准一般分为语音质量评价和语音可懂度评价,这里先介绍语音质量评价.提起语音质量评价,大家第一个想到的肯定是信噪比这个十分常用的评价标准以及它的相关衍生标准,这里总结一些常用的语音质量评价标 ...

  6. 五大评价偏误 | 管理者如何做到客观评价员工?

    引言 企业管理者常说"对员工一视同仁",然而,一些不理性的印象.感觉,常会不自觉地左右人们的评价. 职场上,有哪些容易踏入的评价误区?管理者又该如何做到客观公正的评价?「得到」作者 ...

  7. 04|主观与客观评价音频质量

    目录 引言: 一. 音频主观评价方法 MUSHRA 介绍 特点: 应用MUSHRA例子: 常用的主观评价指标: 二. 音频客观评价方法 有参考评价: PESQ与POLQA PESQ的算法步骤: not ...

  8. 安卓和java哪个前景好_2016安卓开发和java开发哪个前景好? 请详细客观评价近期以及长远的优势,谢谢!...

    2016安卓开发和java开发哪个前景好? 请详细客观评价近期以及长远的优势,谢谢!以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容,让我们赶快一起来 ...

  9. 客观评价C#的优点和缺点

    对于C#,coder中可以分为2类:一类是C#死忠,对它赞赏到极点:另一类刚好相反,对它极度排斥. 本文将以全面.客观的方式评价这门语言,特别是语法方面. 其实很多人评价一门语言是带着一种感情色彩的. ...

最新文章

  1. 解决Oracle启动失败
  2. 计算机网络基础 — 网络设备转发原理
  3. python爬虫教程书-Python 爬虫:把廖雪峰教程转换成 PDF 电子书
  4. websocket与socket.io
  5. 快速问医生接入云信,医患沟通快速搞定,关键是稳定
  6. jMeter创建微软Azure Lambda Function报错
  7. 漫画科普:芯片是如何设计出来的
  8. 基于ZooKeeper的Dubbo注册中心【转】
  9. C语言extern的用法
  10. 在laravel5.8中集成swoole组件----用协程实现的服务端和客户端(nginx配置篇章)
  11. linux中系统修复
  12. 【小程序】【Tips】【前端】交互【后端】从Json对象数组里面获取数据的方法 - PHP - JS 的原生方法
  13. 百度地图通过经纬度获取地址信息
  14. 一款由css3和jquery实现的卡面折叠式菜单
  15. linux下安装不同版本的jdk
  16. 【牛客网】Whalyzh's Problem
  17. Excel中神秘的间接引用函数Indirect
  18. 欢迎来到whai的博客
  19. 移动MAS短信接口工具类
  20. 入职腾讯第九年,我辞职了!

热门文章

  1. 我的ubuntu 8.10安装配置经验(Intrepid Ibex)(转)
  2. 深度学习笔记(学习中)
  3. 一夜成名的航班追踪网站,什么来头?
  4. 人机交互-7-交互需求定义
  5. Java Web入门之JSTL标签的解析及使用(超详细必看)
  6. RSPSS重复测量方差分析
  7. 心态-《不抱怨的世界》书中的精髓:通过4个步骤,成为一个不抱怨、每天都快乐的人。
  8. mdadm 创建md 删除md步骤
  9. 苹果ios超级签名源码包java版带分发页面支持安卓合并部署教程
  10. 制作坦克大战,坦克移动代码