做效果图兼职的网站/app拉新推广代理
Android热门框架解析,你确定不来看看吗?
OkHttp框架源码深度剖析【Android热门框架分析第一弹】
Retrofit框架源码深度剖析【Android热门框架分析第二弹】
什么是Retrofit?
准确来说,Retrofit 是一个 RESTful 的 HTTP 网络请求框架的封装。
原因:网络请求的工作本质上是 OkHttp 完成,而 Retrofit 仅负责 网络请求接口的封装,当前最新的Retrofit底层使用的是OkHttp3。
上图说明了如下几点:
1. App应用程序通过 Retrofit 请求网络,实际上是使用 Retrofit 接口层封装请求参数、Header、Url 等信息,之 后由 OkHttp 完成后续的请求操作。
2. 在服务端返回数据之后,OkHttp 将原始的结果交给 Retrofit,Retrofit根据用户的需求对结果进行解析。
所以,网络请求的本质仍旧是OkHttp完成的,retrofit只是帮使用者来进行工作简化的,比如配置网络,处理数据等 工作,提高这一系列操作的复用性。这也就是网上流行的一个不太准确的总结:okhttp是瑞士军刀,retrofit则是将这把瑞士军刀包装成了一个非常好用的指甲钳(专治甲沟炎哈哈)。
为什么选择Retrofit?
我曾经写过一篇关于热门网络请求框架OkHttp的详细解析,但为什么我还要对Retrofit继续写文章呢?这肯定是因为Retrofit有它独特的优势。虽然OkHttp非常好用,但它也存在一些局限性。OkHttp最初的设计就是为了实现网络请求,而在实际应用中我们发现了一些缺点,这也促使了Retrofit的诞生。虽然Retrofit只是对OkHttp进行了封装,但它带来了很多显著的优点。
-
超级解耦:
Retrofit实现了接口定义、接口参数和接口回调的彻底解耦。这种设计不仅让代码更清晰、更易于维护,也使得代码的复用性大大提高。 -
灵活的HTTP客户端配置:
在Retrofit中,你可以轻松配置不同的HTTP客户端来实现网络请求,如OkHttp、HttpClient等。这种灵活性使得开发者可以根据项目需求选择最合适的HTTP客户端。 -
同步与异步支持:
Retrofit同时支持同步和异步请求,提供了更丰富的使用场景。此外,它还与RxJava无缝集成,方便进行响应式编程。 -
多种数据格式支持:
通过配置不同的反序列化工具类,Retrofit可以解析多种数据格式,如JSON、XML等。这样,开发者可以根据API的具体需求选择最适合的数据格式。 -
高效的请求速度:
由于Retrofit底层使用OkHttp,再加上其优秀的封装和优化,Retrofit在请求速度上表现优异。此外,它使用方便、灵活且简洁,极大地提高了开发效率。
就笔者自身使用而言,使用Retrofit会比使用OkHttp舒服的多,开发效率也会提升,okhttp需要手动解析数据,而Retrofit通过内置的转换器可以自动解析数据;同时,在网络请求时,Retrofit会自动帮你进行线程切换;除此之外,还使用动态代理的方式自动生成网络请求Api,当你在使用完OkHttp以后,再使用Retrofit,一定会让你欲罢不能的!
Retrofit对OkHttp做了什么?
Retrofit并没有改变网络请求的本质,也无需改变,因为Okhttp已经足够强大,Retrofit的封装可以说是很强大,里 面涉及到一堆的设计模式,可以通过注解直接配置请求,可以使用不同的http客户端,虽然默认是用http ,可以使用 不同Json Converter 来序列化数据,同时提供对RxJava的支持,使用Retrofit + OkHttp + RxJava 可以说是目前比较 潮的一套框架,但是需要有比较高的门槛。
OkHttp网络请求:
我们先来看看OkHttp是怎么进行网络请求的。
库的引入
implementation 'com.squareup.retrofit2:retrofit:2.11.0'// 如果使用 Gson 进行 JSON 解析implementation 'com.squareup.retrofit2:converter-gson:2.11.0'
如果你没有使用过OkHttp,可以看我之前的文章,在顶部有介绍;当然,你也直接看下面的OkHttp的简单使用。
// 1.创建clientval client = OkHttpClient.Builder().cookieJar(CookieJar.NO_COOKIES).callTimeout(10000, TimeUnit.MILLISECONDS).build()// 2.创建requestval request = Request.Builder().url("http://10.34.12.156:68080/admin-api").addHeader("Content-Type", "application/json").get().build()// 3.构建call对象val call = client.newCall(request)// 4.1调用call对象的同步请求方法val response = call.execute() // response对象中保存的有返回的响应参数4.2调用call对象的异步请求方法call.enqueue(object : Callback {override fun onFailure(call: Call, e: IOException) {Log.d("a", "onFailure:") // 失败回调}override fun onResponse(call: Call, response: Response) {Log.d("b", "onResponse:") // 成功回调}})
Step1:创建HttpClient对象,也就是构建一个网络类型的实例,一般会将所有的网络请求使用同一个单例对象。
Step2:构建Request,也就是构建一个具体的网络请求对象,具体的请求url,请求头,请求体等等。
Step3:构建请求Call,也就是将具体的网络请求与执行请求的实体进行绑定,形成一个具体的正式的可执行实体。
Step4: 后面就进行网络请求了,然后处理网络请求的数据了。
是不是看着还挺容易的,一步步的,很好使用呢?
Okhttp给用户留下的问题:
1)用户网络请求的接口配置繁琐,尤其是需要配置请求body,请求头,参数的时候;
2)数据解析过程需要用户手动拿到responsbody进行解析,不能复用;
3)无法适配自动进行线程的切换。
那么这几个问题谁来解决? 对,retrofit!
Retrofit网络请求
1、创建 Retrofit 实例并配置:val retrofit: Retrofit = Retrofit.Builder().baseUrl(baseUrl) // 指定根路径.addConverterFactory(GsonConverterFactory.create()) // 指定解析数据时使用的转换库.build()2、创建服务接口实例:val service: AppService = retrofit.create(AppService::class.java) 这个AppService就是定义的接口3、发起网络请求:val call: Call<ResponseBody> = service.getPostData(user, pwd) ResponseBody是数据Bean,getPostData是接口定义的方法call.enqueue(object : Callback<ResponseBody> {override fun onResponse(call: Call<ResponseBody>?, response: Response<ResponseBody>) {if (response.isSuccessful) {// 请求成功处理Log.d("HttpUtil", "成功")// 这里可以处理响应数据} else {// 请求失败处理Log.e("HttpUtil", "请求失败")}}override fun onFailure(call: Call<ResponseBody>?, t: Throwable) {// 网络请求失败处理t.printStackTrace()Log.e("HttpUtil", "网络请求失败")}})
- step1
构建一个网络请求的载体对象,和okhttp构建OkhttpClient对象有一样的意义,只不过 retrofit在build的时候有非常多的初始化内容,这些内容可以为后面网络请求提供准备,如准备 现成转换Executor, Gson convert,RxJavaCallAdapter。
- step2(精髓)
使用 retrofit.create()
方法创建定义的服务接口 AppService
的实例。为统一配置网络请求完成动态代理的设置。
- step3
建具体网络请求对象Request(service),在这个阶段要完成的任务:
1)将接口中的注解翻译成对应的 参数;
2)确定网络请求接口的返回值response类型以及对应的转换器;
3)讲Okhttp的Request封装成为Retrofit的 OKhttpCall。
总结来说,就是根据请求service 的Interface来封装Okhttp请求Request。
通过下图,让我们来总结一下,retrofit是如何来封装okhttp请求的。(左图的Request都是OkHttp哈,写错了)
大体的网络流程是一致的,毕竟都是通过okhttp进行网络请求。主要的步骤都是:创建网络请求实体client->构建真 正的网络请求-> 将网络请求方案与真正的网络请求实体结合构成一个请求Call->执行网络请求->处理返回数据->处理 Android 平台的线程问题。
在上图中,我们看到的对比最大的区别是什么?
- 1)okhttp创建的是OkhttpClient,然而retrofit创建的是 Retrofit实例
- 2)构建蓝色的Requet的方案,retrofit是通过注解来进行的适配
- 3)配置Call的过程中,retrofit是利用Adapter适配的Okhttp 的Call
- 4)相对okhttp,retrofit会对responseBody进行 自动的Gson解析
- 5)相对okhttp,retrofit会自动的完成线程的切换。
那么retrofit是如何完成这几点的封装的呢?我们继续往下走,撸源码。
Retrofit追溯源码
我们从Retrofit构建这里开始入手,我们通过build拿到我们的retrofit对象,我们看看build干了什么。
public Retrofit build() {// 1. 检查 baseUrl 是否为 nullif (this.baseUrl == null) {throw new IllegalStateException("Base URL required.");} else {// 2. 检查 callFactory 是否为 null,如果是,则使用默认的 OkHttpClientCall.Factory callFactory = this.callFactory;if (callFactory == null) {callFactory = new OkHttpClient();}// 3. 检查 callbackExecutor 是否为 null,如果是,则使用默认的 Platform.callbackExecutorExecutor callbackExecutor = this.callbackExecutor;if (callbackExecutor == null) {callbackExecutor = Platform.callbackExecutor;}// 4. 获取 BuiltInFactories 实例BuiltInFactories builtInFactories = Platform.builtInFactories;// 5. 创建 callAdapterFactories 列表并添加默认的 CallAdapter 工厂List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);List<? extends CallAdapter.Factory> defaultCallAdapterFactories = builtInFactories.createDefaultCallAdapterFactories(callbackExecutor);callAdapterFactories.addAll(defaultCallAdapterFactories);// 6. 创建 defaultConverterFactories 列表List<? extends Converter.Factory> defaultConverterFactories = builtInFactories.createDefaultConverterFactories();int defaultConverterFactoriesSize = defaultConverterFactories.size();// 7. 创建 converterFactories 列表并添加默认的 Converter 工厂List<Converter.Factory> converterFactories = new ArrayList<>(1 + this.converterFactories.size() + defaultConverterFactoriesSize);converterFactories.add(new BuiltInConverters());converterFactories.addAll(this.converterFactories);converterFactories.addAll(defaultConverterFactories);// 8. 创建并返回新的 Retrofit 实例return new Retrofit(callFactory, this.baseUrl, Collections.unmodifiableList(converterFactories), defaultConverterFactoriesSize, Collections.unmodifiableList(callAdapterFactories), defaultCallAdapterFactories.size(), callbackExecutor, this.validateEagerly);}
}
抓重点:
// 2. 检查 callFactory 是否为 null,如果是,则使用默认的 OkHttpClient
Call.Factory callFactory = this.callFactory;
if (callFactory == null) {
callFactory = new OkHttpClient();
}
Call.Factory
是 retrofit2.Call.Factory
接口的一个实例,它的主要目的是为每一个网络请求创建一个新的 Call
对象。也就是说,这里其实就是去取Call对象。这里实例化了OkHttpClient对象,我们继续往下看。
可以看到,这个类实现了Call.Factory这个接口,看到这里,有没有很熟悉?是不是和OkHttp一样的套路?(如果你没看过我写的一篇OkHttp的文章,你可以去看下)
public interface Call extends Cloneable {/** Returns the original request that initiated this call. */Request request();Response execute() throws IOException;void enqueue(Callback responseCallback);void cancel();boolean isExecuted();boolean isCanceled();Timeout timeout();Call clone();interface Factory {Call newCall(Request request);}
}
还记得OkHttp通过newCall方法构建的对象吗?有没有恍然大悟?原来和OkHttp是一样的,只是包装了一下而已。这里的Call.Factory
负责创建 Call
对象,而 Call
对象代表了一个可以执行的 HTTP 请求。通过 Call.Factory
,Retrofit
可以灵活地创建这些 Call
对象,支持不同的 HTTP 客户端实现。
总结:
OkHttpClient是 http 请求的载体包含socket等可以复用的对象,协议配置等等一切。 Request 创建的是一个具体的有url,header,等请求信息的一个网络请求,表示这个具体的请求。
Call 通往请求的,去执行请求的整个过程的一个抽象。也是进行网络请求的最终接口。
所以,此次调用,目的就是创建了一个OkHttpClient,换句话说,这里的调用就是生产 Okhttp网络请求需要的请求Call的,以备后面进行真正的网络请求。
接下来,我们看第三步
// 3. 检查 callbackExecutor 是否为 null,如果是,则使用默认的 Platform.callbackExecutor
Executor callbackExecutor = this.callbackExecutor;
if (callbackExecutor == null) {//这里默认为空,所以我们只要不设置,这里都会自动取获取的
callbackExecutor = Platform.callbackExecutor;
}
我们先看,Executor是个什么玩意。非常简单,一个接口里面有一个抽象方法。
public interface Executor {void execute(Runnable var1);
}
我们再去看Platform.callbackExecutor拿到了什么。
final class Platform {@Nullablestatic final Executor callbackExecutor;static final Reflection reflection;static final BuiltInFactories builtInFactories;private Platform() {}static {switch (System.getProperty("java.vm.name")) {case "Dalvik":callbackExecutor = new AndroidMainExecutor();if (VERSION.SDK_INT >= 24) {reflection = new Reflection.Android24();builtInFactories = new BuiltInFactories.Java8();} else {reflection = new Reflection();builtInFactories = new BuiltInFactories();}break;......}}
}
可以看到,在android开发中,我们的 Platform.callbackExecutor会默认的拿到一个AndroidMainExecutor对象。我们再继续深入。
final class AndroidMainExecutor implements Executor {private final Handler handler = new Handler(Looper.getMainLooper());AndroidMainExecutor() {}public void execute(Runnable r) {this.handler.post(r);}
}
可以看到,其实就是传入了一个AndroidMainExecutor对象,并且它持有主线程的looper,看到这里,你有没有想到为什么Retrofit可以自动完成线程切换呢?原因就在这里。其实,只要是android中的通信,基本都离不开handler的。 虽然这里已经把答案揭晓了,但是可能大家还是不理解,它是怎么实现线程切换的。
我们继续看,它到底是怎么完成线程切换的。
callbackExecutor = new AndroidMainExecutor(); if (VERSION.SDK_INT >= 24) {reflection = new Reflection.Android24();builtInFactories = new BuiltInFactories.Java8();还记得这行代码吗? } else {reflection = new Reflection();builtInFactories = new BuiltInFactories();还记得这行代码吗? }
我们通过new BuiltInFactories()获得了这个BuiltInFactories这个对象。
class BuiltInFactories {BuiltInFactories() {}List<? extends CallAdapter.Factory> createDefaultCallAdapterFactories(@Nullable Executor callbackExecutor) {return Collections.singletonList(new DefaultCallAdapterFactory(callbackExecutor));}List<? extends Converter.Factory> createDefaultConverterFactories() {return Collections.emptyList();}@TargetApi(24)static final class Java8 extends BuiltInFactories {Java8() {}List<? extends CallAdapter.Factory> createDefaultCallAdapterFactories(@Nullable Executor callbackExecutor) {return Arrays.asList(new CompletableFutureCallAdapterFactory(), new DefaultCallAdapterFactory(callbackExecutor));}List<? extends Converter.Factory> createDefaultConverterFactories() {return Collections.singletonList(new OptionalConverterFactory());}}
}
BuiltInFactories builtInFactories = Platform.builtInFactories;在这里我们拿到了我们的builtInFactories的实例对象。
在第5步是,我们构建工厂的时候,用到了builtInFactories
// 5. 创建 callAdapterFactories 列表并添加默认的 CallAdapter 工厂List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);List<? extends CallAdapter.Factory> defaultCallAdapterFactories = builtInFactories.createDefaultCallAdapterFactories(callbackExecutor);callAdapterFactories.addAll(defaultCallAdapterFactories);
调用builtInFactories.createDefaultCallAdapterFactories(callbackExecutor),这里传入了我们在第3步拿到的callbackExecutor,继续看createDefaultCallAdapterFactories这个方法做了什么。
List<? extends CallAdapter.Factory> createDefaultCallAdapterFactories(@Nullable Executor callbackExecutor) {return Collections.singletonList(new DefaultCallAdapterFactory(callbackExecutor));}
这里return Collections.singletonList(new DefaultCallAdapterFactory(callbackExecutor));我们继续看 DefaultCallAdapterFactory。
DefaultCallAdapterFactory有个静态内部类。这里通过构造方法,传入我们的Executor对象和我们的Call。当我们调用enqueue方法时,其实就是调用我们call对象的exqueue方法,在exqueue方法中又将我们的onResponse和onFailure通过Executor的execute包起来,从而实现Retrofit的自动切换线程的功能。(看到这,可能你有点懵,你现在要知道一个点,它其实就是通过Executor将请求结果的响应包装起来,发送给主线程。现在你可能无法将知识串起来,先记住,继续往下看)
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;}public void enqueue(final Callback<T> callback) {Objects.requireNonNull(callback, "callback == null");this.delegate.enqueue(new Callback<T>() {public void onResponse(Call<T> call, Response<T> response) {ExecutorCallbackCall.this.callbackExecutor.execute(() -> {if (ExecutorCallbackCall.this.delegate.isCanceled()) {callback.onFailure(ExecutorCallbackCall.this, new IOException("Canceled"));} else {callback.onResponse(ExecutorCallbackCall.this, response);}});}public void onFailure(Call<T> call, Throwable t) {ExecutorCallbackCall.this.callbackExecutor.execute(() -> {callback.onFailure(ExecutorCallbackCall.this, t);});}});}.......}
然后其他的话,我们还看到很多factory,这些factory的话基本上是给我们解析数据等其他作用的,这里暂不深入。
现在,我们已经构建好了Retrofit,这些步骤用于进行后面请求的需要的内容的一个准备工作。也就是封装Okhttp需要的准备工作。
我们继续往下看,下面两行代码需要连起来才能正确的被阅读,因为,在create里面是使用了动态代理的技术方案,而动态代理是运行时生效的。AppService就是一个接口(相当于Api)里面有很多抽象方法就相当于不同的请求。
2、创建服务接口实例:val service: AppService = retrofit.create(AppService::class.java) 这个AppService就是定义的接口3、发起网络请求:val call: Call<ResponseBody> = service.getPostData(user, pwd) ResponseBody是数据Bean,getPostData是接口定义的方法
这里我们继续看create的代码。
public <T> T create(final Class<T> service) {// 验证服务接口是否合法this.validateServiceInterface(service);// 创建代理实例return Proxy.newProxyInstance(service.getClassLoader(), // 获取服务接口的类加载器new Class[]{service}, // 代理的接口列表,这里只有一个接口new InvocationHandler() { // 代理调用处理器private final Object[] emptyArgs = new Object[0]; // 空参数数组,用于没有参数的方法@Nullablepublic Object invoke(Object proxy, Method method, @Nullable Object[] args) throws Throwable {// 如果是Object类的方法,直接调用if (method.getDeclaringClass() == Object.class) {return method.invoke(this, args);} else {// 如果参数为空,则使用空参数数组args = args != null ? args : this.emptyArgs;// 获取平台特定的反射实例Reflection reflection = Platform.reflection;// 如果是默认方法,通过反射调用默认方法return reflection.isDefaultMethod(method)? reflection.invokeDefaultMethod(method, service, proxy, args)// 否则,通过Retrofit加载服务方法,并调用它: Retrofit.this.loadServiceMethod(service, method).invoke(proxy, args);}}});
}
这里我们先将一下什么是动态代理。
在动态代理中,代理对象不需要实现接口,但是目标对象还是需要实现接口。代理对象的生成,是利用 JDK 的 API ,动态的在内存中构建代理对象。
在 Java 中,java.lang.reflect.Proxy
类为对象生成代理提供了方法:
Proxy.newProxyInstance(service.getClassLoader(), new Class[]{service}, new InvocationHandler())
- 参数一 service.getClassLoader():返回的是
service
接口的类加载器,用于加载这个接口及其方法。 - 参数二 new Class[]{service}:指定这个代理类能够代理目标对象的哪些方法和接口;它包含了所有需要被代理的方法的声明
- 参数三
InvocationHandler
:用来指定生成的代理对象在方法被调用时如何进行处理;
所以,service
参数本身并不包含实际的方法实现,它只是一个接口的类对象,定义了方法的签名(方法名、参数类型、返回类型等)。当你调用这些方法时,实际上会进入 InvocationHandler
的 invoke
方法中,通过反射等机制来实现对这些方法的具体处理,从而实现代理模式。
因此,service
在这里不包含具体方法的实现,而是作为动态代理的基础接口,通过动态代理技术来生成实际的代理对象,以便在运行时动态处理方法调用。
说完动态代理,我们继续看代码。
// 验证服务接口是否合法this.validateServiceInterface(service);
我将validateServiceInterface方法分为两部分。
private void validateServiceInterface(Class<?> service) {第一部分if (!service.isInterface()) {throw new IllegalArgumentException("API declarations must be interfaces.");} else {Deque<Class<?>> check = new ArrayDeque(1);check.add(service);while(!check.isEmpty()) {Class<?> candidate = (Class)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());}第二部分if (this.validateEagerly) {Reflection reflection = Platform.reflection;Method[] var9 = service.getDeclaredMethods();int var5 = var9.length;for(int var6 = 0; var6 < var5; ++var6) {Method method = var9[var6];if (!reflection.isDefaultMethod(method) && !Modifier.isStatic(method.getModifiers()) && !method.isSynthetic()) {this.loadServiceMethod(service, method);}}}}}
第一部分,主要做的是检查我们的service是否是接口。如果不是接口,我们将继续检查它是否是泛型接口,如果定义了则抛出异常。这是因为,Retrofit 在创建服务接口代理对象之前检查是否是泛型接口,是为了确保动态代理能够正确地工作。只有非泛型的接口才能确保在运行时通过动态代理生成有效的代理对象,并且能够正确地调用接口方法,实现网络请求和响应的处理。
我们再看第二部分,第二部分其实是去进行实际的方法加载和验证,对我们写好的网络请求进行验证。这里默认为false,从而实现Retrofit在创建服务接口实例时不会急切地验证接口方法。相反,它仅在实际调用方法时进行验证。
这里我们分析一下它是如何验证的。这里将我们获得到的所有的方法进行遍历,然后调用下面这个方法进行验证。
this.loadServiceMethod(service, method);
我们继续看loadServiceMethod方法。
// 加载服务方法,并将其缓存起来// 参数:// service: 要加载方法的服务接口类// method: 要加载的方法ServiceMethod<?> loadServiceMethod(Class<?> service, Method method) {while(true) {// 从缓存中查找对应方法的 ServiceMethod 对象Object lookup = this.serviceMethodCache.get(method);if (lookup instanceof ServiceMethod) {// 如果缓存中已经有了该方法的 ServiceMethod 对象,则直接返回return (ServiceMethod) lookup;}if (lookup == null) {// 如果缓存中没有该方法的 ServiceMethod 对象,则加锁尝试创建Object lock = new Object();synchronized (lock) {// 双重检查,确保只有一个线程创建 ServiceMethod 对象lookup = this.serviceMethodCache.putIfAbsent(method, lock);if (lookup == null) {// 创建 ServiceMethod 对象并放入缓存ServiceMethod result;try {result = ServiceMethod.parseAnnotations(this, service, method);} catch (Throwable var10) {// 如果创建过程中出现异常,则移除缓存并抛出异常this.serviceMethodCache.remove(method);throw var10;}this.serviceMethodCache.put(method, result);return result;}}}// 如果有其他线程正在创建 ServiceMethod 对象,则等待该线程完成并获取结果synchronized (lookup) {Object result = this.serviceMethodCache.get(method);if (result != null) {return (ServiceMethod) result;}}}}
这里注释里面写的很清楚了。用缓存提高效率,没有缓存则加锁创建,这里使用的是双重检查。不过目前,我们还是没有看见它是怎么验证我们的方法的正确性的,所以我们需要继续往下看。
result = ServiceMethod.parseAnnotations(this, service, method);
这里就是我们验证的关键,调用parseAnnotations
static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Class<?> service, Method method) {RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, service, method);Type returnType = method.getGenericReturnType();if (Utils.hasUnresolvableType(returnType)) {throw Utils.methodError(method, "Method return type must not include a type variable or wildcard: %s", new Object[]{returnType});} else if (returnType == Void.TYPE) {throw Utils.methodError(method, "Service methods cannot return void.", new Object[0]);} else {return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);}}
我们从parseAnnotations一路分析下去,我们将会看到以下代码:
private void parseMethodAnnotation(Annotation annotation) {if (annotation instanceof DELETE) {this.parseHttpMethodAndPath("DELETE", ((DELETE)annotation).value(), false);} else if (annotation instanceof GET) {this.parseHttpMethodAndPath("GET", ((GET)annotation).value(), false);} else if (annotation instanceof HEAD) {this.parseHttpMethodAndPath("HEAD", ((HEAD)annotation).value(), false);} else if (annotation instanceof PATCH) {this.parseHttpMethodAndPath("PATCH", ((PATCH)annotation).value(), true);} else if (annotation instanceof POST) {this.parseHttpMethodAndPath("POST", ((POST)annotation).value(), true);} else if (annotation instanceof PUT) {this.parseHttpMethodAndPath("PUT", ((PUT)annotation).value(), true);} else if (annotation instanceof OPTIONS) {this.parseHttpMethodAndPath("OPTIONS", ((OPTIONS)annotation).value(), false);} else if (annotation instanceof HTTP) {HTTP http = (HTTP)annotation;this.parseHttpMethodAndPath(http.method(), http.path(), http.hasBody());} else if (annotation instanceof retrofit2.http.Headers) {retrofit2.http.Headers headers = (retrofit2.http.Headers)annotation;String[] headersToParse = headers.value();if (headersToParse.length == 0) {throw Utils.methodError(this.method, "@Headers annotation is empty.", new Object[0]);}this.headers = this.parseHeaders(headersToParse, headers.allowUnsafeNonAsciiValues());} else if (annotation instanceof Multipart) {if (this.isFormEncoded) {throw Utils.methodError(this.method, "Only one encoding annotation is allowed.", new Object[0]);}this.isMultipart = true;} else if (annotation instanceof FormUrlEncoded) {if (this.isMultipart) {throw Utils.methodError(this.method, "Only one encoding annotation is allowed.", new Object[0]);}this.isFormEncoded = true;}}
这里对我们写好的方法进行验证。 那么方法正确难道方法就一定可行吗?所以,Retrofit还对我们的请求结果进行验证了。
return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
通过这行代码实现,代码里面注释写的很详细。
// 解析方法的注解和参数,生成对应的 HttpServiceMethod 对象// 参数:// retrofit: Retrofit 实例,用于创建适配器和转换器// method: 当前要解析的方法// requestFactory: 请求工厂,包含请求方法和参数信息static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(Retrofit retrofit, Method method, RequestFactory requestFactory) {// 是否为 Kotlin 的挂起函数boolean isKotlinSuspendFunction = requestFactory.isKotlinSuspendFunction;// 是否挂起函数需要返回响应对象boolean continuationWantsResponse = false;// 挂起函数返回体是否可为 nullboolean continuationBodyNullable = false;// 挂起函数返回类型是否为 Unitboolean continuationIsUnit = false;// 获取方法的所有注解Annotation[] annotations = method.getAnnotations();Object adapterType;Type responseType;// 如果是 Kotlin 挂起函数if (isKotlinSuspendFunction) {Type[] parameterTypes = method.getGenericParameterTypes();// 获取返回类型的实际泛型参数responseType = Utils.getParameterLowerBound(0, (ParameterizedType) parameterTypes[parameterTypes.length - 1]);// 如果返回类型是 Response 类型且包含泛型参数if (Utils.getRawType(responseType) == Response.class && responseType instanceof ParameterizedType) {responseType = Utils.getParameterUpperBound(0, (ParameterizedType) responseType);continuationWantsResponse = true;} else {// 如果返回类型是 retrofit2.Call 类型,则抛出异常if (Utils.getRawType(responseType) == retrofit2.Call.class) {throw Utils.methodError(method, "Suspend functions should not return Call, as they already execute asynchronously.\nChange its return type to %s", new Object[]{Utils.getParameterUpperBound(0, (ParameterizedType) responseType)});}// 判断返回类型是否为 UnitcontinuationIsUnit = Utils.isUnit(responseType);}// 创建一个参数化类型的 Call 类型adapterType = new Utils.ParameterizedTypeImpl((Type) null, retrofit2.Call.class, new Type[]{responseType});// 确保 SkipCallbackExecutorImpl 存在annotations = SkipCallbackExecutorImpl.ensurePresent(annotations);} else {// 非 Kotlin 挂起函数,获取方法的返回类型adapterType = method.getGenericReturnType();}// 创建调用适配器CallAdapter<ResponseT, ReturnT> callAdapter = createCallAdapter(retrofit, method, (Type) adapterType, annotations);// 获取适配器的响应类型responseType = callAdapter.responseType();// 校验返回类型是否合法if (responseType == okhttp3.Response.class) {throw Utils.methodError(method, "'" + Utils.getRawType(responseType).getName() + "' is not a valid response body type. Did you mean ResponseBody?", new Object[0]);} else if (responseType == Response.class) {throw Utils.methodError(method, "Response must include generic type (e.g., Response<String>)", new Object[0]);} else if (requestFactory.httpMethod.equals("HEAD") && !Void.class.equals(responseType) && !Utils.isUnit(responseType)) {throw Utils.methodError(method, "HEAD method must use Void or Unit as response type.", new Object[0]);} else {// 创建响应体转换器Converter<ResponseBody, ResponseT> responseConverter = createResponseConverter(retrofit, method, responseType);// 获取 Retrofit 的调用工厂Call.Factory callFactory = retrofit.callFactory;// 如果不是 Kotlin 挂起函数,返回 CallAdapted 对象if (!isKotlinSuspendFunction) {return new CallAdapted(requestFactory, callFactory, responseConverter, callAdapter);} else {// 如果是 Kotlin 挂起函数,根据需要返回 Response 或 Body 的挂起函数对象return (HttpServiceMethod<ResponseT, ReturnT>) (continuationWantsResponse ?new SuspendForResponse<>(requestFactory, callFactory, responseConverter, callAdapter) :new SuspendForBody<>(requestFactory, callFactory, responseConverter, callAdapter, continuationBodyNullable, continuationIsUnit));}}}
好的,我们所有的准备工作以及相关的原理解析,我们都已经说明白了。那么当我们调用方法的时候,是怎么实现这一切的呢?是怎么串起来的呢?
3、发起网络请求:val call: Call<ResponseBody> = service.getPostData(user, pwd) ResponseBody是数据Bean,getPostData是接口定义的方法call.enqueue(object : Callback<ResponseBody> {override fun onResponse(call: Call<ResponseBody>?, response: Response<ResponseBody>) {if (response.isSuccessful) {// 请求成功处理Log.d("HttpUtil", "成功")// 这里可以处理响应数据} else {// 请求失败处理Log.e("HttpUtil", "请求失败")}}override fun onFailure(call: Call<ResponseBody>?, t: Throwable) {// 网络请求失败处理t.printStackTrace()Log.e("HttpUtil", "网络请求失败")}})
这里我们调用getPostData方法进行网络请求,结合retrofit的动态代理,我们将会执行下面的invoke方法。
public Object invoke(Object proxy, Method method, @Nullable Object[] args) throws Throwable {if (method.getDeclaringClass() == Object.class) {return method.invoke(this, args);} else {args = args != null ? args : this.emptyArgs;Reflection reflection = Platform.reflection;return reflection.isDefaultMethod(method) ? reflection.invokeDefaultMethod(method, service, proxy, args) : Retrofit.this.loadServiceMethod(service, method).invoke(proxy, args);}}
注意看,Retrofit.this.loadServiceMethod(service, method).invoke(proxy, args);
当我们通过loadServiceMethod拿到我们的方法的时候,又调用了invoke函数。这里其实就是发起网络请求以后,通过invoke结合适配器的adapt方法将我们的数据直接转为我们的Bean。(或者结合RxJava使用)
我们看看HttpServiceMethod.adapt()方法:(其实是ServiceMethod的只不过HttpServiceMethod实现了ServiceMethod)。这里其实就是适配器的工作,你需要什么类型的数据就通过适配器适配,返回适配后的对象就是了。
final ReturnT invoke(Object instance, Object[] args) {retrofit2.Call<ResponseT> call = new OkHttpCall(this.requestFactory, instance, args, this.callFactory, this.responseConverter);return this.adapt(call, args);}
总结
一般的Retrofit网络请求的操作是指 Call.excute() & Call.enqueue()的过程,这个过程才是真正的网络请求,因为,网络配置、请求地址配置、Call适配、网络请求requestBody、返回值responseBody转化适配准备工作都已经完成。
在进行网络请求的执行的时候,基本上就是调用,ServiceMethod中设置的各个内容
1)OkHttpCall进行网络请求,实则是进行okhttp的网络请求;
2)利用 converter进行网络请求数据的转换,一般是Gson();
3)利用 rxjava observable构建 rxjava类型的责任链访问方案,并进行线程切换;
4) 如果没有rxjava的添加,那么就使用默认的callAdapter里面的callbackExecutor进行线程的切换 , 进行网络请求.
学生记录所做,如有错误,欢迎指出。
相关文章:

