本文注目录:

  • Retrofit入门
  • Retrofit注解详解
  • Gson与Converter
  • RxJava与CallAdapter
  • 自定义Converter
  • 自定义CallAdapter
  • 其它说明

前言

本文中的Retrofit均指代Retrofit2.0。
本文涉及到的代码以及测试使用的接口可在Github上找到。
测试接口服务器在 server 项目下,直接运行 RESTServer.main() 即可启动测试服务器,所面代码示例均使用该接口(接口地址 http://localhost:4567/ ).
当然你也可以自己借助 json-server 或 最新开源的Parse 搭建一个REST API,不过都需要安装Node.js,有兴趣的可以去试试。

接口列表:


前面写了你应该知道的HTTP基础知识 介绍了HTTP的相关知识,不知道那些想了解Retrofit的同鞋是不是去看了Retrofit的官方教程,曾经我在你真的会用Gson吗?Gson使用指南(四) 中说当你了解了注解、反射、泛型、HTTP的内容只需要看一篇Retrofit的代码示例就可以轻松玩转Retrofit,不知道你玩转了没?注:以上的接口的{id}{page}均代表一个纯数字,/blog/{id} 可以用/blog?id=XXX 代替,page同理。

当然注解、反射、泛型的内容还没有写,Retrofit的内容却先来了!毕竟看懂Retrofit也只需要会使就行,你准备好了吗?

1、Retrofit入门

Retrofit 其实相当简单,简单到源码只有37个文件,其中22个文件是注解还都和HTTP有关,真正暴露给用户的类并不多,所以我看了一遍 官方教程 大多数情景就可以无障碍使用,如果你还没有看过,可以先去看看,虽然是英文,但代码才是最好的教程不是么?当然本篇文章会介绍得详细一点,不能写一篇水文,毕竟我给它命名为《你真的会用Retrofit2吗?Retrofit2完全教程》。

1.1、创建Retrofit实例

  1. Retrofit retrofit = new Retrofit.Builder()
  2. .baseUrl("http://localhost:4567/")
  3. .build();

创建Retrofit实例时需要通过Retrofit.Builder,并调用baseUrl方法设置URL。
注: Retrofit2 的baseUlr 必须以 /(斜线) 结束,不然会抛出一个IllegalArgumentException,所以如果你看到别的教程没有以/ 结束,那么多半是直接从Retrofit 1.X 照搬过来的。

1.2、接口定义

以获取指定id的Blog为例:

  1. public interface BlogService {
  2. @GET("blog/{id}")
  3. Call<ResponseBody> getFirstBlog(@Path("id") int id);
  4. }

注意,这里是interface不是class,所以我们是无法直接调用该方法,我们需要用Retrofit创建一个BlogService的代理对象。

  1. BlogService service = retrofit.create(BlogService.class);

拿到代理对象之后,就可以调用该方法啦。

1.3、接口调用

  1. Call<ResponseBody> call = service.getFirstBlog(2);
  2. // 用法和OkHttp的call如出一辙,
  3. // 不同的是如果是Android系统回调方法执行在主线程
  4. call.enqueue(new Callback<ResponseBody>() {
  5. @Override
  6. public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
  7. try {
  8. System.out.println(response.body().string());
  9. } catch (IOException e) {
  10. e.printStackTrace();
  11. }
  12. }
  13. @Override
  14. public void onFailure(Call<ResponseBody> call, Throwable t) {
  15. t.printStackTrace();
  16. }
  17. });

打印结果:

  1. {"code":200,"msg":"OK","data":{"id":2,"date":"2016-04-15 03:17:50","author":"怪盗kidou","title":"Retrofit2 测试2","content":"这里是 Retrofit2 Demo 测试服务器2"},"count":0,"page":0}
示例源码见 Example01.java

2、Retrofit注解详解

上面提到Retrofit 共22个注解,这节就专门介绍这22个注解,为帮助大家更好理解我将这22个注解分为三类,并用表格的形式展现出来,表格上说得并不完整,具体的见源码上的例子注释。

