retrofit是square公司基于okhttp,okio之上的又一款非常好的开源库。该库主给android/java开发者提供了简化而优美的http实现接口。开发成员可以将http api通过注解的方式写入到interface中,然后通过接口调用的方式发起http请求。而且retrofit提供了丰富的converters,可以方便RequestBody,ResponseBody类型的转换机制,以及对call的adapter机制大大方便了用户对于网络请求的使用。官网地址为:retrofit
重要的类和字段功能
retrofit 注解
retrofit的其中一个特点是,允许用户将http api通过注解的方式写入到一个interface之中。retrofit提供的注解的生命周期全部是RUNTIME级别,意味着都是在运行时进行注解解析配置的。这一点和ButterKnife不一样。retrofit提供的注解主要有以下几类
http method部分
GET, POST, PATCH, PUT, DELETE, HEAD, OPTIONS, HTTP
这些注解中,都支持填入path,该path最终和retrofit中设置的base url一起组成完整的url。其中注解HTTP比较特殊,HTTP支持自定义method。
1 2 3 4 5 6 7 8
| @Documented @Target(METHOD) @Retention(RUNTIME) public @interface HTTP { String method(); String path() default ""; boolean hasBody() default false; }
|
head部分
如果需要对所有http访问请求都需要添加统一的header,可以通过okhttp的Interceptor实现。如果针对某些个具体的http api才需要添加的header可以使用retrofit的Header/Headers/HeadMap注解,例如:
1 2 3 4 5 6
| @Headers({ "Accept: application/vnd.github.v3.full+json", "User-Agent: Retrofit-Sample-App" }) @GET("users/{username}") Call<User> getUser(@Path("username") String username);
|
path, query部分
在http method的注解中填入的path可以是不完整的。可以在函数的参数列表中,通过Path注解进行参数指定,同时可以选择是否encode
对于url中的的query部分,可以通过注解Query/QueryMap/QueryNam进行指定
body部分
考虑到body部分可以多种多样的类型,因此这一部分的注解比较庞杂,包括:Body, Field, FieldMap, FormUrlEncoded, Multipart, Part, PartMap
response部分
retrofit提供注解Streaming指明返回类型没有经过任何转换,保持为okhttp的ResponseBody
Converter<F,T>
用于转换类型从类型F转变成T,这个类非常用于,被retrofit用于转换requestBody和responseBody,而且用户可以配置自己所需转换的类型。
下面给出Converter的核心代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| public interface Converter<F, T> { T convert(F value) throws IOException; abstract class Factory { public @Nullable Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) { return null; } public @Nullable Converter<?, RequestBody> requestBodyConverter(Type type, Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) { return null; } public @Nullable Converter<?, String> stringConverter(Type type, Annotation[] annotations, Retrofit retrofit) { return null; } ... } }
|
CallAdapter<R, T>
和Converter类似,用于将一个类型转换到另一个,不过CallAdapter主要用于转换Call<R>到T
下面给出其核心代码
1 2 3 4 5 6 7 8 9 10 11
| public interface CallAdapter<R, T> { Type responseType(); T adapt(Call<R> call); abstract class Factory { public abstract @Nullable CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit); ...... } }
|
BuiltInConverters
在调用Retrofit.Builder过程中,Builder已经默认添加上了BuiltInConverters。该类用于提供默认的converter,帮助将原始的ResponseBody,RequestBody转换到指定的类型,下面给出部分核心代码,代码很简单
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| final class BuiltInConverters extends Converter.Factory { @Override public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) { if (type == ResponseBody.class) { return Utils.isAnnotationPresent(annotations, Streaming.class) ? StreamingResponseBodyConverter.INSTANCE : BufferingResponseBodyConverter.INSTANCE; } if (type == Void.class) { return VoidResponseBodyConverter.INSTANCE; } return null; } @Override public Converter<?, RequestBody> requestBodyConverter(Type type, Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) { if (RequestBody.class.isAssignableFrom(Utils.getRawType(type))) { return RequestBodyConverter.INSTANCE; } return null; } ...... }
|
ExecutorCallAdapterFactory
在android平台下,Retrofit.Builder build过程中生成一个ExecutorCallAdapterFactory添加到Retrofit中。该类的主要目的是,替换Call,在Call的enqueue过程中,确保最终response返回白执行在指定的executor中。
该类正常情况下位于Retrofit的adapterFactories list的最后一个,正式为了确保最终callback在executor中执行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
| final class ExecutorCallAdapterFactory extends CallAdapter.Factory { final Executor callbackExecutor; ExecutorCallAdapterFactory(Executor callbackExecutor) { this.callbackExecutor = callbackExecutor; } @Override public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) { if (getRawType(returnType) != Call.class) { return null; } final Type responseType = Utils.getCallResponseType(returnType); return new CallAdapter<Object, Call<?>>() { @Override public Type responseType() { return responseType; } @Override public Call<Object> adapt(Call<Object> call) { return new ExecutorCallbackCall<>(callbackExecutor, call); } }; } static final class ExecutorCallbackCall<T> implements Call<T> { final Executor callbackExecutor; final Call<T> delegate; ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) { this.callbackExecutor = callbackExecutor; this.delegate = delegate; } @Override public void enqueue(final Callback<T> callback) { checkNotNull(callback, "callback == null"); delegate.enqueue(new Callback<T>() { @Override public void onResponse(Call<T> call, final Response<T> response) { callbackExecutor.execute(new Runnable() { @Override public void run() { if (delegate.isCanceled()) { callback.onFailure(ExecutorCallbackCall.this, new IOException("Canceled")); } else { callback.onResponse(ExecutorCallbackCall.this, response); } } }); } @Override public void onFailure(Call<T> call, final Throwable t) { callbackExecutor.execute(new Runnable() { @Override public void run() { callback.onFailure(ExecutorCallbackCall.this, t); } }); } }); } ...... }
|
ParameterHandler
ParameterHandler的主要作用是提供apply方法,根据method的输入值构建okhttp的RequestBuilder,下面给出ParameterHandler的部分代码
1 2
| abstract class ParameterHandler<T> { abstract void apply(RequestBuilder builder, @Nullable T value) throws IOException;
|
可以看出子类需要实现apply方法来实现对RequestBuider的配置
有意思的是,该类中,还提供了array和iterable的变形,方便处理批量的T类型数据到RequestBuilder中,下面给出部分代码
1 2 3 4 5 6 7 8 9 10 11 12
| final ParameterHandler<Iterable<T>> iterable() { return new ParameterHandler<Iterable<T>>() { @Override void apply(RequestBuilder builder, @Nullable Iterable<T> values) throws IOException { if (values == null) return; for (T value : values) { ParameterHandler.this.apply(builder, value); } } }; }
|
由该类延伸出很多子类,例如RelativeUrl, Header等等,下面仅仅给出部分子类代码仅供参考
1 2 3 4 5 6
| static final class RelativeUrl extends ParameterHandler<Object> { @Override void apply(RequestBuilder builder, @Nullable Object value) { checkNotNull(value, "@Url parameter is null."); builder.setRelativeUrl(value); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| static final class Header<T> extends ParameterHandler<T> { private final String name; private final Converter<T, String> valueConverter; Header(String name, Converter<T, String> valueConverter) { this.name = checkNotNull(name, "name == null"); this.valueConverter = valueConverter; } @Override void apply(RequestBuilder builder, @Nullable T value) throws IOException { if (value == null) return; String headerValue = valueConverter.convert(value); if (headerValue == null) return; builder.addHeader(name, headerValue); } }
|
ServiceMethod
对于service interface中的method主要解析工作都放在了该类中。
Builder
对于method的parse工作主要在Builder的build方法中进行, build的工作主要为:
- callAdapter
根据method的returnType从retrofit的adapterFactories中寻找合适的callAdapter,并保存该callAdapter的索引
- responseConverter
根据callAdapter的responseType,从retrofit的converterFactories中寻找并生成对应的responseConverter
- method annotations
解析method上的每一个注解,并将解析结果保存在对应的字段中
- parameter annotations
解析每一个method parameter上的注解,生成parameter上对应ParameterHandler<?>
toRequest
ServiceMethod在toRequest中,生成okhttp的ReqeustBuilder,并通过ParameterHanders处理每一个输入参数的值,从而完成对RequestBuilder配置,最后上传okhttp的Request。过程非常简洁,代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| Request toRequest(@Nullable Object... args) throws IOException { RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl, headers, contentType, hasBody, isFormEncoded, isMultipart); @SuppressWarnings("unchecked") ParameterHandler<Object>[] handlers = (ParameterHandler<Object>[]) parameterHandlers; int argumentCount = args != null ? args.length : 0; if (argumentCount != handlers.length) { throw new IllegalArgumentException("Argument count (" + argumentCount + ") doesn't match expected count (" + handlers.length + ")"); } for (int p = 0; p < argumentCount; p++) { handlers[p].apply(requestBuilder, args[p]); } return requestBuilder.build(); }
|
Retrofit
Builder
可以通过Retrofit.Builder内部类对Retrofit进行初始化,通过builder可以配置一些重要的参数,例如
- OkHttpClient, 用于提供callFactory
- callFactory, 用于提供newCall接口,一般为okHttpClient实例
- baseUrl, 可以控制所有api的base url
- Converter.Factory, 用于控制requestbody,responsebody类型转换
- CallAdapter.Factory, 用于控制Call<>类型的转换
- CallbackExcecutor, 可以控制callback执行线程,默认为platform的defaultCallbackExcecutor,即android的主线程
serviceMethodCache
该字段类型: Map <<Method, ServiceMethod<?, ?>>,用于缓存解析过的method
<T>T create(class<T> service)
该method主要用于将包含用户定义的service interface进行实例化。实例化之前,如果用户配置了validateEagerly=true,则立即解析service interface中的所有注解,解析过程中,每一个method对应一个ServiceMethod。并将结果保存在serviceMethodCache中
在实例化service interface时,使用了java的proxy代理机制,具体代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| public <T> T create(final Class<T> service) { Utils.validateServiceInterface(service); if (validateEagerly) { eagerlyValidateMethods(service); } 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, @Nullable Object[] args) throws Throwable { if (method.getDeclaringClass() == Object.class) { return method.invoke(this, args); } if (platform.isDefaultMethod(method)) { return platform.invokeDefaultMethod(method, service, proxy, args); } ServiceMethod<Object, Object> serviceMethod = (ServiceMethod<Object, Object>) loadServiceMethod(method); OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args); return serviceMethod.callAdapter.adapt(okHttpCall); } }); }
|
OkhttpCall
该类实现了Call<T>接口, 实际上该类是对okhttp call的一个封装。内部通过rawCall属性指向实际call对象。
1 2 3 4 5 6 7 8 9 10 11
| ...... rawCall = createRawCall(); ...... private okhttp3.Call createRawCall() throws IOException { Request request = serviceMethod.toRequest(args); okhttp3.Call call = serviceMethod.callFactory.newCall(request); if (call == null) { throw new NullPointerException("Call.Factory returned null."); } return call; }
|
大部分操作通过rawCall对象进行,okHttpCall的任务是将执行结果封装成response对象,并在封装过程中,执行类型转换。下面代码给出了enqueue的部分代码,以及封装response的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
| @Override public void enqueue(final Callback<T> callback) { okhttp3.Call 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(); } catch (Throwable t) { failure = creationFailure = t; } } } if (failure != null) { callback.onFailure(this, failure); return; } if (canceled) { call.cancel(); } 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); } @Override public void onFailure(okhttp3.Call call, IOException e) { callFailure(e); } private void callFailure(Throwable e) { try { callback.onFailure(OkHttpCall.this, e); } catch (Throwable t) { t.printStackTrace(); } } private void callSuccess(Response<T> response) { try { callback.onResponse(OkHttpCall.this, response); } catch (Throwable t) { t.printStackTrace(); } } }); }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException { ResponseBody rawBody = rawResponse.body(); rawResponse = rawResponse.newBuilder() .body(new NoContentResponseBody(rawBody.contentType(), rawBody.contentLength())) .build(); int code = rawResponse.code(); if (code < 200 || code >= 300) { try { ResponseBody bufferedBody = Utils.buffer(rawBody); return Response.error(bufferedBody, rawResponse); } finally { rawBody.close(); } } if (code == 204 || code == 205) { rawBody.close(); return Response.success(null, rawResponse); } ExceptionCatchingRequestBody catchingBody = new ExceptionCatchingRequestBody(rawBody); try { T body = serviceMethod.toResponse(catchingBody); return Response.success(body, rawResponse); } catch (RuntimeException e) { catchingBody.throwIfCaught(); throw e; } }
|