Retrofit框架源码深度剖析【Android热门框架分析第二弹】
Android热门框架解析,你确定不来看看吗? OkHttp框架源码深度剖析【Android热门框架分析第一弹】 Retrofit框架源码深度剖析【Android热门框架分析第二弹】 什么是Retrofit? 准确来说,Retrofit 是一个 RESTful 的 HTTP 网络请求…...

C++Windows环境搭建(CLion)
文章目录 CLion下载安装CLion下载CLion安装新建项目新建一个文件基础设置字体设置clion中单工程多main函数设置 参考 CLion下载安装 CLion下载 打开网址:https://www.jetbrains.com/clion/download/ 点击Download进行下载。 CLion安装 双击下载好的安装包&…...

【区块链 + 智慧政务】省级一体化区块链平台 | FISCO BCOS应用案例
在加强数字政府建设的大背景下,科大讯飞广泛应用数字技术于政府管理服务,推动政府数字化、智能化运行。同时, 统筹推进业务、数据和技术的融合,提升跨地域、跨层级、跨部门和跨业务的协同管理和服务水平。 当前政务信息化建设中&…...

局域网远程共享桌面如何实现
在局域网内实现远程共享桌面,可以通过以下几种方法: 一、使用Windows自带的远程桌面功能: 首先,在需要被控制的电脑上右键点击“此电脑”,选择“属性”。 进入计算机属性界面后,点击“高级系统设置”&am…...