第一类:HTTP请求方法

HTTP请求方法注解

以上表格中的除HTTP以外都对应了HTTP标准中的请求方法,而HTTP注解则可以代替以上方法中的任意一个注解,有3个属性:methodpath,hasBody,下面是用HTTP注解实现上面Example01.java 的例子。

  1. public interface BlogService {
  2. /**
  3. * method 表示请的方法,不区分大小写
  4. * path表示路径
  5. * hasBody表示是否有请求体
  6. */
  7. @HTTP(method = "get", path = "blog/{id}", hasBody = false)
  8. Call<ResponseBody> getFirstBlog(@Path("id") int id);
  9. }

示例源码见 Example02.java

第二类:标记类

标记类注解

示例源码见 Example03.java

第三类:参数类

参数类注解

注1:{占位符}和PATH尽量只用在URL的path部分,url中的参数使用QueryQueryMap 代替,保证接口定义的简洁
注2:QueryFieldPart这三者都支持数组和实现了Iterable接口的类型,如ListSet等,方便向后台传递数组。

  1. Call<ResponseBody> foo(@Query("ids[]") List<Integer> ids);
  2. //结果:ids[]=0&ids[]=1&ids[]=2

Path 示例源码见 Example01.java
Field、FieldMap、Part和PartMap 示例源码见 Example03.java
Header和Headers 示例源码见 Example04.java
Query、QueryMap、Url 示例源码见 Example05.java

3、Gson与Converter

在默认情况下Retrofit只支持将HTTP的响应体转换换为ResponseBody,
这也是什么我在前面的例子接口的返回值都是 Call<ResponseBody>
但如果响应体只是支持转换为ResponseBody的话何必要引用泛型呢,
返回值直接用一个Call就行了嘛,既然支持泛型,那说明泛型参数可以是其它类型的,
Converter就是Retrofit为我们提供用于将ResponseBody转换为我们想要的类型,
有了Converter之后我们就可以写把我们的第一个例子的接口写成这个样子了:

  1. public interface BlogService {
  2. @GET("blog/{id}") //这里的{id} 表示是一个变量
  3. Call<Result<Blog>> getFirstBlog(/** 这里的id表示的是上面的{id} */@Path("id") int id);
  4. }

当然只改变泛型的类型是不行的,我们在创建Retrofit时需要明确告知用于将ResponseBody转换我们泛型中的类型时需要使用的Converter

引入Gson支持:

  1. compile 'com.squareup.retrofit2:converter-gson:2.0.2'

通过GsonConverterFactory为Retrofit添加Gson支持:

  1. Gson gson = new GsonBuilder()
  2. //配置你的Gson
  3. .setDateFormat("yyyy-MM-dd hh:mm:ss")
  4. .create();
  5. Retrofit retrofit = new Retrofit.Builder()
  6. .baseUrl("http://localhost:4567/")
  7. //可以接收自定义的Gson,当然也可以不传
  8. .addConverterFactory(GsonConverterFactory.create(gson))
  9. .build();

示例源码见 Example06.java

这样Retrofit就会使用Gson将ResponseBody转换我们想要的类型。

这是时候我们终于可以演示如使创建一个Blog了!

  1. @POST("blog")
  2. Call<Result<Blog>> createBlog(@Body Blog blog);

@Body注解的的Blog将会被Gson转换成RequestBody发送到服务器。

  1. BlogService service = retrofit.create(BlogService.class);
  2. Blog blog = new Blog();
  3. blog.content = "新建的Blog";
  4. blog.title = "测试";
  5. blog.author = "怪盗kidou";
  6. Call<Result<Blog>> call = service.createBlog(blog);

结果:

  1. Result{code=200, msg='OK', data=Blog{id=20, date='2016-04-21 05:29:58', author='怪盗kidou', title='测试', content='新建的Blog'}, count=0, page=0}

示例源码见 Example07.java

