Retrofit 为我们提供了一种非常优雅的方式去书写 Restful 的请求的接口代码,和 OkHttp 、Rxjava 都能方便的无缝搭配,为我们在 Java 和 Android 提供了非常便捷的网络请求的编写方式。这篇文章中我们会从 Usage 出发,逐个步骤的分析 Retrofit 的实现方式。
实现分析
我们可以定义这样的一个接口,代表一种 restful 请求:
public interface GitHubService {
@GET("users/{user}/repos")
Call<List<Repo>> listRepos(@Path("user") String user);
}
在使用 Retrofit 的时候:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.build();
GitHubService service = retrofit.create(GitHubService.class);
我们通过 Builder 模式拼好 baseUrl 等字串,通过 retrofit 对象可以创建我们的接口对应的实体类,我们通过对这个实体类的操作,就能对我们定义好的接口去请求对应的数据:
Call<List<Repo>> repos = service.listRepos("octocat");
创建请求类
@SuppressWarnings("unchecked") // Single-interface proxy creation guarded by parameter safety.
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() {
// ... 省略
}
});
}
这里我们先是对 class 对象的类型做了检测保证了是 Interface 而且没有多继承,并且在开了 validateEagerly
的情况下会对 Service 里面的请求接口进行 Cache:
private void eagerlyValidateMethods(Class<?> service) {
Platform platform = Platform.get();
for (Method method : service.getDeclaredMethods()) {
if (!platform.isDefaultMethod(method)) {
loadServiceMethod(method);
}
}
}
ServiceMethod<?, ?> loadServiceMethod(Method method) {
ServiceMethod<?, ?> result = serviceMethodCache.get(method);
if (result != null) return result;
synchronized (serviceMethodCache) {
result = serviceMethodCache.get(method);
if (result == null) {
result = new ServiceMethod.Builder<>(this, method).build();
serviceMethodCache.put(method, result);
}
}
return result;
}
动态代理
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 the method is a method from Object then defer to normal invocation.
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);
}
});
这就是刚才在 Create 函数中的省略的部分,从这个写法看了明显是用了动态代理的方式。使用代理的方式当然是为了能统一的对我们 Service 进去的方法做一些操作,比如判断是否走 method 的正常的 invoke 方法,判断了是否是 default 方法,当然这个根本就不支持,然后就是正常的绑定成一个 ServiceMethod 里面存储和请求的各种元信息,最后把他们封装成一个 OkHttp 的请求交给 Adapt 托管,这样我们就完成了整个请求服务类的创建。
ServiceMethod
我们可以来看下这个包含元信息的 ServiceMethod 是怎么构建的:
ServiceMethod<?, ?> loadServiceMethod(Method method) {
ServiceMethod<?, ?> result = serviceMethodCache.get(method);
if (result != null) return result;
synchronized (serviceMethodCache) {
result = serviceMethodCache.get(method);
if (result == null) {
result = new ServiceMethod.Builder<>(this, method).build();
serviceMethodCache.put(method, result);
}
}
return result;
}
使用了 ServiceMethod 的默认 build 方法:
Builder(Retrofit retrofit, Method method) {
this.retrofit = retrofit;
this.method = method;
this.methodAnnotations = method.getAnnotations();
this.parameterTypes = method.getGenericParameterTypes();
this.parameterAnnotationsArray = method.getParameterAnnotations();
}
在这之中我们绑定了很多的东西,绑定了我们对这个方法加的注解,参数类型,还有参数的注解,之后在 build
方法之中,主要的事情就是生成 CallAdapter , 还有就是为我们的 Response 提供类型转化的 Converter (比如和 Rxjava 一同使用的就会需要这个)。
CallAdapter
在生成的 CallAdapter 的探索路径中我们会发现是在 Retrofit 的 build
创建的:
adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));
我们的默认的 CallAdapter.Factory 是从 Platform 中提供的:
CallAdapter.Factory defaultCallAdapterFactory(@Nullable Executor callbackExecutor) {
if (callbackExecutor != null) {
return new ExecutorCallAdapterFactory(callbackExecutor);
}
return DefaultCallAdapterFactory.INSTANCE;
}
/**
* Creates call adapters for that uses the same thread for both I/O and application-level
* callbacks. For synchronous calls this is the application thread making the request; for
* asynchronous calls this is a thread provided by OkHttp's dispatcher.
*/
final class DefaultCallAdapterFactory extends CallAdapter.Factory {
static final CallAdapter.Factory INSTANCE = new DefaultCallAdapterFactory();
@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 call;
}
};
}
}
从注释里面我们也能看出来这个是使用相同的线程去创建 CallBack 和 开 IO ,如果是异步的 OKHTTP 就是从它的默认的分发的线程,如果是同步调用就和应用用同一个线程。
public interface CallAdapter<R, T> {
/**
* Returns the value type that this adapter uses when converting the HTTP response body to a Java
* object. For example, the response type for {@code Call<Repo>} is {@code Repo}. This type
* is used to prepare the {@code call} passed to {@code #adapt}.
* <p>
* Note: This is typically not the same type as the {@code returnType} provided to this call
* adapter's factory.
*/
Type responseType();
/**
* Returns an instance of {@code T} which delegates to {@code call}.
* <p>
* For example, given an instance for a hypothetical utility, {@code Async}, this instance would
* return a new {@code Async<R>} which invoked {@code call} when run.
* <pre><code>
* @Override
* public <R> Async<R> adapt(final Call<R> call) {
* return Async.create(new Callable<Response<R>>() {
* @Override
* public Response<R> call() throws Exception {
* return call.execute();
* }
* });
* }
* </code></pre>
*/
T adapt(Call<R> call);
}
另外我们看这个生成的 CallAdapter 本身做了什么事,它判断了返回的 Response 的类型,针对类型去泛化了一个对应的 CallAdapter , Adapter 做得事情就是把提供接口在 adapter 的过程中给 Call 增加代理(默认的工厂方法没有提供),另外就是提供类型信息当 Response 回来的时候能进行正确的类型转化。
Converter
Response Converter 的设计思路和 Adapter 的思路其实是差不多的提供默认的接口,并且辅助包中提供了大量的额外实现:
/**
* Convert objects to and from their representation in HTTP. Instances are created by {@linkplain
* Factory a factory} which is {@linkplain Retrofit.Builder#addConverterFactory(Factory) installed}
* into the {@link Retrofit} instance.
*/
public interface Converter<F, T> {
T convert(F value) throws IOException;
/**
* Creates {@link Converter} instances based on a type and target usage.
*/
abstract class Factory {
/**
* Returns a {@link Converter} for converting an HTTP response body to {@code type}, or null if
* {@code type} cannot be handled by this factory. This is used to create converters for
* response types such as {@code SimpleResponse} from a {@code Call<SimpleResponse>}
* declaration.
*/
public @Nullable
Converter<ResponseBody, ?> responseBodyConverter(Type type,
Annotation[] annotations, Retrofit retrofit) {
return null;
}
/**
* Returns a {@link Converter} for converting {@code type} to an HTTP request body, or null if
* {@code type} cannot be handled by this factory. This is used to create converters for types
* specified by {@link Body @Body}, {@link Part @Part}, and {@link PartMap @PartMap}
* values.
*/
public @Nullable
Converter<?, RequestBody> requestBodyConverter(Type type,
Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
return null;
}
/**
* Returns a {@link Converter} for converting {@code type} to a {@link String}, or null if
* {@code type} cannot be handled by this factory. This is used to create converters for types
* specified by {@link Field @Field}, {@link FieldMap @FieldMap} values,
* {@link Header @Header}, {@link HeaderMap @HeaderMap}, {@link Path @Path},
* {@link Query @Query}, and {@link QueryMap @QueryMap} values.
*/
public @Nullable
Converter<?, String> stringConverter(Type type, Annotation[] annotations,
Retrofit retrofit) {
return null;
}
/**
* Extract the upper bound of the generic parameter at {@code index} from {@code type}. For
* example, index 1 of {@code Map<String, ? extends Runnable>} returns {@code Runnable}.
*/
protected static Type getParameterUpperBound(int index, ParameterizedType type) {
return Utils.getParameterUpperBound(index, type);
}
/**
* Extract the raw class type from {@code type}. For example, the type representing
* {@code List<? extends Runnable>} returns {@code List.class}.
*/
protected static Class<?> getRawType(Type type) {
return Utils.getRawType(type);
}
}
}
真对不同需求的实现可以去实现不同的方法,比如 rxjava2 包里的 StringConverterFactory 就默认只实现了和 String 相关的方法:
final class StringConverterFactory extends Converter.Factory {
@Override
public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
Retrofit retrofit) {
return new Converter<ResponseBody, String>() {
@Override public String convert(ResponseBody value) throws IOException {
return value.string();
}
};
}
@Override public Converter<?, RequestBody> requestBodyConverter(Type type,
Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
return new Converter<String, RequestBody>() {
@Override public RequestBody convert(String value) throws IOException {
return RequestBody.create(MediaType.parse("text/plain"), value);
}
};
}
}
这里面提供了吧 ResponseBody 转为 String 的 Converter 和把 String 构建成 ResponseBody 的方法,比如我们经常用的 Gson 的 Converter:
/**
* A {@linkplain Converter.Factory converter} which uses Gson for JSON.
* <p>
* Because Gson is so flexible in the types it supports, this converter assumes that it can handle
* all types. If you are mixing JSON serialization with something else (such as protocol buffers),
* you must {@linkplain Retrofit.Builder#addConverterFactory(Converter.Factory) add this instance}
* last to allow the other converters a chance to see their types.
*/
public final class GsonConverterFactory extends Converter.Factory {
/**
* Create an instance using a default {@link Gson} instance for conversion. Encoding to JSON and
* decoding from JSON (when no charset is specified by a header) will use UTF-8.
*/
public static GsonConverterFactory create() {
return create(new Gson());
}
/**
* Create an instance using {@code gson} for conversion. Encoding to JSON and
* decoding from JSON (when no charset is specified by a header) will use UTF-8.
*/
@SuppressWarnings("ConstantConditions") // Guarding public API nullability.
public static GsonConverterFactory create(Gson gson) {
if (gson == null) throw new NullPointerException("gson == null");
return new GsonConverterFactory(gson);
}
private final Gson gson;
private GsonConverterFactory(Gson gson) {
this.gson = gson;
}
@Override
public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
Retrofit retrofit) {
TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
return new GsonResponseBodyConverter<>(gson, adapter);
}
@Override
public Converter<?, RequestBody> requestBodyConverter(Type type,
Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
return new GsonRequestBodyConverter<>(gson, adapter);
}
}
里面则提供了对 Gson 的转化,因为 Type 信息在之前已经拿到了,所以 Gson 的转换也非常简单。
ParameterHandlers
parseParameterAnnotation
根据注解和类型分门别类的对每个参数对应了一个 ParameterHandler ,这段过程没有太多可说的地方,基本上就是对不同类型的一个大的判断和 Parser ,ParameterHandler 中有不同的 apply 方法对我们不同的类型进行处理:
abstract void apply(RequestBuilder builder, @Nullable T value) throws IOException;
最终的目的都是为了为 RequestBuilder 这个对象拼接需要的元信息。
发送请求
Call<List<Repo>> repos = service.listRepos("octocat");
刚才我们提到了发送请求就是直接这样使用的就可以把我们的请求发送出来了,我们刚才提到了很多请求用到的 元数据 的绑定,那我们现在应该已经有了一个充满了请求所需数据的 ResponseBuilder 我们需要的就是调用刚才绑定的那个 OkHttpCall 了:
execute()
@Override
public Response<T> execute() throws IOException {
okhttp3.Call call;
synchronized (this) {
if (executed) throw new IllegalStateException("Already executed.");
executed = true;
if (creationFailure != null) {
if (creationFailure instanceof IOException) {
throw (IOException) creationFailure;
} else {
throw (RuntimeException) creationFailure;
}
}
call = rawCall;
if (call == null) {
try {
call = rawCall = createRawCall();
} catch (IOException | RuntimeException e) {
creationFailure = e;
throw e;
}
}
}
if (canceled) {
call.cancel();
}
return parseResponse(call.execute());
}
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;
}
Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
ResponseBody rawBody = rawResponse.body();
// Remove the body's source (the only stateful object) so we can pass the response along.
rawResponse = rawResponse.newBuilder()
.body(new NoContentResponseBody(rawBody.contentType(), rawBody.contentLength()))
.build();
int code = rawResponse.code();
if (code < 200 || code >= 300) {
try {
// Buffer the entire body to avoid future I/O.
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) {
// If the underlying source threw an exception, propagate that rather than indicating it was
// a runtime exception.
catchingBody.throwIfCaught();
throw e;
}
}
execute()
方法实现了同步请求的方法,这其中的网络请求和调用自然是全靠 OkHttp 来实现的,另外本身使用 OkHttp 的 execute
方法就是一个阻塞的方法。其中值得注意的是我们对 Request 和 Response 的处理,分别是用了 ServiceMethod 中的方法:
/** Builds an HTTP request from method arguments. */
Request toRequest(@Nullable Object... args) throws IOException {
RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl, headers,
contentType, hasBody, isFormEncoded, isMultipart);
@SuppressWarnings("unchecked") // It is an error to invoke a method with the wrong arg types.
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();
}
/** Builds a method return value from an HTTP response body. */
R toResponse(ResponseBody body) throws IOException {
return responseConverter.convert(body);
}
一个是用了刚才 ParameterHandler 抓到的元信息去构造 Request,另外一个就是把 Response 用对应的 Converter 去解析成想要的类型。
enqueue
@Override
public void enqueue(final Callback<T> callback) {
checkNotNull(callback, "callback == null");
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) {
try {
callback.onFailure(OkHttpCall.this, e);
} catch (Throwable t) {
t.printStackTrace();
}
}
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();
}
}
});
}
相应的 enqueue
方法也是在一堆的 check 之后,直接使用了 OkHttp 的异步 enqueue 去请求。
再看 CallAdapter
CallAdapter 刚才我们已经看了它的默认的实现,将 Call<R>
类型,转换为 T
,当然这里默认的实现里面 这个 T
还是 Call<R>
,但是这是默认实现,retrofit 中提供了为数众多的 adapters ,比如针对 Rxjava 的 Adapter:
@Override
public Object adapt(Call<R> call) {
Observable<Response<R>> responseObservable = isAsync
? new CallEnqueueObservable<>(call)
: new CallExecuteObservable<>(call);
Observable<?> observable;
if (isResult) {
observable = new ResultObservable<>(responseObservable);
} else if (isBody) {
observable = new BodyObservable<>(responseObservable);
} else {
observable = responseObservable;
}
if (scheduler != null) {
observable = observable.subscribeOn(scheduler);
}
if (isFlowable) {
return observable.toFlowable(BackpressureStrategy.LATEST);
}
if (isSingle) {
return observable.singleOrError();
}
if (isMaybe) {
return observable.singleElement();
}
if (isCompletable) {
return observable.ignoreElements();
}
return observable;
}
就为我们提供了返回 Observable 对象的返回 Adapter。
总结
至此我们就完成了对 Retrofit 的本体的分析,当然 Retrofit 还提供了非常多的 Adapter 和 Converter ,不过都是针对不同的请求类型进行的特化,这里就不一并分析了。Retrofit 本身的本体代码非常的少,但是能为我们提供这么 Restful 的 API 支持,不得不说确实有其过人之处。
阅读本文你学到了什么:
- Retrofit 源码的分析
- 通过动态代理为 API 提供统一操作的思路
- 使用注解提供元信息的请求框架的设计思路
- 使用 Factory 对整个架构留出扩充空间和解耦的思路