Ubuntu固定虚拟机的ip地址
1、由于虚拟机网络是桥接,所以ip地址会不停地变化,接下来我们就讲述ip如何固定 2、如果apt安装时报错W: Target CNF (multiverse/cnf/Commands-all) is configured multiple times in /etc/apt/sources.list:10, 检查 /etc/apt/sources.list…...

python破解密码·筛查和选择
破解密码时可能遇到的几种情况 ① 已知密码字符,破排序 ② 已知密码位数,破字符 ③ 已知密码类型,破字位 ④ 已知部分密码,破未知 ⑤ 啥都不知道,盲破,玩完 ⑥ 已知位数、字符、类型、部分密码中的几个&am…...

【将应用程序注册为系统服务】
在 Linux 系统中,将应用程序注册为系统服务可以使其在系统启动时自动运行,并且可以通过 systemctl 命令进行管理。/etc/systemd/system 目录是用于存放用户定义的 systemd 服务单元文件的目录。 将 Logstash 注册为系统服务 假设你已经安装了 Logstash…...

从0-1搭建一个web项目(路由目录分析)详解
本章分析vue路由目录文件详解 ObJack-Admin一款基于 Vue3.3、TypeScript、Vite3、Pinia、Element-Plus 开源的后台管理框架。在一定程度上节省您的开发效率。另外本项目还封装了一些常用组件、hooks、指令、动态路由、按钮级别权限控制等功能。感兴趣的小伙伴可以访问源码点个赞…...

