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.rhkb.cn/news/373049.html

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

相关文章

【代码随想录】【算法训练营】【第59天】 [卡码110]字符串接龙 [卡码105]有向图的完全可达性 [卡码106]岛屿的周长

前言 思路及算法思维&#xff0c;指路 代码随想录。 题目来自 卡码网。 day 59&#xff0c;周五&#xff0c;继续ding~ 题目详情 [卡码110] 字符串接龙 题目描述 卡码110 字符串接龙 解题思路 前提&#xff1a; 思路&#xff1a; 重点&#xff1a; 代码实现 C语言 […

LLM大模型从入门到精通(1)--LLM基础知识介绍

1. 大语言模型 (LLM) 背景 2. 语言模型 (Language Model, LM) 1. 大语言模型 (LLM) 背景 大语言模型 (英文&#xff1a;Large Language Model&#xff0c;缩写LLM) 是一种人工智能模型, 旨在理解和生成人类语言. 大语言模型可以处理多种自然语言任务&#xff0c;如文本分类、问…

绝区伍--2024年AI发展路线图

2024 年将是人工智能具有里程碑意义的一年。随着新模式、融资轮次和进步以惊人的速度出现&#xff0c;很难跟上人工智能世界发生的一切。让我们深入了解 2024 年可能定义人工智能的关键事件、产品发布、研究突破和趋势。 2024 年第一季度 2024 年第一季度将推出一些主要车型并…

什么是O2O?线上线下怎么完美结合?

现如今互联网技术飞速发展&#xff0c;智能手机普及。O2O&#xff08;Online To Offline&#xff09;模式已经成为一种新的商业模式&#xff0c;人们的生活和消费习惯也逐渐被改变。经常听到企业提到“O2O”&#xff0c;它究竟是什么呢&#xff1f;对企业有着什么魅力呢&#x…

Flutter Inno Setup 打包 Windows 程序

转载自&#xff1a;flutter桌面应用从开发配置到打包分发 - 掘金 (juejin.cn) 五.打包 1.创建 release 版本的应用 flutter build release 执行完成后&#xff0c; release包位置在项目的build->windows->runer文件夹中 2.应用程序分发 Windows 为 Windows 平台构建…

AE-关键帧

目录 关键帧操作步骤&#xff08;以位置变化为例&#xff09; 1.确定动画起点 2.设置起点的位置属性 3.为起点打上关键帧 4.确定动画终点 5.设置终点的位置属性 改变动画速度 1.选中所有关键帧 2.拖拽 时间反向关键帧 1.选中要反向的关键帧 2.使用时间反向关键帧 …

Apache POI、EasyPoi、EasyExcel

目录 ​编辑 &#xff08;一&#xff09;Apache PoI 使用 &#xff08;二&#xff09;EasyPoi使用 &#xff08;三&#xff09;EasyExcel使用 写 读 最简单的读​ 最简单的读的excel示例​ 最简单的读的对象​ &#xff08;一&#xff09;Apache PoI 使用 &#xff08;二&…

mp4视频太大怎么压缩不影响画质,mp4文件太大怎么变小且清晰度高

在数字化时代&#xff0c;我们常常面临视频文件过大的问题。尤其是mp4格式的视频&#xff0c;文件大小往往令人望而却步。那么&#xff0c;如何在不影响画质的前提下&#xff0c;有效地压缩mp4视频呢&#xff1f;本文将为您揭秘几种简单实用的压缩技巧。 在分享和存储视频时&am…

算法复杂度

目录 1. 数据结构前言 1.1 数据结构 1.2 算法 2. 算法效率 2.1 复杂度的概念 3. 时间复杂度 3.1 大O的渐进表示法 3.2 时间复杂度计算示例: 3.2.1 示例1 3.2.2 示例2 3.2.3 示例3 3.2.4 示例4 3.2.6 示例6 4. 空间复杂度 4.1 空间复杂度计算示例 4.1.1 示例1 …

【Python实战因果推断】18_线性回归的不合理效果8

目录 Saturated Regression Model Regression as Variance Weighted Average Saturated Regression Model 还记得我在本章开头强调回归和条件平均值之间的相似性吗&#xff1f;我向你展示了使用二元干预进行回归与比较干预组和对照组的平均值是完全一样的。现在&#xff0c;由…

Parallels Desktop 19下载及查找我的 Parallels Desktop for Mac 激活密钥

Parallels Desktop 19 for Mac v19.3.0.54924中文破解版是一款适用于Mac的虚拟化软件&#xff0c;parallels desktop 19中文版允许您在Mac计算机上同时运行多个操作系统。它使您能够创建虚拟机并在这些虚拟机中安装不同的操作系统&#xff0c;如Windows、Linux或macOS。使用Par…

electron src build

编译文档&#xff1a; 构建说明 | Electron 1 下载depot_tools &#xff08;1&#xff09;安装depot_tools用于获取 Chromium 及其依赖项的工具集&#xff1a;地址 WINDOWS Download the depot_tools bundle and extract it somewhere. (2)在 Windows 上&#xff0c;您需要…

zdppy+onlyoffice+vue3解决文档加载和文档强制保存时弹出警告的问题

解决过程 第一次排查 最开始排查的是官方文档说的 https://api.onlyoffice.com/editors/troubleshooting#key 解决方案。参考的是官方的 https://github.com/ONLYOFFICE/document-server-integration/releases/latest/download/Python.Example.zip 基于Django的Python代码。 …

葵花奖见证品牌实力 乐橙旗舰智能锁公开首秀引全场热议

7月9日&#xff0c;被誉为智能家居界奥斯卡的2024第八届“葵花奖”于广州建博会广交会展馆A区会议室隆重举行。经过专业评审委员的严格筛选&#xff0c;乐橙荣获“2024智能锁行业消费者喜爱品牌奖”。 作为广州建博会的重要展商之一&#xff0c;乐橙本次携年度高端旗舰新品智能…

CLion学习笔记-cmake编译和多main函数编译

这里就不讲怎么配置clion了 项目名字 pcl_kdtree_search 1.新建一个工程名字自己取&#xff0c;我这里用自己学习pcl的&#xff0c;加一个main函数&#xff0c;这个时候Cmake里边就是这样的。 #声明要求的cmake最低版本 cmake_minimum_required(VERSION 3.19) #声明一个工程…

Python编程学习笔记(3)--- 操作列表

1、遍历列表 遍历列表可以采用for循环的方法&#xff0c;需要对列表中的每一个元素都执行相同的操作。 具体事实如下&#xff1a; name ["ada","cdb","dbc","bad","jinb"] for Name in name:print(Name)运行结果&#x…

企业化运维(7)_Zabbix企业级监控平台

官网&#xff1a;Zabbix :: The Enterprise-Class Open Source Network Monitoring Solution ###1.Zabbix部署### &#xff08;1&#xff09;zabbix安装 安装源 修改安装路径为清华镜像 [rootserver1 zabbix]# cd /etc/yum.repos.d/ [rootserver1 yum.repos.d]# vim zabbix.r…

【Python】基础语法体系:两种常用语句

个人主页&#xff1a;【&#x1f60a;个人主页】 系列专栏&#xff1a;【❤️Python】 文章目录 前言条件语句动态实例if语句if-else 语句if-elif-else 语句嵌套条件语句 循环语句for循环while循环 动态实例嵌套循环 前言 Python语句是构成Python程序的基本单元&#xff0c;用…

Docker:一、安装与卸载、配置阿里云加速器(Ubuntu)

目录 &#x1f341;安装docker&#x1f332;1、环境准备&#x1f332;2、安装docker Engine&#x1f9ca;1、卸载旧版、任何冲突的包&#x1f9ca;2、使用存储库安装&#x1f9ca;3、安装 Docker 包。&#x1f9ca;4、查询是否安装成功&#x1f9ca;5、运行hello-world镜像&…

通用的职位招聘小程序ui模板

蓝色简单的校园招聘&#xff0c;行业招聘&#xff0c;职位招聘手机小程序页面模板。包含&#xff1a;职位列表、职位详情、基本信息填写、登录、个人主页、消息页面等功能 通用的职位招聘小程序ui模板