如果你对Gson不熟悉可以参考我写的《你真的会用Gson吗?Gson使用指南》 系列。

4、RxJava与CallAdapter

说到Retrofit就不得说到另一个火到不行的库RxJava,网上已经不少文章讲如何与Retrofit结合,但这里还是会有一个RxJava的例子,不过这里主要目的是介绍使用CallAdapter所带来的效果。

第3节介绍的Converter是对于Call<T>T的转换,而CallAdapter则可以对Call转换,这样的话Call<T>中的Call也是可以被替换的,而返回值的类型就决定你后续的处理程序逻辑,同样Retrofit提供了多个CallAdapter,这里以RxJava的为例,用Observable代替Call

引入RxJava支持:

  1. compile 'com.squareup.retrofit2:adapter-rxjava:2.0.2'

通过RxJavaCallAdapterFactory为Retrofit添加RxJava支持:

  1. Retrofit retrofit = new Retrofit.Builder()
  2. .baseUrl("http://localhost:4567/")
  3. .addConverterFactory(GsonConverterFactory.create())
  4. .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
  5. .build();

接口设计:

  1. public interface BlogService {
  2. @POST("/blog")
  3. Observable<Result<List<Blog>>> getBlogs();
  4. }

使用:

  1. BlogService service = retrofit.create(BlogService.class);
  2. service.getBlogs(1)
  3. .subscribeOn(Schedulers.io())
  4. .subscribe(new Subscriber<Result<List<Blog>>>() {
  5. @Override
  6. public void onCompleted() {
  7. System.out.println("onCompleted");
  8. }
  9. @Override
  10. public void onError(Throwable e) {
  11. System.err.println("onError");
  12. }
  13. @Override
  14. public void onNext(Result<List<Blog>> blogsResult) {
  15. System.out.println(blogsResult);
  16. }
  17. });

结果:

  1. Result{code=200, msg='OK', data=[Blog{id=1, date='2016-04-15 03:17:50', author='怪盗kidou', title='Retrofit2 测试1', content='这里是 Retrofit2 Demo 测试服务器1'},.....], count=20, page=1}
示例源码见 Example08.java

「20160608补充」:像上面的这种情况最后我们无法获取到返回的Header和响应码的,如果我们需要这两者,提供两种方案:
1、用Observable<Response<T>>``Observable<T> ,这里的Responseretrofit2.Response
2、用Observable<Result<T>> 代替Observable<T>,这里的Result是指retrofit2.adapter.rxjava.Result,这个Result中包含了Response的实例

5、自定义Converter

本节的内容是教大家实现在一简易的Converter,这里以返回格式为Call<String>为例。

在此之前先了解一下Converter接口及其作用:

  1. public interface Converter<F, T> {
  2. // 实现从 F(rom) 到 T(o)的转换
  3. T convert(F value) throws IOException;
  4. // 用于向Retrofit提供相应Converter的工厂
  5. abstract class Factory {
  6. // 这里创建从ResponseBody其它类型的Converter,如果不能处理返回null
  7. // 主要用于对响应体的处理
  8. public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
  9. Retrofit retrofit) {
  10. return null;
  11. }
  12. // 在这里创建 从自定类型到ResponseBody 的Converter,不能处理就返回null,
  13. // 主要用于对Part、PartMap、Body注解的处理
  14. public Converter<?, RequestBody> requestBodyConverter(Type type,
  15. Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
  16. return null;
  17. }
  18. // 这里用于对Field、FieldMap、Header、Path、Query、QueryMap注解的处理
  19. // Retrfofit对于上面的几个注解默认使用的是调用toString方法
  20. public Converter<?, String> stringConverter(Type type, Annotation[] annotations,
  21. Retrofit retrofit) {
  22. return null;
  23. }
  24. }
  25. }