Zabbix分布式监控
目录 分布式监控架构 实现分布式监控的步骤 优点和应用场景 安装Zabbix_Proxy Server端Web页面配置 测试 Zabbix 的分布式监控架构允许在大规模和地理上分散的环境中进行高效的监控。通过分布式监控,Zabbix 可以扩展其监控能力,支持大量主机和设备…...

前端面试39(关于git)
针对前端开发者的Git面试题可以覆盖Git的基础概念、常用命令、工作流程、团队协作、以及解决冲突等方面。以下是一些具体的Git面试 Git基础知识 什么是Git? Git是一个分布式版本控制系统,用于跟踪计算机文件的更改,并协调多个人共同在一个项…...

13--memcache与redis
前言:数据库读取速度较慢一直是无法解决的问题,大型网站应对的方式主要是使用缓存服务器来缓解这种情况,减少数据库访问次数,以提高动态Web等应用的速度、提高可扩展性。 1、简介 Memcached/redis是高性能的分布式内存缓存服务器…...

QT学习日记一
创建QT文件步骤 这是创建之后widget.cpp和widget.h文件的具体代码解释,也是主要操作的文件,其中main.cpp不用操作,ui则是图形化操作界面,综合使用时,添加一个元件要注意重编名和编译一下,才能在widget这类…...

