相信android的同学们应该都对retrofit不陌生,它是由美国Square公司开源的一个用于处理网络请求的框架,该公司还推出很多应用非常广泛的框架,如:okhttp,dagger,picasso等。
仓库地址:https://github.com/square/retrofit
使用方式
retrofit有别与传统的网络框架(准确的说retrofit也不是一个网络框架,它只是对okhttp做了封装,相当于是个工具库),retrofit使用了注解的方式,非常简洁优雅的完成了网络接口的封装。
public interface GitHubService {
@GET("users/{user}/repos")
Call<List<Repo>> listRepos(@Path("user") String user);
}
如上,将一个http请求转换为一个java接口。然后构建Retrofit对象:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.validateEagerly(true)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
得到接口对象,发起请求:
GitHubService service = retrofit.create(GitHubService.class);
Call<List<Repo>> repos = service.listRepos("octocat");
call.enqueue(new Callback<List<Repo>>() {
@Override
public void onResponse(Call<List<Repo>> call, Response<List<Repo>>
response) {
}
@Override
public void onFailure(Call<List<Repo>> call, Throwable t) {
}
});
这就完成了一个http请求,如果对使用上还有不熟悉的可以查看官方文档。这里就不过多的讨论使用方式以及一些注解含义了。
原理解析
那么retrofit到底做了什么,我们先从retrofit的构建开始分析。
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.validateEagerly(true)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
老司机看了这段代码应该就知道,retrofit的构建采用了建造者模式,所谓建造者模式就是将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
public Builder() {
this(Platform.get());
}
最后我们会调用到:
private static Platform findPlatform() {
try {
Class.forName("android.os.Build");
if (Build.VERSION.SDK_INT != 0) {
return new Android();
}
} catch (ClassNotFoundException ignored) {
}
try {
Class.forName("java.util.Optional");
return new Java8();
} catch (ClassNotFoundException ignored) {
}
try {
Class.forName("org.robovm.apple.foundation.NSObject");
return new IOS();
} catch (ClassNotFoundException ignored) {
}
return new Platform();
}
这里其实就是判定我们当前程序的运行环境,是android,java8还是IOS,当然我们都是基于android平台来分析的。
static class Android extends Platform {
@Override public Executor defaultCallbackExecutor() {
return new MainThreadExecutor();
}
@Override CallAdapter.Factory defaultCallAdapterFactory(Executor callbackExecutor) {
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);
}
}
}
可以看出里面封装了handler,所以在android平台就是通过handler转到了主线程。
前面提到Retrofit的构建是通过建造者模式来的,我们先来看下他的内部类Builder:
public static final class Builder {
private Platform platform;
private okhttp3.Call.Factory callFactory;
private HttpUrl baseUrl;
private List<Converter.Factory> converterFactories = new ArrayList<>();
private List<CallAdapter.Factory> adapterFactories = new ArrayList<>();
private Executor callbackExecutor;
private boolean validateEagerly;
Builder(Platform platform) {
this.platform = platform;
// Add the built-in converter factory first. This prevents overriding its behavior but also
// ensures correct behavior when using converters that consume all types.
converterFactories.add(new BuiltInConverters());
}
public Builder() {
this(Platform.get());
}
Platform前面已经大致分析了下,代表运行平台。
看到第三行我们就可以猜到Retrofit其实是使用Okhttp来作为网络请求框架的。
baseUrl顾名思义,将我们传入的url转换成为Okhttp框架中的HttpUrl。
converterFactories,在上例构建中我们有调用addConverterFactory,其中传入的参数GsonConverterFactory.create()就是被存储在converterFactories,主要用于将服务端返回的数据转换成客户端使用的model对象,如通过Gson解析。
adapterFactories用来存放网络适配器,默认是ExecutorCallAdapterFactory,同时我们可以使用rxjava,使用Observable来更好的处理网络请求。
callbackExecutor就是执行的线程池,默认实现是MainThreadExecutor。
validateEagerly用于提前对注解进行解析。之后会分析到。
看看Build()
在Retrofit构建的最后一步调用了build,主要代码:
public Retrofit build() {
...
okhttp3.Call.Factory callFactory = this.callFactory;
if (callFactory == null) {
callFactory = new OkHttpClient();
}
...
return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
callbackExecutor, validateEagerly);
}
从这里我们也可以发现,创建了一个OkHttpClient,说明了Retrofit就是封装了Okhttp的。最后一行返回创建Retrofit并返回。
Retrofit是怎么处理注解的呢?
主要是通过retrofit.create这个方法,非常重要,他帮助我们生成了接口实现类。那么他到底做了什么呢?
public <T> T create(final Class<T> service) {
Utils.validateServiceInterface(service);
if (validateEagerly) {//注释1
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, 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 serviceMethod = loadServiceMethod(method);//注释2
OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);//注释3
return serviceMethod.callAdapter.adapt(okHttpCall);//注释4
}
});
}
原来他是使用了动态代理的方式来实现的,这也是Retrofit的核心之一,也是Retrofit精妙的地方。看注释1,就是在Retrofit构建的时候可以传递的一个参数,上面提过一嘴,用于提前解析,我们可以看到当判断为true的时候就会执行loadServiceMethod(),否则的话就不执行,直到注释2处再执行。
重点关注注释2,3,4三行代码。
ServiceMethod的生成
注释2调用了loadServiceMethod来生成ServiceMethod
ServiceMethod loadServiceMethod(Method method) {
ServiceMethod result;
synchronized (serviceMethodCache) {
result = serviceMethodCache.get(method);
if (result == null) {
result = new ServiceMethod.Builder(this, method).build();
serviceMethodCache.put(method, result);
}
}
return result;
}
serviceMethodCache为一个Map对象: Map<Method, ServiceMethod> serviceMethodCache = new LinkedHashMap<>()
逻辑比较简单,以method为key,ServiceMethod为value,先从map中取,如果没有再重新new一个对象,放入map中并返回。这里主要看ServiceMethod的创建:ServiceMethod.Builder(this, method).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();
}
- 获取retrofit实例
- 接口方法method
- methodAnnotations,这里一般是指http的请求方式,如GET,POST,就是我们在方法上面添加的注解
- parameterTypes为参数类型
- parameterAnnotationsArray网络请求参数注解
再看build():
public ServiceMethod build() {
callAdapter = createCallAdapter();
responseType = callAdapter.responseType();
...
responseConverter = createResponseConverter();
for (Annotation annotation : methodAnnotations) {
parseMethodAnnotation(annotation);
}
...
int parameterCount = parameterAnnotationsArray.length;
parameterHandlers = new ParameterHandler<?>[parameterCount];
for (int p = 0; p < parameterCount; p++) {
Type parameterType = parameterTypes[p];
...
Annotation[] parameterAnnotations = parameterAnnotationsArray[p];
if (parameterAnnotations == null) {
throw parameterError(p, "No Retrofit annotation found.");
}
parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);
}
...
return new ServiceMethod<>(this);
}
build方法内容比较多,已经删除了一些不重要的内容。首先获得了callAdapter,responseConverter等,接着调用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);
}
...
}
可以发现这里是处理Http请求方法的解析。
接着调用parseParameter(p, parameterType, parameterAnnotations),进行参数解析
private ParameterHandler<?> parseParameter(
int p, Type parameterType, Annotation[] annotations) {
ParameterHandler<?> result = null;
for (Annotation annotation : annotations) {
ParameterHandler<?> annotationAction = parseParameterAnnotation(
p, parameterType, annotations, annotation);
...
return result;
}
这个方法比较简单,主要是调用parseParameterAnnotation来得到ParameterHandler<?> annotationAction对象,我们主要看parseParameterAnnotation()
if (annotation instanceof Path) {
if (gotQuery) {
throw parameterError(p, "A @Path parameter must not come after a @Query.");
}
if (gotUrl) {
throw parameterError(p, "@Path parameters may not be used with @Url.");
}
if (relativeUrl == null) {
throw parameterError(p, "@Path can only be used with relative url on @%s", httpMethod);
}
gotPath = true;
Path path = (Path) annotation;
String name = path.value();
validatePathName(p, name);
Converter<?, String> converter = retrofit.stringConverter(type, annotations);
return new ParameterHandler.Path<>(name, converter, path.encoded());
}
这个方法很长,但是逻辑比较相似,通过if else遍历各个参数注解,进行相应的处理,我这边就截取了其中的一个if语句。比较简单,拿到注解的name,然后通过正则判断是否合法,再构建一个Converter<?, String>对象,我们看下这对象是怎么构建的
public <T> Converter<T, String> stringConverter(Type type, Annotation[] annotations) {
checkNotNull(type, "type == null");
checkNotNull(annotations, "annotations == null");
for (int i = 0, count = converterFactories.size(); i < count; i++) {
Converter<?, String> converter =
converterFactories.get(i).stringConverter(type, annotations, this);
if (converter != null) {
//noinspection unchecked
return (Converter<T, String>) converter;
}
}
// Nothing matched. Resort to default converter which just calls toString().
//noinspection unchecked
return (Converter<T, String>) BuiltInConverters.ToStringConverter.INSTANCE;
}
可以看到他是通过遍历converterFactories来得到对应的converter,也就是我们在构建Retrofit的时候通过addConverterFactory传递进来的对象。如果没有设置,会取BuiltInConverters.ToStringConverter.INSTANCE,BuiltInConverters是框架内置的转换器。
方法最后再构建一个Path对象(我们这边截取的是path代码块)。就这样通过上述过程,relativeUrl就拼接完成了。
由此,ServiceMethod的创建基本也分析完了
OkHttpCall的创建
生成好ServiceMethod之后,将生成好的serviceMethod用于OkHttpCall的参数生成对应的OkHttpCall,OkHttpCall是Retrofit中的call接口的实现类。包含了具体的同步execute()与异步enqueue()操作。
@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());
}
可以发现,到这里Retrofit就把网络请求相关的操作直接扔给了Okhttp来处理。
Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
ResponseBody rawBody = rawResponse.body();
...
ExceptionCatchingRequestBody catchingBody = new ExceptionCatchingRequestBody(rawBody);
try {
T body = serviceMethod.toResponse(catchingBody);//注释1
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;
}
}
在注释1处,将服务端返回的数据做处理
T toResponse(ResponseBody body) throws IOException {
return responseConverter.convert(body);
}
很明显,就是调用我们之前所配置的数据转换器responseConverter。
serviceMethod.callAdapter.adapt
注释3处就是返回需要被操作的Call对象,用于调用端发起请求。
总结
Retrofit的大致分析就是这些,其中还有些细节没有讲到,感兴趣的可以自己查看源码。我这边分析了一个大体的框架。可以发现Retrofit设计还是非常精妙的,使用了大量的设计模式,值得大家品味。