我们要想从Call<ResponseBody> 转换为 Call<String> 那么对应的F和T则分别对应ResponseBodyString,我们定义一个StringConverter并实现Converter接口。

  1. public static class StringConverter implements Converter<ResponseBody, String> {
  2. public static final StringConverter INSTANCE = new StringConverter();
  3. @Override
  4. public String convert(ResponseBody value) throws IOException {
  5. return value.string();
  6. }
  7. }

我们需要一个Fractory来向Retrofit注册StringConverter

  1. public static class StringConverterFactory extends Converter.Factory {
  2. public static final StringConverterFactory INSTANCE = new StringConverterFactory();
  3. public static StringConverterFactory create() {
  4. return INSTANCE;
  5. }
  6. // 我们只关实现从ResponseBody 到 String 的转换,所以其它方法可不覆盖
  7. @Override
  8. public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
  9. if (type == String.class) {
  10. return StringConverter.INSTANCE;
  11. }
  12. //其它类型我们不处理,返回null就行
  13. return null;
  14. }
  15. }

使用Retrofit.Builder.addConverterFactory向Retrofit注册我们StringConverterFactory

  1. Retrofit retrofit = new Retrofit.Builder()
  2. .baseUrl("http://localhost:4567/")
  3. // 如是有Gson这类的Converter 一定要放在其它前面
  4. .addConverterFactory(StringConverterFactory.create())
  5. .addConverterFactory(GsonConverterFactory.create())
  6. .build();

注:addConverterFactory是有先后顺序的,如果有多个ConverterFactory都支持同一种类型,那么就是只有第一个才会被使用,而GsonConverterFactory是不判断是否支持的,所以这里交换了顺序还会有一个异常抛出,原因是类型不匹配。

只要返回值类型的泛型参数就会由我们的StringConverter处理,不管是Call<String>还是Observable<String>

有没有很简单?如果你有其它的需求处理的就自己实现吧。

示例源码见 Example09.java

6、自定义CallAdapter

本节将介绍如何自定一个CallAdapter,并验证是否所有的String都会使用我们第5节中自定义的Converter。

先看一下CallAdapter接口定义及各方法的作用:

  1. public interface CallAdapter<T> {
  2. // 直正数据的类型 如Call<T> 中的 T
  3. // 这个 T 会作为Converter.Factory.responseBodyConverter 的第一个参数
  4. // 可以参照上面的自定义Converter
  5. Type responseType();
  6. <R> T adapt(Call<R> call);
  7. // 用于向Retrofit提供CallAdapter的工厂类
  8. abstract class Factory {
  9. // 在这个方法中判断是否是我们支持的类型,returnType 即Call<Requestbody>和`Observable<Requestbody>`
  10. // RxJavaCallAdapterFactory 就是判断returnType是不是Observable<?> 类型
  11. // 不支持时返回null
  12. public abstract CallAdapter<?> get(Type returnType, Annotation[] annotations,
  13. Retrofit retrofit);
  14. // 用于获取泛型的参数 如 Call<Requestbody> 中 Requestbody
  15. protected static Type getParameterUpperBound(int index, ParameterizedType type) {
  16. return Utils.getParameterUpperBound(index, type);
  17. }
  18. // 用于获取泛型的原始类型 如 Call<Requestbody> 中的 Call
  19. // 上面的get方法需要使用该方法。
  20. protected static Class<?> getRawType(Type type) {
  21. return Utils.getRawType(type);
  22. }
  23. }
  24. }

了解了CallAdapter的结构和其作用之后,我们就可以开始自定义我们的CallAdapter了,本节以CustomCall<String>为例。

在此我们需要定义一个CustomCall,不过这里的CustomCall作为演示只是对Call的一个包装,并没有实际的用途。

  1. public static class CustomCall<R> {
  2. public final Call<R> call;
  3. public CustomCall(Call<R> call) {
  4. this.call = call;
  5. }
  6. public R get() throws IOException {
  7. return call.execute().body();
  8. }
  9. }