redhat7.x 升级openssh至openssh-9.8p1
1.环境准备: OS系统:redhat 7.4 2.备份配置文件: cp -rf /etc/ssh /etc/ssh.bak cp -rf /usr/bin/openssl /usr/bin/openssl.bak cp -rf /etc/pam.d /etc/pam.d.bak cp -rf /usr/lib/systemd/system /usr/lib/systemd/system.bak 3.安装…...

Spring Cloud Eureka
引入:远程调用时,url是写死的 String url "http://127.0.0.1:9090/product/" orderInfo.getProductId(); 解决思路: 比如(医院,学校等)机构的电话号码发生变化,就需要通知各个使⽤…...

threejs
1.场景清空,释放内容 // 假设你已经有一个Three.js的场景对象scene// 函数:清空场景中的所有对象 function clearScene(scene) {while(scene.children.length > 0){const object scene.children[0];if(object.isMesh) {// 如果有几何体和材质&#…...

将pytorch 模型封装为c++ api 例子
在 PyTorch 中,通常使用 Python 来定义和训练模型,但是可以将训练好的模型导出为 TorchScript,然后在 C 中加载和使用。以下是一个详细的过程,展示了如何将 PyTorch 模型封装成 C API: 步骤 1: 定义和训练模型&#x…...

珠宝迷你秤方案
珠宝迷你秤作为一种便携式电子称重设备,因其小巧、便携、精度高等特点,广泛应用于各种需要精确称重的场景。可能这个目前在国内使用的人比较少,但在西方国家珠宝迷你秤却是可以用来送礼的物品。因为珠宝迷你秤的外观跟手机外观大多相似&#…...

