前言
现在的网络请求开源项目层出不穷,优秀者也不胜枚举。常用的Volley,OkHttp等等。层出不穷的优秀开源工具,使得开发者的开发效率大幅提高,并且更加专注于自身的业务。今天介绍一下Retrofit的基本使用方法。
声明在前
由于retrofit1.x和retrofit2.x升级变化很大,而有些教程在进行知识讲解时并未之处针对的retrofit版本,这一点很让初学者头痛。现在您将看到的是关于retrofit2.x的讲解。另外,关于retrofit1.x和retrofit2.x推荐一个不错的网站:https://futurestud.io,该网站关于retrofit1.x有详细的系列讲解,并且文章也出了retrofit2.x系列,且没有删除retrofit1.x的相关讲解。非常的不错,可惜的是英文的。retrofit2使用了其默认的okhttpClient,当然你也可以自定义client。不过此时你需要关联OkHttp。
闲聊retrofit
函数式编程使得代码更易于阅读,更加直观、简洁。retrofit也借鉴了这方面的有点。这是一个比较好的趋势。与此同时,注解在retrofit的大量应用,使得网络请求变得更加轻量级。大大减少了工作量,例如GET, POST, PUT, DELETE, 和 HEAD的请求参数都可以通过注解的方式来实现。retrofit涵盖了主要的网络请求方式。
另外,如果想要更深入的学习retrofit,对注解,反射的掌握是前提条件,如果想要更深入,还要学习一下java的动态代理技术。
retrofit的使用
首先需要将http api定义在接口中
public interface GitHubService {
@GET("users/{user}/repos")
Call<List<Repo>> listRepos(@Path("user") String user);
}
其中Get注解代表这是一个get请求,大括号{}包含了请求api的可变部分。在listRepos中通过注解指定get请求路径中的可变参数user。这是一个比较简单的api接口。稍后会列出一些混合的复杂的接口实现。List是请求回的数据的封装。
直接使用
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.build();
GitHubService service = retrofit.create(GitHubService.class);
通过被创建的GitHubService可以调用call方法实现一个同步或者异步的网络请求:
Call<List<Repo>> repos = service.listRepos("octocat");
一些复杂的请求接口
添加请求参数
@GET("group/{id}/users")
Call<List<User>> groupList(@Path("id") int groupId, @Query("sort") String sort);
添加多个请求参数
@GET("group/{id}/users")
Call<List<User>> groupList(@Path("id") int groupId, @QueryMap Map<String, String> options);
指定请求头
方式一:
@Headers({
"Accept: application/vnd.github.v3.full+json",
"User-Agent: Retrofit-Sample-App"
})
@GET("users/{username}")
Call<User> getUser(@Path("username") String username);
方式二:
@GET("user")
Call<User> getUser(@Header("Authorization") String authorization)
以上请求都是同步的;
如果使用异步网络请求,需要在以上网路接口中增加一个回调例如:
@GET("/user/{id}/photo")
void getUserPhoto(@Path("id") int id, Callback<Photo> cb);
序列化方式
默认的是RequestBody和ResponseBody
另外提供了以下converter
Gson - com.squareup.retrofit2:converter-gson
Jackson - com.squareup.retrofit2:converter-jackson
Moshi - com.squareup.retrofit2:converter-moshi
Protobuf - com.squareup.retrofit2:converter-protobuf
Wire - com.squareup.retrofit2:converter-wire
Simple Framework - com.squareup.retrofit2:converter-simpleframework
Scalars - com.squareup.retrofit2:converter-scalars
附录:官方教程
到底怎么使用Retrofit呢?
Retrofit通过注解和动态代理来进行网络请求。所以充分了解Retrofit的注解非常的重要:
上图是Retrofit中关于网络请求相关的所有注解。
例如想要添加请求头
如果你想添加请求头:
retrofit提供了HEAD和Header和Headers三个注解,供你使用。
关于HEAD:
@Documented
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface HEAD {
String value() default "";
}
请注意HEAD是用在方法的注解的。但这是请求头参数较少的请求。此外还可以使用Header
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER})
public @interface Header {
String value();
}
Header是用在参数中的。
再说说Headers上面已经提供了一个service实例。
看看源码:
@Documented
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Headers {
String[] value();
}
所以无论你的请求想要添加什么参数,先查看一下retrofit关于注解的源码:
关于@Target的类型
- CONSTRUCTOR:用于描述构造器
- FIELD:用于描述域
- LOCAL_VARIABLE:用于描述局部变量
- METHOD:用于描述方法
- PACKAGE:用于描述包
- PARAMETER:用于描述参数
- TYPE:用于描述类、接口(包括注解类型) 或enum声明
了解到这儿,至少你应该具有独立使用retrofit的能力了。
作为扩展知识,还是希望能研究一下retrofit的源码。另外推荐一下Rxjava。二者结合使用,效率更高啊!
优秀文章:
关于retrofit的一个坑爹的提示
这个提示可能称不上是bug,先来描述一下这个提示:
can't resolve host
在github的issues里面找了好久也没找到合适的答案,由于我的手机设置了代理,当我取消代理时,这个问题就解决了。真是好坑啊!!!!!!!!!!!!!!!!!
使用案例
案例1:为请求添加头
可以通过添加注解@Headers在endpoint上,也可以通过okhttp的插入器,来进行添加头。
通过@Headers来添加头
这个例子很好找,在retrofit的官方介绍中:
@Headers("Cache-Control: max-age=640000")
@GET("widget/list")
Call<List<Widget>> widgetList();
通过okhttp的插入器实现
okHttpClient.interceptors().add(new Interceptor() {
@Override
public Response intercept(Interceptor.Chain chain) throws IOException {
Request original = chain.request();
// Request customization: add request headers
Request.Builder requestBuilder = original.newBuilder()
.header("Authorization", "auth-value"); // <-- this is the important line
Request request = requestBuilder.build();
return chain.proceed(request);
}
});
不过此时要注意Request.Builder的header(key,value)和addHeader(key,value)的区别。
.header(key, val): will override preexisting headers identified by key//将会覆盖已存在的header
.addHeader(key, val): will add the header and don’t override preexisting ones//将会添加到header而不是覆盖
retrofit2的转换器转换为字符串
这是一个字符串转换器,有来做测试比较方便
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import okhttp3.MediaType;
import okhttp3.RequestBody;
import okhttp3.ResponseBody;
import retrofit2.Converter;
import retrofit2.Retrofit;
public class ToStringConverterFactory extends Converter.Factory {
private static final MediaType MEDIA_TYPE = MediaType.parse("text/plain");
@Override
public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
if (String.class.equals(type)) {
return new Converter<ResponseBody, String>() {
@Override
public String convert(ResponseBody value) throws IOException {
return value.string();
}
};
}
return null;
}
public Converter<?, RequestBody> requestBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
if (String.class.equals(type)) {
return new Converter<String, RequestBody>() {
@Override
public RequestBody convert(String value) throws IOException {
return RequestBody.create(MEDIA_TYPE, value);
}
};
}
return null;
}
}
一些未知的小事项
最好不要通过插入器修改链接,会造成一些错误的结果,例如:重复修改链接,这个问题有待查明!