引言
Retrofit
2.x,配合OkHttp
、Gson
组成Android最强网络请求框架。
正文
Retrofit
终于迎来了2.0版本,也修复了老版本的一些bug,这是修复日志,各位可以自己去看看,https://github.com/square/retrofit/blob/master/CHANGELOG.md
1.使用retrofit,需要下载一些jar包
- retrofit gitHub项目地址:https://github.com/square/retrofit
- retrofit 官网地址:http://square.github.io/retrofit/
- okHttp gitHub项目地址:https://github.com/square/okhttp
- okHttp 官网地址:http://square.github.io/okhttp/
Gradle引入依赖:
compile 'com.squareup.retrofit2:retrofit:2.4.0'
compile 'com.squareup.retrofit2:converter-gson:2.4.0'
compile 'com.squareup.retrofit2:adapter-rxjava:2.4.0'
2.介绍这些jar包的作用
我现在所用的是retrofit-2.4.0.jar ,目前的最新版本。发现retrofit改变的挺大。
(1)retrofit必须使用okhttp请求了,如果项目中没有okhttp的依赖的话,肯定会出错
(2)请求返回的解析不是默认的Gson解析了,需要我们手动去指定。
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://apis.baidu.com/showapi_open_bus/showapi_joke/")
.addConverterFactory(GsonConverterFactory.create())
.build();
这个就是Gson解析了,GsonConverterFactory是需要下载的jar。这是官方原话:
3.Retrofit的请求
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(请求地址)
.addConverterFactory(GsonConverterFactory.create())
.build();
GitHubService service = retrofit.create(GitHubService.class);
retrofit.Call<返回实体> repos = service.请求的接口名称(请求参数);
repos.enqueue(new retrofit.Callback<返回实体>() {
@Override
public void onResponse(retrofit.Response<返回实体> response, Retrofit retrofit) {
返回实体 body = response.body();
LogUtil.i(TAG, body.toString());
}
@Override
public void onFailure(Throwable throwable) {
// TODO Auto-generated method stub
}
});
4.删除一些类
(1)RequestInterceptor类
2.0中retrofit的请求拦截类被删除了,现在直接用的okhttp的请求拦截类
OkHttpClient client = new OkHttpClient();
client.interceptors().add(new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
Response response = chain.proceed(chain.request());
// Do anything with response here
return response;
}
});
// 然后传递创建的client到Retrofit的Builder链中。
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(请求地址)
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.build();
(2)删除ErrorHandler接口和RetrofitError类
没有错误的提示了,统一集中在 callback的onFailure(Throwable throwable)的异常错误中了。
使用详解
1、Retrofit入门
Retrofit
其实相当简单,简单到源码只有37个文件,其中22个文件是注解还都和HTTP有关,真正暴露给用户的类并不多,所以我看了一遍 官方教程 大多数情景就可以无障碍使用,如果你还没有看过,可以先去看看,虽然是英文,但代码才是最好的教程不是么?当然本篇文章会介绍得详细一点,不能写一篇水文,毕竟我给它命名为《你真的会用Retrofit2吗?Retrofit2完全教程》。
1.1、创建Retrofit实例
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://localhost:4567/")
.build();
创建Retrofit
实例时需要通过Retrofit.Builder
,并调用baseUrl
方法设置URL。
注1: Retrofit2 的baseUlr 必须以 /(斜线) 结束,不然会抛出一个IllegalArgumentException
,所以如果你看到别的教程没有以 / 结束,那么多半是直接从Retrofit 1.X 照搬过来的。
注2: 上面的 注1 应该描述为 baseUrl 中的路径(path)必须以 / 结束, 因为有些特殊情况可以不以/结尾,比如 其实这个 URL https://www.baidu.com?key=value
用来作为baseUrl其实是可行的,因为这个URL隐含的路径就是 /(斜线,代表根目录) ,而后面的?key=value
在拼装请求时会被丢掉所以写上也没用。之所以 Retrofit 2 在文档上要求必须以 /(斜线) 结尾的要求想必是要消除歧义以及简化规则。
1.2、接口定义.
以获取指定id的Blog为例:
public interface BlogService {
@GET("blog/{id}")
Call getBlog(@Path("id") int id);
}
注意,这里是interface
不是class
,所以我们是无法直接调用该方法,我们需要用Retrofit创建一个BlogService的代理对象。
BlogService service = retrofit.create(BlogService.class);
1.3、接口调用
Call call = service.getBlog(2);
// 用法和OkHttp的call如出一辙,
// 不同的是如果是Android系统回调方法执行在主线程
call.enqueue(new Callback() {
@Override
public void onResponse(Call call, Response response) {
try {
System.out.println(response.body().string());
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void onFailure(Call call, Throwable t) {
t.printStackTrace();
}
});
打印结果:
{"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}
2、Retrofit注解详解
上面提到Retrofit
共22个注解,这节就专门介绍这22个注解,为帮助大家更好理解我将这22个注解分为三类,并用表格的形式展现出来,表格上说得并不完整,具体的见源码上的例子注释。
第一类:HTTP请求方法
以上表格中的除HTTP以外都对应了HTTP标准中的请求方法,而HTTP注解则可以代替以上方法中的任意一个注解,有3个属性:method
、path
,hasBody
,下面是用HTTP注解实现上面 Example01.java 的例子。
public interface BlogService {
/**
* method 表示请求的方法,区分大小写
* path表示路径
* hasBody表示是否有请求体
*/
@HTTP(method = "GET", path = "blog/{id}", hasBody = false)
Call getBlog(@Path("id") int id);
}
注:method 的值 retrofit 不会做处理,所以要自行保证其准确性,之前使用小写也可以是因为示例源码中的服务器不区分大小写,所以希望大家注意。
示例源码见 Example02.java
第二类:标记类
示例源码见 Example03.java
第三类:参数类
注1:{占位符}和PATH尽量只用在URL的path部分,url中的参数使用Query和QueryMap 代替,保证接口定义的简洁
注2:Query、Field和Part这三者都支持数组和实现了Iterable接口的类型,如List,Set等,方便向后台传递数组。
Call foo(@Query("ids[]") List ids);
//结果: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
的话何必要引入泛型呢,
返回值直接用一个Call
就行了嘛,既然支持泛型,那说明泛型参数可以是其它类型的,
而Converter
就是Retrofit
为我们提供用于将ResponseBody
转换为我们想要的类型,
有了Converter之后我们就可以写把我们的第一个例子的接口写成这个样子了:
public interface BlogService {
@GET("blog/{id}")
Call> getBlog(@Path("id") int id);
}
当然只改变泛型的类型是不行的,我们在创建Retrofit时需要明确告知用于将ResponseBody
转换我们泛型中的类型时需要使用的Converter
引入Gson支持:
compile 'com.squareup.retrofit2:converter-gson:2.4.0'
通过GsonConverterFactory为Retrofit添加Gson支持:
Gson gson = new GsonBuilder()
//配置你的Gson
.setDateFormat("yyyy-MM-dd hh:mm:ss")
.create();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://localhost:4567/")
//可以接收自定义的Gson,当然也可以不传
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
示例源码见 Example06.java
这样Retrofit就会使用Gson将ResponseBody
转换我们想要的类型。
这是时候我们终于可以演示如使创建一个Blog了!
@POST("blog")
Call> createBlog(@Body Blog blog);
被@Body
注解的的Blog将会被Gson
转换成RequestBody
发送到服务器。
BlogService service = retrofit.create(BlogService.class);
Blog blog = new Blog();
blog.content = "新建的Blog";
blog.title = "测试";
blog.author = "怪盗kidou";
Call> call = service.createBlog(blog);
结果:
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
是对于CallCallAdapter
则可以对Call转换,这样的话CallObservable
代替Call
:
引入RxJava支持:
compile 'com.squareup.retrofit2:adapter-rxjava:2.0.2'
// 针对rxjava2.x(adapter-rxjava2的版本要 >= 2.2.0)
compile 'com.squareup.retrofit2:adapter-rxjava2:2.3.0'
通过RxJavaCallAdapterFactory为Retrofit添加RxJava支持:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://localhost:4567/")
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
// 针对rxjava2.x
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
接口设计:
public interface BlogService {
@POST("/blog")
Observable>> getBlogs();
}
使用:
BlogService service = retrofit.create(BlogService.class);
service.getBlogs(1)
.subscribeOn(Schedulers.io())
.subscribe(new Subscriber>>() {
@Override
public void onCompleted() {
System.out.println("onCompleted");
}
@Override
public void onError(Throwable e) {
System.err.println("onError");
}
@Override
public void onNext(Result> blogsResult) {
System.out.println(blogsResult);
}
});
结果:
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
「补充」:像上面的这种情况最后我们无法获取到返回的Header和响应码的,如果我们需要这两者,提供两种方案:
1、用Observable
代替 Observable
,这里的Response
指retrofit2.Response
2、用Observable
代替 Observable
,这里的Result
是指retrofit2.adapter.rxjava.Result
,这个Result中包含了Response
的实例
5、自定义Converter
本节的内容是教大家实现在一简易的Converter,这里以返回格式为Call
在此之前先了解一下Converter接口及其作用:
public interface Converter {
// 实现从 F(rom) 到 T(o)的转换
T convert(F value) throws IOException;
// 用于向Retrofit提供相应Converter的工厂
abstract class Factory {
// 这里创建从ResponseBody其它类型的Converter,如果不能处理返回null
// 主要用于对响应体的处理
public Converter responseBodyConverter(Type type, Annotation[] annotations,
Retrofit retrofit) {
return null;
}
// 在这里创建 从自定类型到ResponseBody 的Converter,不能处理就返回null,
// 主要用于对Part、PartMap、Body注解的处理
public Converter, RequestBody> requestBodyConverter(Type type,
Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
return null;
}
// 这里用于对Field、FieldMap、Header、Path、Query、QueryMap注解的处理
// Retrfofit对于上面的几个注解默认使用的是调用toString方法
public Converter, String> stringConverter(Type type, Annotation[] annotations,
Retrofit retrofit) {
return null;
}
}
}
我们要想从Call
转换为 Call
那么对应的F
和T
则分别对应ResponseBody
和String
,我们定义一个StringConverter
并实现Converter
接口。
public static class StringConverter implements Converter {
public static final StringConverter INSTANCE = new StringConverter();
@Override
public String convert(ResponseBody value) throws IOException {
return value.string();
}
}
我们需要一个Fractory
来向Retrofit
注册StringConverter
public static class StringConverterFactory extends Converter.Factory {
public static final StringConverterFactory INSTANCE = new StringConverterFactory();
public static StringConverterFactory create() {
return INSTANCE;
}
// 我们只关实现从ResponseBody 到 String 的转换,所以其它方法可不覆盖
@Override
public Converter responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
if (type == String.class) {
return StringConverter.INSTANCE;
}
//其它类型我们不处理,返回null就行
return null;
}
}
使用Retrofit.Builder.addConverterFactory向Retrofit注册我们StringConverterFactory:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://localhost:4567/")
// 如是有Gson这类的Converter 一定要放在其它前面
.addConverterFactory(StringConverterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.build();
注:addConverterFactory
是有先后顺序的,如果有多个ConverterFactory都支持同一种类型,那么就是只有第一个才会被使用,而GsonConverterFactory
是不判断是否支持的,所以这里交换了顺序还会有一个异常抛出,原因是类型不匹配。
只要返回值类型的泛型参数就会由我们的StringConverter
处理,不管是Call
还是Observable
有没有很简单?如果你有其它的需求处理的就自己实现吧。
示例源码见 Example09.java
6、自定义CallAdapter
本节将介绍如何自定义一个CallAdapter
,并验证是否所有的String都会使用我们第5节中自定义的Converter。
先看一下CallAdapter接口定义及各方法的作用:
public interface CallAdapter {
// 直正数据的类型 如Call 中的 T
// 这个 T 会作为Converter.Factory.responseBodyConverter 的第一个参数
// 可以参照上面的自定义Converter
Type responseType();
T adapt(Call call);
// 用于向Retrofit提供CallAdapter的工厂类
abstract class Factory {
// 在这个方法中判断是否是我们支持的类型,returnType 即Call和`Observable`
// RxJavaCallAdapterFactory 就是判断returnType是不是Observable> 类型
// 不支持时返回null
public abstract CallAdapter> get(Type returnType, Annotation[] annotations,
Retrofit retrofit);
// 用于获取泛型的参数 如 Call 中 Requestbody
protected static Type getParameterUpperBound(int index, ParameterizedType type) {
return Utils.getParameterUpperBound(index, type);
}
// 用于获取泛型的原始类型 如 Call 中的 Call
// 上面的get方法需要使用该方法。
protected static Class> getRawType(Type type) {
return Utils.getRawType(type);
}
}
}
了解了CallAdapter
的结构和其作用之后,我们就可以开始自定义我们的CallAdapter
了,本节以CustomCall
为例。
在此我们需要定义一个CustomCall,不过这里的CustomCall作为演示只是对Call的一个包装,并没有实际的用途。
public static class CustomCall {
public final Call call;
public CustomCall(Call call) {
this.call = call;
}
public R get() throws IOException {
return call.execute().body();
}
}
有了CustomCall,我们还需要一个CustomCallAdapter来实现Call
到 CustomCall
的转换,这里需要注意的是最后的泛型,是我们要返回的类型。
public static class CustomCallAdapter implements CallAdapter> {
private final Type responseType;
// 下面的 responseType 方法需要数据的类型
CustomCallAdapter(Type responseType) {
this.responseType = responseType;
}
@Override
public Type responseType() {
return responseType;
}
@Override
public CustomCall adapt(Call call) {
// 由 CustomCall 决定如何使用
return new CustomCall<>(call);
}
}
提供一个CustomCallAdapterFactory用于向Retrofit提供CustomCallAdapter:
public static class CustomCallAdapterFactory extends CallAdapter.Factory {
public static final CustomCallAdapterFactory INSTANCE = new CustomCallAdapterFactory();
@Override
public CallAdapter> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
// 获取原始类型
Class> rawType = getRawType(returnType);
// 返回值必须是CustomCall并且带有泛型
if (rawType == CustomCall.class && returnType instanceof ParameterizedType) {
Type callReturnType = getParameterUpperBound(0, (ParameterizedType) returnType);
return new CustomCallAdapter(callReturnType);
}
return null;
}
}
使用addCallAdapterFactory向Retrofit注册CustomCallAdapterFactory
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://localhost:4567/")
.addConverterFactory(Example09.StringConverterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(CustomCallAdapterFactory.INSTANCE)
.build();
注: addCallAdapterFactory
与addConverterFactory
同理,也有先后顺序。
示例源码见 Example10.java
7、其它说明
7.1 Retrofit.Builder
前面用到了Retrofit.Builder
中的baseUrl
、addCallAdapterFactory
、addConverterFactory
、build
方法,还有callbackExecutor
、callFactory
、client
、validateEagerly
这四个方法没有用到,这里简单的介绍一下。
方法 | 用途 |
---|---|
callbackExecutor(Executor) | 指定Call.enqueue时使用的Executor,所以该设置只对返回值为Call的方法有效 |
callFactory(Factory) | 设置一个自定义的okhttp3.Call.Factory,那什么是Factory呢?OkHttpClient就实现了okhttp3.Call.Factory接口,下面的client(OkHttpClient)最终也是调用了该方法,也就是说两者不能共用 |
client(OkHttpClient) | 设置自定义的OkHttpClient ,以前的Retrofit版本中不同的Retrofit 对象共用同OkHttpClient ,在2.0各对象各自持有不同的OkHttpClient 实例,所以当你需要共用OkHttpClient 或需要自定义时则可以使用该方法,如:处理Cookie、使用stetho 调式等 |
validateEagerly(boolean) | 是否在调用create(Class)时检测接口定义是否正确,而不是在调用方法才检测,适合在开发、测试时使用 |
7.2 Retrofit的Url组合规则
BaseUrl | 和URL有关的注解中提供的值 | 最后结果 |
---|---|---|
http://localhost:4567/path/to/other/ | /post | http://localhost:4567/post |
http://localhost:4567/path/to/other/ | post | http://localhost:4567/path/to/other/post |
http://localhost:4567/path/to/other/ | https://github.com/ikidou | https://github.com/ikidou |
从上面不能难看出以下规则:
- 如果你在注解中提供的url是完整的url,则url将作为请求的url。
- 如果你在注解中提供的url是不完整的url,且不以 / 开头,则请求的url为baseUrl+注解中提供的值
- 如果你在注解中提供的url是不完整的url,且以 / 开头,则请求的url为baseUrl的主机部分+注解中提供的值
Converter | Gradle依赖 |
---|---|
Gson | com.squareup.retrofit2:converter-gson:2.0.2 |
Jackson | com.squareup.retrofit2:converter-jackson:2.0.2 |
Moshi | com.squareup.retrofit2:converter-moshi:2.0.2 |
Protobuf | com.squareup.retrofit2:converter-protobuf:2.0.2 |
Wire | com.squareup.retrofit2:converter-wire:2.0.2 |
Simple XML | com.squareup.retrofit2:converter-simplexml:2.0.2 |
Scalars | com.squareup.retrofit2:converter-scalars:2.0.2 |
7.3 Retrofit提供的Converter
Converter | Gradle依赖 |
---|---|
Gson | com.squareup.retrofit2:converter-gson:2.0.2 |
Jackson | com.squareup.retrofit2:converter-jackson:2.0.2 |
Moshi | com.squareup.retrofit2:converter-moshi:2.0.2 |
Protobuf | com.squareup.retrofit2:converter-protobuf:2.0.2 |
Wire | com.squareup.retrofit2:converter-wire:2.0.2 |
Simple XML | com.squareup.retrofit2:converter-simplexml:2.0.2 |
Scalars | com.squareup.retrofit2:converter-scalars:2.0.2 |
7.4 Retrofit提供的CallAdapter:
CallAdapter | Gradle依赖 |
---|---|
guava | com.squareup.retrofit2:adapter-guava:2.0.2 |
Java8 | com.squareup.retrofit2:adapter-java8:2.0.2 |
rxjava | com.squareup.retrofit2:adapter-rxjava:2.0.2 |
总结
搞了这么多,终于可以使用Retrofit
+OkHttp
+Gson
超级网络请求框架了,网上说目前这个是最快的网络请求,比google的volley还快点,至于真假,就不知道了,但是不可否认,确实挺好用的,效率也是杠杠的。
就单独的来说,OkHttp的网络请求也是很好用的,推荐文章鸿神的http://blog.csdn.net/lmj623565791/article/details/47911083
可惜单独的Retrofit不能网络请求了,感觉这是2.0最不爽的地方了。
当然组合使用应该是更爽的吧。
推荐
这是一份很详细的 Retrofit 2.0 使用教程(含实例讲解)