边缘概率密度、条件概率密度、边缘分布函数、联合分布函数关系
目录 二维随机变量及其分布离散型随机变量连续型随机变量边缘分布边缘概率密度举例边缘概率密度 条件概率密度边缘概率密度与条件概率密度的区别边缘概率密度条件概率密度举个具体例子 参考资料 二维随机变量及其分布 离散型随机变量 把所有的概率,都理解成不同质量…...

软件架构之系统分析与设计方法(2)
软件架构之系统分析与设计方法(2) 8.4 面向对象的分析与设计8.4.1 面向对象的基本概念8.4.2 面向对象分析8.4.3 统一建模语言 8.5 用户界面设计8.5.1 用户界面设计的原则8.5.2 用户界面设计过程 8.6 工作流设计8.6.1 工作流设计概述8.6.2 工作流管理系统 8.7 简单分…...

AD确定板子形状
方法1 修改栅格步进值,手动绘制 https://cnblogs.com/fqhy/p/13768031.html 方法2 器件摆放确定板子形状 https://blog.csdn.net/Mark_md/article/details/116445961...

CSS【详解】边框 border,边框-圆角 border-radius,边框-填充 border-image,轮廓 outline
边框 border border 是以下三种边框样式的简写: border-width 边框宽度 —— 数值 px(像素),thin(细),medium(中等),thick(粗)border-style 边框线型 —— none【默认值…...

