开源库地址:https://github.com/square/retrofit
解读版本:2.1.0
基本概念
Retrofit 是一个针对Java/Android类型安全的Http请求客户端。
基本使用如下:
- 首先定义一个接口,抽象方法的返回值必须为
Call<XX>
。public interface GitHubService { @GET("users/{user}/repos") Call<List<Repo>> listRepos(@Path("user") String user);//默认CallAdapter返回值必须为`Call<XX>`。 }
- 通过Retrofit的
create
创建接口实现//初始化Retrofit Retrofit retrofit = new Retrofit.Builder() .baseUrl("https://api.github.com/") //必须以斜杠结尾 .addConverterFactory(GsonConverterFactory.create())//添加Gson转换工厂 .build(); //通过Retrofit创建接口实现 GitHubService service = retrofit.create(GitHubService.class);
- 接口调用相关方法获取
Call<XX>
,然后就能和Okhttp一样进行同步/异步调用。Call<List<Repo>> repos = service.listRepos("octocat"); //异步调用 repos.enqueue(new Callback<List<Repo>>() { @Override public void onResponse(Call<List<Repo>> call, Response<List<Repo>> response) { List<String> list=response.body();//调用body() } @Override public void onFailure(Call<List<Repo>> call, Throwable t) { } }); //同步调用 Response<List<Repo>> response=repos.execute();
源码解读
Retrofit中的相关注解
请求方法注解
该类注解用于指明请求方法,在使用时必须指定且只可指定一种请求方法。
- @GET
用来指明请求方法和相对/绝对/完整路径。当然也可以配合@Url使用。
public interface RequestService { @GET("xx/xxx") //指定相对路径 Call<ResponseBody> getContent1(); @GET("/xxx") //指定绝对路径,相对路径和绝对路径的区别见后文 Call<ResponseBody> getContent1(); @GET("http://www.baidu.com") //指定完整Url Call<ResponseBody> getContent2(); @GET //配合@Url使用,此时@GET只能指明请求方法,路径参数必须留空 Call<ResponseBody> getContent3(@Url String url); }
- @POST
用来指明请求方法和相对/绝对/完整路径,同时也可以配合@Url使用。
public interface RequestService { @POST("xx/xxx") //指定相对路径 Call<ResponseBody> getContent1(); @POST("/xxx") //指定绝对路径 Call<ResponseBody> getContent1(); @POST("http://www.baidu.com") //指定完整Url Call<ResponseBody> getContent2(); @POST //配合@Url使用,此时@POST只能指明请求方法,路径参数必须留空 Call<ResponseBody> getContent3(@Url String url); }
- @DELETE,@HEAD,@OPTIONS,@PATCH,@PUT
这几种请求方法和POST/GET在使用上没有太大区别,由于不太常用,这里就不做介绍。
请求参数注解
- @Path
该注解用于替换Url的路径(path)部分。
public interface RequestService { @GET("/image/{id}") //替换{}内的路径 Call<ResponseBody> getImage(@Path("id") int id); @GET("/user/{name}") //替换{}内的路径 Call<ResponseBody> getUserInfo(@Path("name") String name); @GET("/user/{name}") //替换{}内的路径,encoded=true则表示自己处理编码, Call<ResponseBody> getUserInfo(@Path(value="name", encoded=true) String name); }
上述代码有一个是否需要编码的问题,这里举个例子:假如输入名字
maple+jaw
,默认情况下为GET中的注解为/user/maple%2Bjaw
,如果指明自己处理编码,此时输入maple+jaw
,路径参数为/user/maple+jaw
- @Query
该注解用于在Url后追加查询参数
public interface RequestService { @GET("/list") //追加查询参数后,此时路径为/list?page=xx Call<ResponseBody> getList(@Query("page") int page); @GET("/list") //追加查询参数后,此时路径为/list?page=xx&category=xx Call<ResponseBody> getList(@Query("page") int page,@Query("category") String category); @GET("/list") //同理也可指定是否编码 Call<ResponseBody> getList(@Query(value="category",encoded=true) String category); }
- @QueryMap
@QueryMap和@Query的作用是一样的,只不过@QueryMap是以map的形式追加参数,可以一次性追加多个参数
public interface RequestService { @GET("/search") Call<ResponseBody> getList(@QueryMap Map<String, String> filters); }
- @Field
该注解用于添加表单数据,须配合
@FormUrlEncoded
一起使用。public interface RequestService { @FormUrlEncoded @POST("/register") //使用@Field提交表单数据 Call<ResponseBody> register(@Field("name") String name,@Field("password") String pwd,); @FormUrlEncoded @POST("/register") //使用@Field提交表单数据,encoded=true表示该数据自己编码 Call<ResponseBody> register(@Field(value="name",encoded=true) String name,@Field("password") String pwd,); }
- @FieldMap
@Field和@FieldMap的作用一样,后者以map的方式提交表单数据
public interface RequestService { @FormUrlEncoded @POST("/register") //使用@FieldMap提交表单数据 Call<ResponseBody> register(@FieldMap Map<String, String> fields); }
- @Header
该注解用于指明请求头参数,相同名字的请求头是惟一的,新的会直接覆盖旧的。
public interface RequestService { @GET("/") Call<ResponseBody> getContent(@Header("Accept-Language") String lang ,@Header("Cache-Control") String cache); }
- @Headers
该注解用于指明请求头参数,和@Header的区别在于不会覆盖掉相同名字的请求参数
public interface RequestService { @Headers("Cache-Control: max-age=640000") @GET("/") Call<ResponseBody> getContent(); @Headers({"Cache-Control: max-age=640000","Accept-Language: gzip, deflate"}) @GET("/") Call<ResponseBody> getContent(); }
- @HeaderMap
@HeaderMap和@Headers的作用一样,区别在于以Map的形式指明请求头参数。
public interface RequestService { @GET("/search") Call<ResponseBody> getList(@HeaderMap Map<String, String> headers); }
- @Part
该注解用于定义分块表单请求中的块。需配合@Multipart一起使用。
如果类型是MultipartBody.Part,内容将直接被使用。(
@Part MultipartBody.Part part
)如果类型是RequestBody,将会和它的contentType一起使用。(
@Part("foo") RequestBody foo
)其他类型将会通过转换器转换后使用。(
@Part("foo") Image photo
)@Part(XX),XX表示表单名。
public interface RequestService { @Multipart @POST("/") Call<ResponseBody> example(@Part("description") String description, @Part(value = "image", encoding = "8-bit") RequestBody image); }
- @PartMap
和@Part的作用一样,区别在于以Map的形式定义,其中Map的Value类型转换同@Part。
public interface RequestService { @Multipart @POST("/upload") Call<ResponseBody> example(@Part("file\"; filename=\"test.txt") RequestBody file, @PartMap Map<String, RequestBody> params); }
- @Body
当请求体不是表单/分块类型时,该注解用于直接指定请求体。请求体内部转换时通过
Converter
转换为RequestBody
实现的。public interface RequestService { @POST("/upload") Call<ResponseBody> example(@Body String conent); }
- @HTTP
该注解用于自定义请求。
interface Service { @HTTP(method = "DELETE", path = "remove/", hasBody = true) Call<ResponseBody> deleteObject(@Body RequestBody object); @HTTP(method = "CUSTOM", path = "custom/endpoint/") Call<ResponseBody> customEndpoint(); }
- @Streaming
使用该注解后,将不会缓冲流到内存中,只在返回类型为
Call<ResponseBody>
时起效。如果使用Retrofit下载大文件,务必要加上该注解。
Retrofit构造
按照老规矩,从Retrofit的构造方法下手,要想了解Retrofit的构造方法,必须先了解以下几个类
Call接口
注意,此Call非Okhttp中的Call,结构如下,通过Call可以进行同步和异步请求,还能判断取消与否。
CallAdapter
CallAdapter主要用来代理call(默认为OkhttpCall)以方便实现自己想要的功能,比如RxJavaCallAdapter
等。可以通过Retrofit.Builder#addCallAdapterFactory(Factory)
来添加CallAdapterFactory。
public interface CallAdapter<T> {
//返回响应类型,即Call<Repo>的响应类型为Repo
Type responseType();
//返回一个代理call的实例。
<R> T adapt(Call<R> call);
//..
//省略了工厂源码,用于获取CallAdapter的实例
}
Callback
Callback用于回调结果。
public interface Callback<T> {
//接收HTTP响应(404,500也会调用,所以需要调用isSuccessful来判断)
void onResponse(Call<T> call, Response<T> response);
//网络异常或者发生异常时调用
void onFailure(Call<T> call, Throwable t);
}
Converter
用于类型转换,使用Retrofit.Builder#addConverterFactory(Factory)
添加转换工厂,比如添加Gson转换。
public interface Converter<F, T> {
T convert(F value) throws IOException;
//..
//省略了工厂源码
}
构造方法
Retrofit构造方法采用构建者模式构造,源码如下。
private final Map<Method, ServiceMethod> serviceMethodCache = new LinkedHashMap<>();//key为接口中定义方法,value为转换过后的方法
public static final class Builder {
private Platform platform;//平台:安卓、java等
private okhttp3.Call.Factory callFactory; //okhttp的Call工厂类,自定义newCall将Request转为Call
private HttpUrl baseUrl;//okhttp中的类,保存解析过的url
private List<Converter.Factory> converterFactories = new ArrayList<>();//类型转换工厂列表。
private List<CallAdapter.Factory> adapterFactories = new ArrayList<>();//CallAdapter工厂列表。
private Executor callbackExecutor;//回调线程池
private boolean validateEagerly;//急需验证?作用在于直接将所有方法加入前面的map缓存中。
Builder(Platform platform) {
this.platform = platform;
converterFactories.add(new BuiltInConverters());//添加默认的转换器
}
public Builder() {
this(Platform.get());//通过Platform.get()获取关于当前平台的实现
}
public Retrofit build() {
if (baseUrl == null) {
throw new IllegalStateException("Base URL required.");
}
okhttp3.Call.Factory callFactory = this.callFactory;
if (callFactory == null) {
callFactory = new OkHttpClient();//默认使用OkHttpClient
}
Executor callbackExecutor = this.callbackExecutor;
if (callbackExecutor == null) {//默认使用平台默认回调线程池
callbackExecutor = platform.defaultCallbackExecutor();
}
//将平台默认CallAdapter.Factory加入列表中
List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories);
adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));
//将默认Converter.Factory加入列表中
List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories);
return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
callbackExecutor, validateEagerly);
}
可以看出,首选通过Platform.get()
来获取平台实现然后添加了默认转换工厂。由于我们是Android平台,毋容置疑,接下来就去看看Andoird类的源码。
static class Android extends Platform {
//回调线程池
@Override public Executor defaultCallbackExecutor() {
return new MainThreadExecutor();//主线程
}
@Override CallAdapter.Factory defaultCallAdapterFactory(Executor callbackExecutor) {
//ExecutorCallAdapterFactory继承CallAdapter.Factory,内部代理了原来的Call<T>,用于将Callback回调到指定线程中。
return new ExecutorCallAdapterFactory(callbackExecutor);
}
static class MainThreadExecutor implements Executor {
private final Handler handler = new Handler(Looper.getMainLooper());
@Override public void execute(Runnable r) {
handler.post(r);//采用Hanlder#post回调到主线程
}
}
}
Andoird类的源码主要实现了两个方法,一个是实现了默认的回调线程池,用于在主线程中执行任务,另一个是实现了CallAdapter工厂,通过代理的方式,将执行结果回调到callbackExecutor中去执行。所以,我们只需将callbackExecutor赋值为MainThreadExecutor即可实现主线程间的回调。
BuiltInConverters继承于Converter.Factory,Converter.Factory中有三个方法:
public Converter<ResponseBody, ?> responseBodyConverter(xx)
用于将ResponseBody转换为指定类型,通常用于对响应结果的类型转换。public Converter<?, RequestBody> requestBodyConverter(xx)
用于将指定类型转为RequestBody。一般用于将@Body,@Part,@PartMap
转为RequestBodypublic Converter<?, String> stringConverter
用于将指定类型转为String,用于将@Field,@FieldMap,@Path,@Query,@Header
等注解的参数类型转为String。
BuiltInConverters源码实现如下:
@Override
public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
Retrofit retrofit) {
//根据类型进行选择不同的转换器
if (type == ResponseBody.class) {//转换类型为ResponseBody
if (Utils.isAnnotationPresent(annotations, Streaming.class)) {
return StreamingResponseBodyConverter.INSTANCE;//如果使用了Steaming注解,就不缓冲到内存,直接返回OkHttp的ResponseBody。
}
return BufferingResponseBodyConverter.INSTANCE;//缓冲ResponseBody到内存后返回
}
if (type == Void.class) {
return VoidResponseBodyConverter.INSTANCE;//Void就直接关闭ResponseBody
}
return null;
}
@Override
public Converter<?, RequestBody> requestBodyConverter(Type type,
Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
if (RequestBody.class.isAssignableFrom(Utils.getRawType(type))) {//默认只处理RequestBody相关类型
return RequestBodyConverter.INSTANCE;
}
return null;
}
@Override public Converter<?, String> stringConverter(Type type, Annotation[] annotations,
Retrofit retrofit) {
if (type == String.class) {//默认只处理String类型的转换,说白了就是直接返回
return StringConverter.INSTANCE;
}
return null;
}
//..
//省略了部分源码
}
BuiltInConverters源码中最重要的在于responseBodyConverter
方法,如果转换类型是ResponseBody且有@Streaming注解,那么将不会缓冲到内存。你可能会疑问两者有什么区别?那么我们想象一种情景——下载大文件。如果下载大文件时将ResponseBody缓冲到内存,OOM那是分分钟的事。
Retrofit提供的用于自定义的方法如下:
client(OkHttpClient)
用于自定义客户端callFactory(okhttp3.Call.Factory factory)
用于自定义Call工厂,重写newCall将Request转为Call。OkHttpClient就是实现了这个接口。addConverterFactory
添加类型转换工厂(Gson转换等)addCallAdapterFactory
添加CallAdapter代理工厂,用来代理原始的Call(RxJavaCallAdapter等)。callbackExecutor
自定义回调线程池,默认为主线程validateEagerly
是否继续验证,是就提前将所有方法转为ServiceMethod放入缓存中,而不是调用一个缓存一个baseUrl
用于定义基本链接,必须以”/”结尾
假如基本地址为http://example.com/api/
,关于baseUrl与注解中路径的拼接问题如下:
注解中的路径 | 最终Url (baseUrl为http://example.com/api/) |
---|---|
foo/bar/ | http://example.com/api/foo/bar/ |
/foo/bar/ | http://example.com/foo/bar/ |
https://github.com/square/retrofit/ | https://github.com/square/retrofit/ |
//github.com/square/retrofit/ | http://github.com/square/retrofit/ |
创建Service接口
介绍完构造方法后,我们再来看看retrofit.create(XX.class);
这个方法。
public <T> T create(final Class<T> service) {
Utils.validateServiceInterface(service);//验证接口,接口不能有继承
if (validateEagerly) {
eagerlyValidateMethods(service);//将所有方法(跳过默认方法)转为ServiceMethod放入map中
}
//使用代理
return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
new InvocationHandler() {
private final Platform platform = Platform.get();//获取平台
@Override public Object invoke(Object proxy, Method method, Object... args)
throws Throwable {
//代理调用
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
//JAVA8接口可以定义默认方法
if (platform.isDefaultMethod(method)) {
return platform.invokeDefaultMethod(method, service, proxy, args);
}
//直接看这里
//将普通的Method转为ServiceMethod
ServiceMethod serviceMethod = loadServiceMethod(method);
//将ServiceMethod以及传入的参数值传入OkHttpCall。
OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
//利用callAdapter代理OkHttpCall,默认直接返回。
return serviceMethod.callAdapter.adapt(okHttpCall);
}
});
}
retrofit.create(XX.class);
方法中使用了Java代理,核心在于,首先将普通的Method转为ServiceMethod,然后将ServiceMethod以及传入的参数值传入到OkHttpCall中,最终通过代理OkHttpCall来返回代理Call,默认是直接返回。
ServiceMethod的创建
接下来,我们来看看是怎样将Method转为ServiceMethod的,源码在loadServiceMethod
方法中:
ServiceMethod loadServiceMethod(Method method) {
ServiceMethod result;
synchronized (serviceMethodCache) {
result = serviceMethodCache.get(method);//首先从map中取看看是否已经缓存过
if (result == null) {//否则构造ServiceMethod。
result = new ServiceMethod.Builder(this, method).build();
serviceMethodCache.put(method, result);//缓存map中
}
}
return result;
}
loadServiceMethod
的方法很简陋,直接传入了Method然后build()
,那么这个build()
又做了什么?
public Builder(Retrofit retrofit, Method method) {
this.retrofit = retrofit;
this.method = method;
this.methodAnnotations = method.getAnnotations();//赋值方法注解数组
this.parameterTypes = method.getGenericParameterTypes();//赋值参数类型数组
this.parameterAnnotationsArray = method.getParameterAnnotations();//赋值参数注解数组
}
public ServiceMethod build() {
callAdapter = createCallAdapter();//创建CallAdapter,用来代理Call
responseType = callAdapter.responseType();//获取返回类型
//...
responseConverter = createResponseConverter();//创建ResponseConverter,用来转换ResponseBody为指定类型
for (Annotation annotation : methodAnnotations) {
parseMethodAnnotation(annotation);//遍历解析方法注解
}
//...
int parameterCount = parameterAnnotationsArray.length;//parameterAnnotationsArray为参数注解数组
parameterHandlers = new ParameterHandler<?>[parameterCount];//初始化ParameterHandler,用来处理参数相关
for (int p = 0; p < parameterCount; p++) {//遍历参数注解数组
Type parameterType = parameterTypes[p];//获取参数类型
Annotation[] parameterAnnotations = parameterAnnotationsArray[p];//获取参数注解数组
//...
//通过注解和参数类型,解析并赋值到parameterHandlers中
parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);
}
//...
return new ServiceMethod<>(this);
}
createCallAdapter
用于创建CallAdapter。最终的源码在Retrofit类中,从源码可以看出,进行遍历源码,如果查询到就返回。
public CallAdapter<?> nextCallAdapter(CallAdapter.Factory skipPast, Type returnType,
Annotation[] annotations) {
checkNotNull(returnType, "returnType == null");
checkNotNull(annotations, "annotations == null");
int start = adapterFactories.indexOf(skipPast) + 1;
for (int i = start, count = adapterFactories.size(); i < count; i++) {
//遍历Adapter集合,查到就返回
CallAdapter<?> adapter = adapterFactories.get(i).get(returnType, annotations, this);
if (adapter != null) {
return adapter;
}
}
//..
//省略了部分源码
}
同理,createResponseConverter
也是。
public <T> Converter<ResponseBody, T> nextResponseBodyConverter(Converter.Factory skipPast,
Type type, Annotation[] annotations) {
checkNotNull(type, "type == null");
checkNotNull(annotations, "annotations == null");
int start = converterFactories.indexOf(skipPast) + 1;
for (int i = start, count = converterFactories.size(); i < count; i++) {
//遍历调用查询
Converter<ResponseBody, ?> converter =
converterFactories.get(i).responseBodyConverter(type, annotations, this);
if (converter != null) {
return (Converter<ResponseBody, T>) converter;
}
}
//..
//省略了部分源码
}
parseMethodAnnotation
用于遍历解析方法上的注解,比如请求方法,请求头之类的。
private void parseMethodAnnotation(Annotation annotation) {
//请求方法注解
if (annotation instanceof DELETE) {
parseHttpMethodAndPath("DELETE", ((DELETE) annotation).value(), false);
} else if (annotation instanceof GET) {
parseHttpMethodAndPath("GET", ((GET) annotation).value(), false);
} else if (annotation instanceof HEAD) {
parseHttpMethodAndPath("HEAD", ((HEAD) annotation).value(), false);
if (!Void.class.equals(responseType)) {
throw methodError("HEAD method must use Void as response type.");
}
} else if (annotation instanceof PATCH) {
parseHttpMethodAndPath("PATCH", ((PATCH) annotation).value(), true);
} else if (annotation instanceof POST) {
parseHttpMethodAndPath("POST", ((POST) annotation).value(), true);
} else if (annotation instanceof PUT) {
parseHttpMethodAndPath("PUT", ((PUT) annotation).value(), true);
} else if (annotation instanceof OPTIONS) {
parseHttpMethodAndPath("OPTIONS", ((OPTIONS) annotation).value(), false);
} else if (annotation instanceof HTTP) {
//自定义HTTP请求注解
HTTP http = (HTTP) annotation;
parseHttpMethodAndPath(http.method(), http.path(), http.hasBody());
} else if (annotation instanceof retrofit2.http.Headers) {
//请求头注解
String[] headersToParse = ((retrofit2.http.Headers) annotation).value();
if (headersToParse.length == 0) {
throw methodError("@Headers annotation is empty.");
}
headers = parseHeaders(headersToParse);//解析Header
} else if (annotation instanceof Multipart) {
//Multipart
if (isFormEncoded) {
throw methodError("Only one encoding annotation is allowed.");
}
isMultipart = true;
} else if (annotation instanceof FormUrlEncoded) {
//FormUrlEncoded
if (isMultipart) {
throw methodError("Only one encoding annotation is allowed.");
}
isFormEncoded = true;
}
}
从上面源码可以看出,使用parseHttpMethodAndPath这个方法用于解析请求方法注解和路径参数保存到Set中
private void parseHttpMethodAndPath(String httpMethod, String value, boolean hasBody) {
if (this.httpMethod != null) {
throw methodError("Only one HTTP method is allowed. Found: %s and %s.",
this.httpMethod, httpMethod);
}
this.httpMethod = httpMethod;
this.hasBody = hasBody;
if (value.isEmpty()) {
return;
}
int question = value.indexOf(‘?‘);//查询参数开始的符号
if (question != -1 && question < value.length() - 1) {
//如果在查询参数中使用了{},则抛出异常。
String queryParams = value.substring(question + 1);
Matcher queryParamMatcher = PARAM_URL_REGEX.matcher(queryParams);
if (queryParamMatcher.find()) {
throw methodError("URL query string \"%s\" must not have replace block. "
+ "For dynamic query parameters use @Query.", queryParams);
}
}
//赋值相对链接
this.relativeUrl = value;
//解析{}路径参数保存到Set中
this.relativeUrlParamNames = parsePathParameters(value);
}
从源码可以发现,不允许在查询参数中使用{}
进行占位,否则就会抛出异常,然后将请求方法中的注解值赋值给relativeUrl,通过parsePathParameters
将{}路径参数保存到Set中。
通过parseHeaders
来解析头部注解。
private Headers parseHeaders(String[] headers) {
Headers.Builder builder = new Headers.Builder();
for (String header : headers) {
int colon = header.indexOf(‘:‘);
if (colon == -1 || colon == 0 || colon == header.length() - 1) {
throw methodError(
"@Headers value must be in the form \"Name: Value\". Found: \"%s\"", header);
}
String headerName = header.substring(0, colon);
String headerValue = header.substring(colon + 1).trim();
if ("Content-Type".equalsIgnoreCase(headerName)) {
MediaType type = MediaType.parse(headerValue);
if (type == null) {
throw methodError("Malformed content type: %s", headerValue);
}
contentType = type;
} else {
builder.add(headerName, headerValue);
}
}
return builder.build();
}
看完解析方法注解后,现在来看下是如何解析参数相关注解的。源码在parseParameter
中
private ParameterHandler<?> parseParameter(
int p, Type parameterType, Annotation[] annotations) {
ParameterHandler<?> result = null;
for (Annotation annotation : annotations) {//遍历参数注解
ParameterHandler<?> annotationAction = parseParameterAnnotation(
p, parameterType, annotations, annotation);//解析参数注解
if (annotationAction == null) {
continue;
}
if (result != null) {
//一个参数中只允许使用一个注解,否则抛出异常
throw parameterError(p, "Multiple Retrofit annotations found, only one allowed.");
}
result = annotationAction;
}
if (result == null) {
throw parameterError(p, "No Retrofit annotation found.");
}
return result;
}
可以看出,内部调用了parseParameterAnnotation
来解析参数注解,此外,还进行了相关判断,虽然参数中可以使用多个注解,但Retrofit强制用户只能使用一个,否则抛出异常。
parseParameterAnnotation
中的源码实在太长了,这里就简单介绍下工作流程。首先根据注解来判断来校验使用上有没有错误,比如@Query注解必须在@Path和@Url后使用,使用了@Url注解那么请求方法注解中不允许设置请求路径等等;然后获取相应Converter用于转换类型(String,ResponseBody),最后初始化相应ParameterHandler
返回。
ParameterHandler
这个抽象类用于处理参数,还内置了不同类型的参数转换类,源码如下:
abstract class ParameterHandler<T> {
abstract void apply(RequestBuilder builder, T value) throws IOException;
//Body注解类型的转换
static final class Body<T> extends ParameterHandler<T> {
private final Converter<T, RequestBody> converter;
Body(Converter<T, RequestBody> converter) {
this.converter = converter;
}
@Override void apply(RequestBuilder builder, T value) {
if (value == null) {
throw new IllegalArgumentException("Body parameter value must not be null.");
}
RequestBody body;
try {
body = converter.convert(value);//转换为RequestBody
} catch (IOException e) {
throw new RuntimeException("Unable to convert " + value + " to RequestBody", e);
}
builder.setBody(body);//设置Body
}
}
//..
//省略了其他注解类型转换代码
}
调用服务接口
以上介绍了retrofit.create(XX.class);
用于创建服务方法接口的过程。现在该进行相关调用了。我们知道,创建服务方法会返回一个Call<XX>
对象,通过Call<XX>
可以进行相关异步同步调用。
默认CallAdapter相关源码实现在OkHttpCall中,源码如下:
@Override public void enqueue(final Callback<T> callback) {
if (callback == null) throw new NullPointerException("callback == null");
okhttp3.Call call;//okhttp中的call对象
Throwable failure;
synchronized (this) {
if (executed) throw new IllegalStateException("Already executed.");
executed = true;
call = rawCall;
failure = creationFailure;
if (call == null && failure == null) {
try {
call = rawCall = createRawCall();//创建原始Call,即okhttp中的Call对象
} catch (Throwable t) {
failure = creationFailure = t;
}
}
}
if (failure != null) {
callback.onFailure(this, failure);
return;
}
if (canceled) {
call.cancel();
}
//通过enqueue进行异步调用
call.enqueue(new okhttp3.Callback() {
@Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse)
throws IOException {
Response<T> response;
try {
response = parseResponse(rawResponse);//解析响应内容
} catch (Throwable e) {
callFailure(e);
return;
}
callSuccess(response);
}
}
从源码不难看出,首先通过createRawCall()
来创建OkHttp中的Call对象,然后通过Call进行异步/同步调用,获取结果后通过parseResponse
解析OkHttp中的Response,然后进行相应回调。
createRawCall()
用于创建OkHttp中的Call对象,源码如下:
private okhttp3.Call createRawCall() throws IOException {
Request request = serviceMethod.toRequest(args);//通过 serviceMethod.toRequest进行转换成Request
okhttp3.Call call = serviceMethod.callFactory.newCall(request);//调用newCall返回Call对象,默认callFactory为OkHttpClient,OkHttpClient也实现了okhttp3.Call.Factory接口。
if (call == null) {
throw new NullPointerException("Call.Factory returned null.");
}
return call;
}
parseResponse
用于解析原始Response,源码如下:
Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
ResponseBody rawBody = rawResponse.body();//获取ResponseBody
//移除响应体
rawResponse = rawResponse.newBuilder()
.body(new NoContentResponseBody(rawBody.contentType(), rawBody.contentLength()))
.build();
int code = rawResponse.code();//获取状态吗
//code < 200 || code >= 300
if (code < 200 || code >= 300) {
try {
//将整个rawBody缓冲到内存中回调
ResponseBody bufferedBody = Utils.buffer(rawBody);
return Response.error(bufferedBody, rawResponse);
} finally {
rawBody.close();
}
}
//code == 204 || code == 205
if (code == 204 || code == 205) {
//此时没有响应体,只需回调状态
return Response.success(null, rawResponse);
}
ExceptionCatchingRequestBody catchingBody = new ExceptionCatchingRequestBody(rawBody);
try {
T body = serviceMethod.toResponse(catchingBody);//通过`serviceMethod.toResponse`转换为对应对象
//回调
return Response.success(body, rawResponse);
} catch (RuntimeException e) {
catchingBody.throwIfCaught();
throw e;
}
}
从createRawCall()
和parseResponse
的源码中,可以发现,通过 serviceMethod.toRequest
进行转换成OkHttp中的Request,通过serviceMethod.toResponse
将OkHttp中的Response转换为对应对象。
toRequest相关源码如下:
Request toRequest(Object... args) throws IOException {
// RequestBuilder
RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl, headers,
contentType, hasBody, isFormEncoded, isMultipart);
//ParameterHandler数组
ParameterHandler<Object>[] handlers = (ParameterHandler<Object>[]) parameterHandlers;
//...
for (int p = 0; p < argumentCount; p++) {
//遍历数组进行循环应用
handlers[p].apply(requestBuilder, args[p]);
}
return requestBuilder.build();//通过build()返回Request对象
}
toResponse源码非常简单,如下:
T toResponse(ResponseBody body) throws IOException {
return responseConverter.convert(body);
}
默认情况下,会调用BuiltInConverters
中的转换器来转换,当然,更多时候我们回去应用Gson转换器来进行转换。
最后
- GsonConverterFactory是怎么工作的?
一般情况下,我们会加入
addConverterFactory(GsonConverterFactory.create())
来增加Gson转换功能。public final class GsonConverterFactory extends Converter.Factory { //默认Gson public static GsonConverterFactory create() { return create(new Gson()); } //自己配置Gson public static GsonConverterFactory create(Gson gson) { return new GsonConverterFactory(gson); } private final Gson gson; private GsonConverterFactory(Gson gson) { if (gson == null) throw new NullPointerException("gson == null"); this.gson = gson; } //转换ResponseBody @Override public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) { TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));//根据Type获取TypeAdapter return new GsonResponseBodyConverter<>(gson, adapter);//转换逻辑代码 } //转换成RequestBody @Override public Converter<?, RequestBody> requestBodyConverter(Type type, Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) { TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));//根据Type获取TypeAdapter return new GsonRequestBodyConverter<>(gson, adapter);//转换逻辑代码 }
ResponseBody核心转换代码:
@Override public T convert(ResponseBody value) throws IOException { JsonReader jsonReader = gson.newJsonReader(value.charStream());//创建JsonReader try { return adapter.read(jsonReader);//通过adapter转换 } finally { value.close();//关闭流 } }
RequestBody核心转换代码:
@Override public RequestBody convert(T value) throws IOException { Buffer buffer = new Buffer(); Writer writer = new OutputStreamWriter(buffer.outputStream(), UTF_8); JsonWriter jsonWriter = gson.newJsonWriter(writer);//获取JsonWriter adapter.write(jsonWriter, value);//adapter将value转换为Gson jsonWriter.close(); return Re
- RxJavaCallAdapterFactory是怎么工作的?
我们还可以加入
addCallAdapterFactory(RxJavaCallAdapterFactory.create())
使你的代码可以进行响应式编程。//直接创建 public static RxJavaCallAdapterFactory create() { return new RxJavaCallAdapterFactory(null); } //指定工作线程创建 public static RxJavaCallAdapterFactory createWithScheduler(Scheduler scheduler) { if (scheduler == null) throw new NullPointerException("scheduler == null"); return new RxJavaCallAdapterFactory(scheduler); } @Override public CallAdapter<?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) { //.. //省略了部分源码, //通过getCallAdapter来获取Adapter CallAdapter<Observable<?>> callAdapter = getCallAdapter(returnType, scheduler); return callAdapter; } //获取CallAdapter<Observable<?>> private CallAdapter<Observable<?>> getCallAdapter(Type returnType, Scheduler scheduler) { Type observableType = getParameterUpperBound(0, (ParameterizedType) returnType); Class<?> rawObservableType = getRawType(observableType); //.. //省略了部分源码, //一般会走SimpleCallAdapter return new SimpleCallAdapter(observableType, scheduler); } //SimpleCallAdapter的源码如下 static final class SimpleCallAdapter implements CallAdapter<Observable<?>> { private final Type responseType; private final Scheduler scheduler; SimpleCallAdapter(Type responseType, Scheduler scheduler) { this.responseType = responseType; this.scheduler = scheduler; } @Override public Type responseType() { return responseType; } @Override public <R> Observable<R> adapt(Call<R> call) { //通过Observable.create创建被观察者 Observable<R> observable = Observable.create(new CallOnSubscribe<>(call)) .lift(OperatorMapResponseToBodyOrError.<R>instance()); if (scheduler != null) { return observable.subscribeOn(scheduler);//切换到指定线程 } return observable; } }
CallOnSubscribe中使用了RequestArbiter中包装了
Call<T>
对象,然后通过call.execute()同步执行,最后将结果subscriber.onNext(response)
回调出去。static final class RequestArbiter<T> extends AtomicBoolean implements Subscription, Producer { private final Call<T> call;//原始的Call private final Subscriber<? super Response<T>> subscriber; RequestArbiter(Call<T> call, Subscriber<? super Response<T>> subscriber) { this.call = call; this.subscriber = subscriber; } @Override public void request(long n) { //... try { Response<T> response = call.execute();//call.execute()同步执行 if (!subscriber.isUnsubscribed()) { subscriber.onNext(response);//onNext回调 } } catch (Throwable t) { Exceptions.throwIfFatal(t); if (!subscriber.isUnsubscribed()) { subscriber.onError(t); } return; } if (!subscriber.isUnsubscribed()) { subscriber.onCompleted(); } } @Override public void unsubscribe() { call.cancel(); } @Override public boolean isUnsubscribed() { return call.isCanceled(); } }
本篇解读到此结束。