有了CustomCall,我们还需要一个CustomCallAdapter来实现 Call<T> 到CustomCall<T>的转换,这里需要注意的是最后的泛型,是我们要返回的类型。

  1. public static class CustomCallAdapter implements CallAdapter<CustomCall<?>> {
  2. private final Type responseType;
  3. // 下面的 responseType 方法需要数据的类型
  4. CustomCallAdapter(Type responseType) {
  5. this.responseType = responseType;
  6. }
  7. @Override
  8. public Type responseType() {
  9. return responseType;
  10. }
  11. @Override
  12. public <R> CustomCall<R> adapt(Call<R> call) {
  13. // 由 CustomCall 决定如何使用
  14. return new CustomCall<>(call);
  15. }
  16. }

提供一个CustomCallAdapterFactory用于向Retrofit提供CustomCallAdapter

  1. public static class CustomCallAdapterFactory extends CallAdapter.Factory {
  2. public static final CustomCallAdapterFactory INSTANCE = new CustomCallAdapterFactory();
  3. @Override
  4. public CallAdapter<?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
  5. // 获取原始类型
  6. Class<?> rawType = getRawType(returnType);
  7. // 返回值必须是CustomCall并且带有泛型
  8. if (rawType == CustomCall.class && returnType instanceof ParameterizedType) {
  9. Type callReturnType = getParameterUpperBound(0, (ParameterizedType) returnType);
  10. return new CustomCallAdapter(callReturnType);
  11. }
  12. return null;
  13. }
  14. }

使用addCallAdapterFactory向Retrofit注册CustomCallAdapterFactory

  1. Retrofit retrofit = new Retrofit.Builder()
  2. .baseUrl("http://localhost:4567/")
  3. .addConverterFactory(Example09.StringConverterFactory.create())
  4. .addConverterFactory(GsonConverterFactory.create())
  5. .addCallAdapterFactory(CustomCallAdapterFactory.INSTANCE)
  6. .build();

注: addCallAdapterFactoryaddConverterFactory同理,也有先后顺序。

示例源码见 Example10.java

7、其它说明

7.1 Retrofit.Builder

前面用到了 Retrofit.Builder 中的baseUrladdCallAdapterFactoryaddConverterFactorybuild方法,还有callbackExecutorcallFactoryclientvalidateEagerly这四个方法没有用到,这里简单的介绍一下。


7.2 Retrofit的Url组合规则

从上面不能难看出以下规则:

  • 如果你在注解中提供的url是完整的url,则url将作为请求的url。
  • 如果你在注解中提供的url是不完整的url,且不以 / 开头,则请求的url为baseUrl+注解中提供的值
  • 如果你在注解中提供的url是不完整的url,且以 / 开头,则请求的url为baseUrl的主机部分+注解中提供的值

7.3 Retrofit提供的Converter

7.4 Retrofit提供的CallAdapter:

7.5 关于源码

看到这儿可能有小伙伴要问为什么源码没有把类拆分到单独的文件,命名也不能体现其用途,这里主要是因为方便大家看源码,而不是将注意力放在反复跳转上,另一方面也是因为同一个例子中不可避免的使用其它小节要介绍的内容,所以就直接用了ExampleXX的形式,不过在项目中千万不要使用这种方式,一定要好好命名,做到见名知意。

结语

其它本博客的内容早就已经完成好了,但由于当时HTTP、反射、注解的博客一篇也没有写,所以一直没有发,期间也有不少的博主写了Retrofit2的博文,不过呢没有自定义相关的内容也没有对各个注解进行详解,所以我还是决定发出来帮助一下那此对Retrofit2无从下手同鞋。

原文链接:http://www.jianshu.com/p/308f3c54abdd