Error: EBUSY: resource busy or locked, rmdir...npm install执行报错
Error: EBUSY: resource busy or locked, rmdir...npm install执行报错 你一个文件夹目录开了两个cmd命令行(或者powershell),关掉一个就好了。...

Hot100-排序
1.快排 215. 数组中的第K个最大元素 - 力扣(LeetCode) (1)第k大的元素在排序数组中的位置是nums.length - k。 假设我们有一个数组nums [3, 2, 1, 5, 6, 4],并且我们想找到第2大的元素。 步骤 1:排序数…...

树链剖分相关
树链剖分这玩意儿还挺重要的,是解决静态树问题的一个很好的工具~ 这里主要介绍一下做题时经常遇到的两个操作: 1.在线求LCA int LCA(int x,int y){while(top[x]!top[y])if(dep[top[x]]>dep[top[y]]) xfa[top[x]];else yfa[top[y]];return dep[x]&l…...

如何将Grammarly内嵌到word中(超简单!)
1、下载 安装包下载链接见文章结尾 官网的grammarly好像只能作为单独软件使用,无法内嵌到word中🧐🧐🧐 2、双击安装包(安装之前把Office文件都关掉) 3、安装完成,在桌面新建个word文件并打开 注…...

OTG -- 用于FPGA的ULPI接口芯片USB3320讲解(续)
目录 1 背景 2 USB3320在FPGA上的应用 1 背景 最近使用FPGA驱动USB PHY实现高速USB功能,为了方便,购买了一块微雪的USB3300子板,发现怎么都枚举不了,使用逻辑分析仪抓取波形,和STM32F407USB3300波形进行对比…...

了解劳动准备差距:人力资源专业人员的战略
劳动准备差距是一个紧迫的问题,在全球人事部门回应,谈论未开发的潜力和错过的机会。想象一下,人才和需求之间的悬崖之间有一座桥,这促使雇主思考:我们是否为员工提供了足够的设备来应对未来的考验? 这种不…...

SAP PS学习笔记02 - 网络,活动,PS文本,PS文书(凭证),里程碑
上一章讲了PS 的概要,以及创建Project,创建WBS。 SAP PS学习笔记01 - PS概述,创建Project和WBS-CSDN博客 本章继续讲PS的后续内容。包括下面的概念和基本操作,以及一些Customize: - 网络(Network…...

Github 2024-07-07php开源项目日报 Top9
根据Github Trendings的统计,今日(2024-07-07统计)共有9个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量PHP项目9Blade项目2JavaScript项目1Laravel:表达力和优雅的 Web 应用程序框架 创建周期:4631 天开发语言:PHP, BladeStar数量:75969 个Fork数…...

算法训练(leetcode)第二十六天 | 452. 用最少数量的箭引爆气球、435. 无重叠区间、763. 划分字母区间
刷题记录 452. 用最少数量的箭引爆气球思路一思路二 435. 无重叠区间763. 划分字母区间 452. 用最少数量的箭引爆气球 leetcode题目地址 思路一 先按起始坐标从小到大排序。排序后找交集并将交集存入一个数组中,遍历气球数组从交集数组中找交集,找到与…...