Retrofit框架源码深度剖析【Android热门框架分析第二弹】

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进行了封装,但它带来了很多显著的优点。

  1. 超级解耦

    Retrofit实现了接口定义、接口参数和接口回调的彻底解耦。这种设计不仅让代码更清晰、更易于维护,也使得代码的复用性大大提高。
  2. 灵活的HTTP客户端配置

    在Retrofit中,你可以轻松配置不同的HTTP客户端来实现网络请求,如OkHttp、HttpClient等。这种灵活性使得开发者可以根据项目需求选择最合适的HTTP客户端。
  3. 同步与异步支持

    Retrofit同时支持同步和异步请求,提供了更丰富的使用场景。此外,它还与RxJava无缝集成,方便进行响应式编程。
  4. 多种数据格式支持

    通过配置不同的反序列化工具类,Retrofit可以解析多种数据格式,如JSON、XML等。这样,开发者可以根据API的具体需求选择最适合的数据格式。
  5. 高效的请求速度

    由于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.Factoryretrofit2.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.FactoryRetrofit 可以灵活地创建这些 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 参数本身并不包含实际的方法实现,它只是一个接口的类对象,定义了方法的签名(方法名、参数类型、返回类型等)。当你调用这些方法时,实际上会进入 InvocationHandlerinvoke 方法中,通过反射等机制来实现对这些方法的具体处理,从而实现代理模式。

因此,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进行线程的切换 , 进行网络请求.

学生记录所做,如有错误,欢迎指出。 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/43332.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Miniconda的常见用法——以Isaacgym为例

1. ubuntu24.04安装minicondda mkdir -p ~/miniconda3 wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh -O ~/miniconda3/miniconda.sh解释下这段代码 bash ~/miniconda3/miniconda.sh -b -u -p ~/miniconda3~/miniconda3/miniconda.sh: 指向Mi…

SHARPNESS-AWARE MINIMIZATION FOR EFFICIENTLY IMPROVING GENERALIZATION--论文笔记

论文笔记 资料 1.代码地址 https://github.com/google-research/sam https://github.com/davda54/sam 2.论文地址 https://arxiv.org/abs/2010.01412 3.数据集地址 论文摘要的翻译 在当今严重过度参数化的模型中&#xff0c;训练损失的值很难保证模型的泛化能力。事实上…

MySQL第三次练习

作业三 一 先创建DB abc&#xff0c;创建table student 1、插入一条记录 2、添加多条记录 3、添加部分记录 4、加0.5 5、删除成绩为空的记录 二 1、创建一个用户test1使他只能本地登录拥有查询student表的权限。 2、查询用户test1的权限。 3、删除用户test1. 全在一张图上…

怎样优化 PostgreSQL 中对日期时间范围的模糊查询?

文章目录 一、问题分析&#xff08;一&#xff09;索引未有效利用&#xff08;二&#xff09;日期时间格式不统一&#xff08;三&#xff09;复杂的查询条件 二、优化策略&#xff08;一&#xff09;使用合适的索引&#xff08;二&#xff09;规范日期时间格式&#xff08;三&a…

逻辑回归模型(非回归问题,而是分类问题)

目录&#xff1a; 一、Sigmoid函数&#xff1a;二、逻辑回归介绍&#xff1a;三、决策边界四、逻辑回归模型训练过程&#xff1a;1.训练目标&#xff1a;2.梯度下降调整参数&#xff1a; 一、Sigmoid函数&#xff1a; Sigmoid函数是构建逻辑回归模型的重要函数&#xff0c;如下…

免费压缩pdf文件大小软件收费吗?pdf如何压缩文件大小?12款压缩应用推荐!

在数字化时代&#xff0c;PDF文件因其跨平台、格式统一的特点而广受欢迎。然而&#xff0c;随着文件内容的增加&#xff0c;PDF文件的大小也逐渐增大&#xff0c;给存储和传输带来了诸多不便。因此&#xff0c;寻找一款合适的PDF压缩软件成为了许多用户的需求。本文将详细介绍1…

电子设备常用的胶水有哪些?

目录 1、502胶水 2、703胶水 3、704胶水 4、AB胶 5、红胶 6、Underfill 7、导电胶 8、UV胶 9、热熔胶 10、环氧树脂胶 11、硅酮胶 12、聚氨酯胶 13、丙烯酸胶 14、丁基胶 1、502胶水 502胶水&#xff0c;也被称为瞬间胶或快干胶&#xff0c;是一种非常常见的粘合…

电动卡丁车语音芯片方案选型:让驾驶体验更智能、更安全