Retrofit2完全教程的更多相关文章

  1. 你真的会用Retrofit2吗?Retrofit2完全教程

    本文注目录: Retrofit入门 Retrofit注解详解 Gson与Converter RxJava与CallAdapter 自定义Converter 自定义CallAdapter 其它说明 前言 ...

  2. retrofit2 使用教程 及 Android 网络架构搭建 (原创)

    squareup 推出 retrofit2 已经有一段时间了,现在的版本比较稳定,没有什么大坑了.网络上的教程要么太简单,只是个Demo:要么有些落时,要么复用性比较差,所以自己写个教程,供大家参考. ...

  3. 安卓开发实战之app之版本更新升级(DownloadManager和http下载)完整实现

    转载: https://blog.csdn.net/u013278099/article/details/52692008 前言 本文将讲解app的升级与更新.一般而言用户使用App的时候升级提醒有两 ...

  4. Retrofit2.0+RxJava+Dragger2实现不一样的Android网络架构搭建

    Tamic :csdn http://blog.csdn.net/sk719887916 众所周知,手机APP的核心就在于调用后台接口,展示相关信息,方便我们在手机上就能和外界交互.所以APP中网络框 ...

  5. 基于Retrofit2.0+RxJava+Dragger2实现不一样的Android网络构架搭建(转载)

    转载请注明出处:http://blog.csdn.net/finddreams/article/details/50849385#0-qzone-1-61707-d020d2d2a4e8d1a374a ...

  6. Retrofit 2.0 使用详细教程

    文章来自:https://blog.csdn.net/carson_ho/article/details/73732076 前言 在Andrroid开发中,网络请求十分常用 而在Android网络请求 ...

  7. Retrofit2+Rxjava+OkHttp的使用和网络请求

    Retrofit2+Rxjava+OkHttp的使用和网络请求 https://blog.csdn.net/huandroid/article/details/79883895 加入Rxjava 如果 ...

  8. 这是一份很详细的 Retrofit 2.0 使用教程(含实例讲解)(转)

    前言 在Andrroid开发中,网络请求十分常用 而在Android网络请求库中,Retrofit是当下最热的一个网络请求库 今天,我将献上一份非常详细Retrofit v2.0的使用教程,希望你们会 ...

  9. Android 网络教程: 开始

    原文:Android Networking Tutorial: Getting Started 作者:Eunice Obugyei 译者:kmyhy 从 API 级别 1 开始,网络始终是 Andro ...

随机推荐

  1. swift3.0:CoreData的使用

    一.介绍 CoreData不像slqite3那样编写代码繁琐,同时避免了使用了SQL语句的麻烦,也可以回避使用C语言的语法,降低了iOS开发的技术门槛. CoreData可降低开发成本,提高代码质量. ...

  2. CentOS6.4下安装Chrome

    因为蛋疼的glibc问题,(我就纳闷了,为何CentOS支持的glibc的版本就那么低呢,害得我至今还没把genymotion安装上), Chrome浏览器一直没安装上,不过终于找到了解决方案,安装派 ...

  3. Libxml2函数及使用方法概述【转】

    https://blog.csdn.net/chengwenyao18/article/details/7176082 一.关于XML: 在开始研究 Libxml2 库之前,先了解一下XML的相关基础 ...

  4. jquery实现上传文件大小类型的验证

    <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content ...

  5. Search Insert Position leetcode java

    Given a sorted array and a target value, return the index if the target is found. If not, return the ...

  6. jQuery操作radio、checkbox、select 集合

    1.radio:单选框 HTML代码: <input type="radio" name="radio" id="radio1" va ...

  7. window.location属性用法及解决一个window.location.search为什么为空的问题

    通常用window.location该属性获取页面 URL 地址: 1.什么是window.location? 比如URL:http://b.a.com:88/index.php?name=kang& ...

  8. 【R】函数-字符处理函数

  9. 7.4 Javascript:表单验证-揭开正則表達式的面纱

    用元字符匹配对应的字符类型 创建正則表達式有点像创建字符串字面量,仅仅只是正則表達式出如今一对"/"里 正則表達式中会用到一级元字符.用于连接字母与数字 "." ...

  10. 【Python】列表(数组)的引用和拷贝

    # Python里对象赋值传递的引用 arr=[1,2,3,4,5] newArr=arr arr[1]=9 print('arr='+str(arr)) print('newArr='+str(ne ...