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

news2024/9/23 15:24:35

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.创建client
        val client = OkHttpClient.Builder()
            .cookieJar(CookieJar.NO_COOKIES)
            .callTimeout(10000, TimeUnit.MILLISECONDS)
            .build()

        // 2.创建request
        val 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 是否为 null
    if (this.baseUrl == null) {
        throw new IllegalStateException("Base URL required.");
    } else {
        // 2. 检查 callFactory 是否为 null,如果是,则使用默认的 OkHttpClient
        Call.Factory callFactory = this.callFactory;
        if (callFactory == null) {
            callFactory = new OkHttpClient();
        }

        // 3. 检查 callbackExecutor 是否为 null,如果是,则使用默认的 Platform.callbackExecutor
        Executor 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 {
    @Nullable
    static 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]; // 空参数数组,用于没有参数的方法

            @Nullable
            public 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;
        // 挂起函数返回体是否可为 null
        boolean continuationBodyNullable = false;
        // 挂起函数返回类型是否为 Unit
        boolean 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)});
                }
                // 判断返回类型是否为 Unit
                continuationIsUnit = 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.coloradmin.cn/o/1910697.html

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

相关文章

R包:reticulate R对python的接口包

介绍1 R和python是两种不同的编程语言&#xff0c;前者是统计学家发明并且服务数学统计计算&#xff0c;后者则是最万能的胶水语言。随着大数据时代的到来&#xff0c;两者在数据分析领域存在越来越多的共同点且可以相互使用&#xff0c;为了破解二者的编程壁垒&#xff0c;CR…

Noah-MP陆面生态水文模拟与多源遥感数据同化

陆面模型在生态水文研究中的地位和作用&#xff1b;熟悉模型的发展历程&#xff0c;常见模型及各自特点&#xff1b;理解Noah-MP模型的原理&#xff0c;掌握Noah-MP模型在单站和区域的模拟、模拟结果的输出和后续分析及可视化等方法&#xff1b;课程还将深入讲解数据同化的原理…

从天空到地面:无人机航拍推流直播技术在洞庭湖决口封堵中的全方位支援

据新闻报道&#xff0c;受持续强降雨影响&#xff0c;湖南省华容县团洲垸洞庭湖一线堤防发生管涌险情&#xff0c;随后出现决口。截至7月8日20时左右&#xff0c;226米长的洞庭湖一线堤防决口已累计进占208米&#xff0c;目前剩余18米&#xff0c;有望在今晚或9日凌晨实现合龙。…

Spring Cloud Alibaba -- 分布式定时任务解决方案(轻量级、快速构建)(ShedLock 、@SchedulerLock )

文章目录 一、 ShedLock简介二、 SchedulerLock三、基于Mysql方式使用步骤1.建表2.引入依赖3.Mysql连接配置4.ScheduledLock配置5.启动类配置6.创建定时任务7.启动多个项目服务进行测试8.SchedulerLock注解说明 四、使用注意事项 一、 ShedLock简介 ShedLock 是一个用于 Java …

swiftui给视图添加边框或者只给某个边设置border边框

直接使用border()就可以给一个视图添加边框效果&#xff0c;但是这种边框会给所有的边都设置上。 border()里面也可以添加属性.border(.blue, width: 5)这种就是设置颜色和宽度。 设置圆角边框 Text("1024小神").padding().cornerRadius(20).overlay(RoundedRectang…

如何在Facebook上保护你的个人资料安全?

随着社交媒体的普及和个人信息的数字化&#xff0c;保护个人资料安全成为越来越重要的议题。特别是在使用像Facebook这样的平台时&#xff0c;我们需要特别注意如何保护我们的数据免受未经授权的访问和滥用。本文将探讨一些实用的方法&#xff0c;以及如何增强你在Facebook上的…

keil mdk注释插件合集格式、时间、日期注释

文章目录 一、前言二、安装步骤2.1 解压tools.zip2.2 tools 文件解释2.3 添加注释带keil 三、配置3.1 格式化代码3.2 文件注释3.3函数注释3.4 当前日期3.5 当前时间 四、编辑注释模板4.1 编辑函数注释模板4.2 编辑C文件注释模板4.3 编辑h文件注释模板 五、为注释功能添加快捷键…

qmt量化交易策略小白学习笔记第55期【qmt编程之期权数据--获取历史期权列表】

qmt编程之获取期权数据 qmt更加详细的教程方法&#xff0c;会持续慢慢梳理。 也可找寻博主的历史文章&#xff0c;搜索关键词查看解决方案 &#xff01; 感谢关注&#xff0c;咨询免费开通量化回测与获取实盘权限&#xff0c;欢迎和博主联系&#xff01; 获取历史期权列表 …

Sui DeFi现状介绍

关于Sui Network Sui是基于第一原理重新设计和构建而成的L1公有链&#xff0c;旨在为创作者和开发者提供能够承载Web3中下一个十亿用户的开发平台。Sui上的应用基于Move智能合约语言&#xff0c;并具有水平可扩展性&#xff0c;让开发者能够快速且低成本支持广泛的应用开发。获…

