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所包含的注解、参数包含的范型以及参数的注解。
接着看看 build 方法:
RequestFactory build

本文深入剖析了Retrofit如何基于OkHttp进行网络请求的封装,包括Retrofit对象构建、Service对象创建、注解解析、HTTP请求处理等关键步骤。Retrofit利用动态代理和注解解析,简化了网络请求接口的使用,同时支持了对协程的整合。文章详细介绍了从创建Retrofit实例到执行网络请求的整个流程,展示了Retrofit优雅地扩展OkHttp功能的设计思想。
最低0.47元/天 解锁文章
1271

被折叠的 条评论
为什么被折叠?