在追求速度与激情的电动卡丁车领域&#xff0c;每一次升级都意味着更加极致的驾驶体验。而今天&#xff0c;我们要介绍的&#xff0c;正是一款能够显著提升电动卡丁车智能化与安全性的语音芯片方案——为您的爱车增添一份独特的魅力与安全保障。 智能化升级&#xff0c;从“听…

布隆过滤器 redis

一.为什么要用到布隆过滤器&#xff1f; 缓存穿透&#xff1a;查询一条不存在的数据&#xff0c;缓存中没有&#xff0c;则每次请求都打到数据库中&#xff0c;导致数据库瞬时请求压力过大&#xff0c;多见于爬虫恶性攻击因为布隆过滤器是二进制的数组&#xff0c;如果使用了它…

科普文:K8S中常见知识点梳理

简单说一下k8s集群内外网络如何互通的 要在 Kubernetes&#xff08;k8s&#xff09;集群内外建立网络互通&#xff0c;可以采取以下措施&#xff1a; 使用service&#xff1a; 使用Service类型为NodePort或LoadBalancer的Kubernetes服务。这可以使服务具有一个公共IP地址或端口…

Open3D 计算点云的平均密度

目录 一、概述 1.1基于领域密度计算原理 1.2应用 二、代码实现 三、实现效果 2.1点云显示 2.2密度计算结果 一、概述 在点云处理中&#xff0c;点的密度通常表示为某个点周围一定区域内的点的数量。高密度区域表示点云较密集&#xff0c;低密度区域表示点云较稀疏。计算…

Redis连接Resp图形化工具和springboot

Redis连接Resp图形化工具和springboot 1.redis配置1.1 备份、修改conf文件1.2 Redis的其它常见配置&#xff1a;1.3 启动Redis&#xff1a;1.4 停止服务&#xff1a;1.5 开机自启&#xff1a; 2. resp的安装、配置和连接&#xff1a;2.1 GitHub上下载2.2 开始连接redis ![在这里…

C++初探究

概述 C可以追溯到1979年&#xff0c;C之父Bjarne Stroustrup在在使用C语言研发工作时发现C语言的不足&#xff0c;并想要将其改进&#xff0c;到1983年&#xff0c;Bjarne Stroustrup在C语言的基础上添加了面向对象编程的特性&#xff0c;设计出了C的雏形。 网址推荐 C官方文…

王老师 linux c++ 通信架构 笔记(三)安装 xftp、

&#xff08;11&#xff09;调整 xshell 终端的字体大小&#xff0c;默认字体大小是 9 &#xff1a; &#xff08;12&#xff09; 共享文件夹 hgfs 的含义&#xff1a; &#xff08;13&#xff09;安装 xftp &#xff0c; 傻瓜式安装&#xff0c;出了修改下默认安装位置。 操作…

.locked勒索病毒解析与防护指南

引言 随着信息技术的飞速发展&#xff0c;网络安全问题日益严峻&#xff0c;其中勒索病毒成为威胁企业和个人数据安全的重要隐患之一。在众多勒索病毒家族中&#xff0c;.locked勒索病毒以其独特的加密方式和广泛的传播途径&#xff0c;引起了广泛的关注。本文将从多个方面详细…

使用redis-cli查找大key

执行命令 涉及redis-cli 连接和登录&#xff0c;请查看&#xff1a;Redis-cli 连接Redis-CSDN博客 redis-cli -h <redis_instance_address> -p <port> -a <password> --bigkeys<redis_instance_address>&#xff1a; Redis 实例的 IP 地址。 <p…

opencv 鱼眼图像的矫正(动态参数调整)

一&#xff1a;棋盘校准参数说明(内参) 棋盘校准的方法及代码很多&#xff0c;参见其他连接 1&#xff1a;内参矩阵 2&#xff1a;畸变系数 针对鱼眼相机此处是4个参数&#xff0c;在其校准代码中也可以知道&#xff0c;其通常的定义如下&#xff1a; data.camera_mat np.e…

报修小程序论文(设计)开题报告

一、课题的背景和意义 近些年来&#xff0c;随着移动互联网巅峰时期的来临&#xff0c;互联网产业逐渐趋于“小、轻、微”的方向发展&#xff0c;符合轻应用时代特点的各类技术受到了不同领域的广泛关注。在诸多产品中&#xff0c;被誉为“运行着程序的网站”之名的微信小程序…

uniapp-小程序获取用户位置

1. 需要在微信公众平台进行接口的申请。选择自己需要用的接口。 2. 在app.json文件中配置permission和requiredPrivateInfos。requiredPrivateInfos里面是你需要使用的接口。 3. 配置完成后&#xff0c;就可以使用了。 相关获取位置API的链接 4. 如果要获取当前位置到某一个指…