视频监控管理平台智能边缘分析一体机视频监控系统客流统计检测算法

在当今数据驱动的时代&#xff0c;客流统计作为商业分析的重要手段&#xff0c;其准确性和实时性对于商家决策具有至关重要的影响。随着技术的发展&#xff0c;智能边缘分析一体机结合了边缘计算与深度学习技术&#xff0c;为客流统计提供了更为高效、精准的解决方案。 首先&am…

可变参数 Collections 不可变集合 Stream流

目录 1.可变参数&#xff1a; 2.Collections: 3.不可变集合&#xff1a; 4.Stream流: 1、什么是流 2、如何生成流 1.单列集合获取Stream流 2.双列集合获取Stream流 3.数组获取Stream流&#xff1a; 4.一堆零散数据&#xff1a; Stream接口中的静态方法 3.Stream流的…

【总线】AXI第九课时:介绍AXI响应信号 (Response Signaling):RRESP和 BRESP

大家好,欢迎来到今天的总线学习时间!如果你对电子设计、特别是FPGA和SoC设计感兴趣&#xff0c;那你绝对不能错过我们今天的主角——AXI4总线。作为ARM公司AMBA总线家族中的佼佼者&#xff0c;AXI4以其高性能和高度可扩展性&#xff0c;成为了现代电子系统中不可或缺的通信桥梁…

如何在 Odoo 16 中配置搜索视图、过滤器和分组

Odoo 中有多种视图类型&#xff0c;包括表单、看板、树、日历、Qweb、搜索等。与表单视图相比&#xff0c;搜索视图用于过滤其他视图的内容&#xff0c;而不是显示内容本身。这使得搜索视图与其他视图不同。表单视图仅包含一条记录&#xff0c;因此不需要搜索。 本文将详细介绍…

PointNet——源码调试(模型训练+可视化测试显示)

因为项目涉及到3D点云项目&#xff0c;故学习下PointNet这个用来处理点云的神经网络 论文的话&#xff0c;大致都看了下&#xff0c;网络结构有了一定的了解&#xff0c;本博文主要为了下载调试PointNet网络源码&#xff0c;训练和测试调通而已&#xff0c;不涉及后续的改进优化…

PHP项目中的前端页面随意点击卡片后会重定向到首页或登录页

个人名片 &#x1f393;作者简介&#xff1a;java领域优质创作者 &#x1f310;个人主页&#xff1a;码农阿豪 &#x1f4de;工作室&#xff1a;新空间代码工作室&#xff08;提供各种软件服务&#xff09; &#x1f48c;个人邮箱&#xff1a;[2435024119qq.com] &#x1f4f1…

判断对象能否回收的两种方法,以及JVM引用

判断对象能否回收的两种方法&#xff1a;引用计数算法&#xff0c;可达性分析算法 引用计数算法&#xff1a;给对象添加一个引用计数器&#xff0c;当该对象被其它对象引用时计数加一&#xff0c;引用失效时计数减一&#xff0c;计数为0时&#xff0c;可以回收。 特点&#xf…

STMCUBEMX_IIC_LL库/HAL库_扫描总线设备

STMCUBEMX_IIC_LL库/HAL库_扫描总线设备 前言&#xff1a; 在很多开发过程中&#xff0c;I2C总线上会挂载多个从机设备&#xff0c;但是又不知道设备的地址是多少&#xff0c;我做一个简单的小工具扫描总线&#xff0c;把地址打印出来就很方便 LL库实例&#xff1a; void scan…

欧姆龙安全PLC及周边产品要点指南

电气安全、自动化设备作业安全&#xff0c;向来是非常非常之重要的&#xff01;越来越多的客户在规划新产线、改造既有产线的过程中&#xff0c;明确要求设计方和施工方将安全考虑进整体方案中进行考虑和报价&#xff01;作为一名自动化电气工程师&#xff0c;尤其是高级工程师…

Python爬虫教程第3篇-解决使用reqeusts遇到的ProxyError异常

起因 问题出现在windows电脑上&#xff0c;我用mac执行程序的时候并不会报错&#xff0c;但是如果在windows上的时候&#xff0c;大部分windows电脑会报错&#xff0c;而有些版本低的windows电脑又不会报错。 异常栈信息 HTTPSConnectionPool, Cannot connect to proxy, no …

智能无人数字直播间 打造24小时的无人直播间源码系统 带网站的安装代码包以及搭建教程

系统概述 智能无人数字直播间系统是一种基于人工智能技术的软件工具&#xff0c;它结合了高精度扫描建模、自动化控制、多模态生成等多项先进技术&#xff0c;能够实现对真实人物的高度仿真&#xff0c;并自主执行各类直播任务。该系统不仅支持24小时不间断直播&#xff0c;还…