OkHttp与Retrofit 的区别与联系是怎样的?
参考答案:
OkHttp和Retrofit都是目前流行网络开源框架
封装不同:
Retrofit封装了具体的请求,线程切换以及数据转换。
retrofit通过使用代理,外观,策略模式对okhttp进行了封装
OkHttp 是基于Http协议封装的一套请求客户端
职责不同:
Retrofit主要负责应用层面的封装,面向开发者,方便使用,比如请求参数,响应数据的处理,错误处理等等。
OkHttp主要负责socket部分的优化与封装,比如网络访问,多路复用,buffer缓存,数据压缩等等。
(顺手留下GitHub链接,需要获取相关面试等内容的可以自己去找)
https://github.com/xiangjiana/Android-MS
更多完整项目下载。未完待续。源码。图文知识后续上传github。
可以点击关于我联系我获取
Retrofit
可以说和 OkHttp
是亲兄弟了,它们都是由 Square 公司推出的网络请求库,并且 Retrofit
实际上是基于 OkHttp
实现的,它在 OkHttp
现有功能的基础上进行了封装,支持通过注解进行网络请求参数的配置,同时对数据返回后的解析、序列化进行了统一的包装,甚至在近期引入了对协程对支持。
今天就让我们一起来看看 Retrofit
是如何在 OkHttp
这样一个已经固定的框架的基础上,优雅的进行封装并拓展功能的。
基本使用
我们首先来看看 Retrofit 的基本使用,来对它有个大致的了解。
首先,我们可以构建一个如下的请求 Service 类,它里面对各个请求的方法及参数通过注解进行了标注:
public interface GitHubService {
@GET("users/{user}/repos")
Call<List<Repo>> listRepos(@Path("user") String user);
}
之后,我们可以构建一个 Retrofit
对象,并通过 Retrofit.create
方法传入对应 class
从而构建对应的 Service
对象:
Retrofit retrofit = new Retrofit.Builder()
baseUrl("https://api.github.com/")
build();
}
GitHubService service = retrofit.create(GitHubService.class);
之后,我们调用 service
中对应的方法,就可以获取到 Call 对象了。
通过对 Call 对象调用 enqueue
就可以实现对请求的异步调用,而通过 execute
方法则可以实现请求的同步调用。
Retrofit 对象的构建
Retrofit 采用了 Builder 模式进行了构建,在 Builder 中可以进行非常多的配置,其中可以对 baseUrl
、okhttpClient
、converterFactory
、callAdapterFactory
等进行设置。
这里没什么特别的,都是一些简单的赋值,就不再关注了,我们只需要看看最后 Retrofit 被传入了哪些参数。它最后调用了下面这个构造函数对参数进行了初始化。
Retrofit(okhttp3.Call.Factory callFactory, HttpUrl baseUrl,
List<Converter.Factory> converterFactories, List<CallAdapter.Factory> callAdapterFactories,
@Nullable Executor callbackExecutor, boolean validateEagerly) {
this.callFactory = callFactory;
this.baseUrl = baseUrl;
this.converterFactories = converterFactories; // Copy+unmodifiable at call site.
this.callAdapterFactories = callAdapterFactories; // Copy+unmodifiable at call site.
this.callbackExecutor = callbackExecutor;
this.validateEagerly = validateEagerly;
}
Service 对象的创建
动态代理创建 Service 代理类
接着我们看到自己定义的 interface
是如何仅仅靠传递 class
给 Retrofit.create
就能实现实例的获取的,它明明只是个接口呀?
public <T> T create(final Class<T> service) {
// 对 Service 的接口进行检测
validateServiceInterface(service);
// 通过动态代理构建代理对象
return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
new InvocationHandler() {
private final Platform platform = Platform.get();
private final Object[] emptyArgs = new Object[0];
@Override public @Nullable Object invoke(Object proxy, Method method,
@Nullable Object[] args) throws Throwable {
// Object 类的方法照常调用
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
// 如果是对应平台本身的类就有的方法,照常调用
if (platform.isDefaultMethod(method)) {
return platform.invokeDefaultMethod(method, service, proxy, args);
}
// 否则通过 loadServiceMethod 方法获取到对应 Method 并 invoke
return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
}
});
}
可以看到,实际上 Service
对象的获取是通过动态代理实现的。这里首先通过 validateServiceInterface
方法对接口进行了检测,之后通过动态代理对该接口进行了代理。
对于 Object
类本身独有以及对应平台本身就存在的方法,就照常调用,否则通过 loadServiceMethod
对 Service
中对应的 Method
对象进行处理,之后对其调用 invoke
方法。
这里说明了 Retrofit
不是在创建 Service
接口对应对象时就立即对所有该接口中的所有方法都进行注解的解析,而是采用了在方法被调用时才进行注解的解析这种懒加载的思想。
接着我们看看 validateServiceInterface
方法:
private void validateServiceInterface(Class<?> service) {
// 判断是否是接口
if (!service.isInterface()) {
throw new IllegalArgumentException("API declarations must be interfaces.");
}
// 判断该接口及其继承的所有接口是否包含了范型参数,如果包含则抛出异常
Deque<Class<?>> check = new ArrayDeque<>(1);
check.add(service);
while (!check.isEmpty()) {
Class<?> candidate = check.removeFirst();
if (candidate.getTypeParameters().length != 0) {
StringBuilder message = new StringBuilder("Type parameters are unsupported on ").append(candidate.getName());
if (candidate != service) {
message.append(" which is an interface of ").append(service.getName());
}
throw new IllegalArgumentException(message.toString());
}
Collections.addAll(check, candidate.getInterfaces());
}
// 如果在创建Retrofit时设置了很急切地对Service的方法进行处理,则对非平台独有且非static的方法通过 loadServiceMethod 方法进行处理。
if (validateEagerly) {
Platform platform = Platform.get();
for (Method method : service.getDeclaredMethods()) {
if (!platform.isDefaultMethod(method) && !Modifier.isStatic(method.getModifiers())) {
loadServiceMethod(method);
}
}
}
}
首先,这个方法对 service
进行了检测,保证了它是一个接口并且它和它继承的类中没有范型参数。
之后如果在 Retrofit 创建时设置 validateEagerly
为 true 的话,会对 Service 中所有非平台独有且非static的方法通过 loadServiceMethod
方法提前进行处理
Service 中方法的解析
那么我们来看看 loadServiceMethod
究竟做了些什么:
ServiceMethod<?> loadServiceMethod(Method method) {
ServiceMethod<?> result = serviceMethodCache.get(method);
if (result != null) return result;
synchronized (serviceMethodCache) {
result = serviceMethodCache.get(method);
if (result == null) {
result = ServiceMethod.parseAnnotations(this, method);
serviceMethodCache.put(method, result);
}
}
return result;
}
首先它会采用 Double Check 的方式尝试从 serviceMethodCache
缓存中获取 ServiceMethod
对象,如果获取不到则通过 ServiceMethod.parseAnnotations
方法对该 Method 的注解进行处理并将得到的 ServiceMethod
对象加入了缓存。
也就是说为了避免多次对方法的注解进行处理,Retrofit 采用了一个 serviceMethodCache
对解析后的 ServiceMethod
进行缓存。
接着我们就来看看,parseAnnotations
方法是如何对方法的注解进行解析的。
static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);
Type returnType = method.getGenericReturnType();
if (Utils.hasUnresolvableType(returnType)) {
throw methodError(method,
"Method return type must not include a type variable or wildcard: %s", returnType);
}
if (returnType == void.class) {
throw methodError(method, "Service methods cannot return void.");
}
return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
}
这里先通过 RequestFactory.parseAnnotations
方法对注解解析并获得了一个 RequestFactory
对象。
之后又通过 HttpServiceMethod.parseAnnotations
方法传入了 requestFactory
继续进行注解的解析并获得 ServiceMethod
对象
注解解析
我们先看看 RequestFactory.parseAnnotations
:
static RequestFactory parseAnnotations(Retrofit retrofit, Method method) {
return new Builder(retrofit, method).build();
}
它把 Method
传入 Builder
从而构建了一个新的 RequestFactory
:
Builder(Retrofit retrofit, Method method) {
this.retrofit = retrofit;
this.method = method;
this.methodAnnotations = method.getAnnotations();
this.parameterTypes = method.getGenericParameterTypes();
this.parameterAnnotationsArray = method.getParameterAnnotations();
}
Builder 中通过反射获取到method
所包含的注解、参数包含的