Retrofit的原理解析

Retrofit是什么

Retrofit 是 Square 公司开发的一款用于在 Android 和 Java 中进行网络请求的库。它提供了一个简单而强大的方式来定义和执行网络请求,支持同步和异步请求,并能够将 HTTP 响应转换为易于使用的对象。

retrofit本身是不具备网络请求功能的,它是对okhttp进行了封装。所以说,网络请求功能本质是由okhttp进行的,retrofit仅负责网络请求接口的封装。

从Retrofit的简单使用中看出它的构建逻辑

1.构建一个Retrofit对象

scss 复制代码
val retrofit = Retrofit.Builder()
                //指定Retrofit在解析数据时所使用的转换库
                .addConverterFactory(GsonConverterFactory.create())
                //用于指定所有Retrofit请求的根路径
                .baseUrl("https://www.wanandroid.com/")
                .build()

2.基于访问接口创建一个接口类型对象(Data是一个Bean)

kotlin 复制代码
interface AppService {
    @GET("article/list/0/json")
    fun getData(): retrofit2.Call<Data>
}
ini 复制代码
 val appService: AppService = retrofit.create(AppService::class.java)

3.基于接口类型对象以及参数构建一个OkHttp Call(appService.getData())

4.将Call添加到OkHttp的请求队列

kotlin 复制代码
appService.getData().enqueue(object : Callback<Data> {
                override fun onResponse(call: Call<Data>, response: Response<Data>) {
                    val data = response.body()//请求返回的数据
                    textView.text = data.toString()
                }
​
                override fun onFailure(call: Call<Data>, t: Throwable) {
                    t.printStackTrace()
                }
            })

构建一个Retrofit对象:Retrofit#Build()

Retrofit的创建是构造者模式

什么时候适合使用构造者模式呢?

1.参数很多,比如大于5个

2.参数是可选的

csharp 复制代码
public Retrofit build() {
      if (baseUrl == null) {
        throw new IllegalStateException("Base URL required.");
      }
​
      okhttp3.Call.Factory callFactory = this.callFactory;
      if (callFactory == null) {
        callFactory = new OkHttpClient();
      }
​
      Executor callbackExecutor = this.callbackExecutor;
      if (callbackExecutor == null) {
        callbackExecutor = platform.defaultCallbackExecutor();
      }
​
      // Make a defensive copy of the adapters and add the default Call adapter.
      List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
      callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor));
​
      // Make a defensive copy of the converters.
      List<Converter.Factory> converterFactories =
          new ArrayList<>(
              1 + this.converterFactories.size() + platform.defaultConverterFactoriesSize());
​
      // Add the built-in converter factory first. This prevents overriding its behavior but also
      // ensures correct behavior when using converters that consume all types.
      converterFactories.add(new BuiltInConverters());
      converterFactories.addAll(this.converterFactories);
      converterFactories.addAll(platform.defaultConverterFactories());
​
      return new Retrofit(
          callFactory,//网络请求工厂
          baseUrl,//网络请求的url地址
          unmodifiableList(converterFactories),
          unmodifiableList(callAdapterFactories),
          callbackExecutor,//回调方法执行器
          validateEagerly);
    }

基于访问接口创建一个接口类型对象:retrofit#create()

kotlin 复制代码
interface AppService {
    @GET("article/list/0/json")
    fun getData(): retrofit2.Call<Data>
}
ini 复制代码
 val appService: AppService = retrofit.create(AppService::class.java)

这句代码很有意思,我们把访问接口作为一个参数传给了retrofit#create()方法,重新生成了一个访问接口的对象,然后再去调用接口里面的方法,居然就可以执行网络申请了,在我们的认知里面,我们的访问接口仅仅是定义了一个getData()用来获取数据的抽象方法,可是经过retrofit#create()方法后,居然可以去获取网络数据了,这就需要我们探究一下retrofit#create()到底干了些什么了

这里其实是使用动态代理,目的是生成一个类。动态代理会生成一个实现类继承于传入的参数也就是我们的访问接口(即AppService接口)

typescript 复制代码
  public <T> T create(final Class<T> service) {
    validateServiceInterface(service);
    //动态代理
    return (T)
        Proxy.newProxyInstance(
            service.getClassLoader(),//类加载器
            new Class<?>[] {service},//要继承的接口
            new InvocationHandler() {
              private final Platform platform = Platform.get();
              private final Object[] emptyArgs = new Object[0];
​
              @Override
              public @Nullable Object invoke(Object proxy, Method method, @Nullable Object[] args)
                  throws Throwable {
                // If the method is a method from Object then defer to normal invocation.
                //说明方法的过滤,会过滤掉默认不需要管理的方法
                if (method.getDeclaringClass() == Object.class) {
                  return method.invoke(this, args);
                }
                args = args != null ? args : emptyArgs;
                //默认情况下,java的接口是不能有默认实现的,但是java8之后可以,使用也要排除
                return platform.isDefaultMethod(method)
                    ? platform.invokeDefaultMethod(method, service, proxy, args)
                    : loadServiceMethod(method).invoke(args);
              }
            });
  }

基于接口类型对象以及参数构建一个OkHttp Call

通过我们前面对代码的分析,retrofit最后都是要把我们的网络请求进行到代理类里面的InvocationHandler.invoke()里来。因为我们前面也说过retrofit只是负责网络接口的封装,最后的实际网络请求是通过OkHttp去进行的,所以一直到这步的目的都是为了去生成OkHttp Call。

大致的流程就是:接口+注解+参数-》invoke-》OkHttp Call

然后把这个生成的call入队列就完成了网络请求。

所以接下来我们应该来看看invoke是怎么变成OkHttp Call的

看到retrofit#create()方法里面的最后一行loadServiceMethod(method).invoke(args),看一下loadServiceMethod(method)的源码,可以看到它返回了一个很重要的对象ServiceMethod,在 Retrofit 中,ServiceMethod 是一个关键的类,它的主要作用是用于将网络请求方法转换为 HTTP 请求。ServiceMethod 的实例包含了与特定方法调用相关的所有信息,包括请求的参数、注解、HTTP 方法、请求头等等。它负责将这些信息组装成一个可以发送到服务器的 HTTP 请求。

sql 复制代码
ServiceMethod<?> loadServiceMethod(Method method) {
    //ServiceMethod不会直接创建,而是从serviceMethodCache缓存池中获取
    ServiceMethod<?> result = serviceMethodCache.get(method);
    //如果获取的result不为空,就直接返回
    if (result != null) return result;
    //若result为空,则创建一个ServiceMethod(构建者模式),最后把创建好后result保存到serviceMethodCache中
    synchronized (serviceMethodCache) {
      result = serviceMethodCache.get(method);
      if (result == null) {
        result = ServiceMethod.parseAnnotations(this, method);
        //把创建好后result保存到serviceMethodCache中
        serviceMethodCache.put(method, result);
      }
    }
    return result;
  }

ServiceMethod创建后,调用它的invoke()生成Call

less 复制代码
@Override
  final @Nullable ReturnT invoke(Object[] args) {
    Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
    return adapt(call, args);
  }

到这里我们就成功的生成了一个OkHttp Call,然后让它入队列就行了。

将Call添加到OkHttp的请求队列

java 复制代码
@Override
  public void enqueue(final Callback<T> callback) {
  //首先,检查回调对象是否为空,如果为空则抛出异常。回调对象用于处理请求的结果。
    Objects.requireNonNull(callback, "callback == null");
​
    okhttp3.Call call;
    Throwable failure;
​
    synchronized (this) {
    //如果已经执行过一次了,就抛出异常。Retrofit 的请求只能被执行一次。
      if (executed) throw new IllegalStateException("Already executed.");
      executed = true;
​
      call = rawCall;
      failure = creationFailure;
      //如果 call 和 failure 都为空,说明是第一次执行请求。
      if (call == null && failure == null) {
        try {
        //createRawCall() 方法由具体的 ServiceMethod 实现,用于创建 OkHttp 的 Call 对象。createRawCall()方法的源码在下面可以
          call = rawCall = createRawCall();
        } catch (Throwable t) {
          throwIfFatal(t);
          failure = creationFailure = t;
        }
      }
    }
​
    if (failure != null) {
      callback.onFailure(this, failure);
      return;
    }
    //如果请求被取消,调用 OkHttp 的 cancel() 方法取消请求。
    if (canceled) {
      call.cancel();
    }
    //将请求加入到 OkHttp 的调度队列中,开始执行异步请求。
    call.enqueue(......);
  }

createRawCall()方法

vbnet 复制代码
private okhttp3.Call createRawCall() throws IOException {
    okhttp3.Call call = callFactory.newCall(requestFactory.create(args));
    if (call == null) {
      throw new NullPointerException("Call.Factory returned null.");
    }
    return call;
  }

一句话总结就是把OkHttp封装成okhttp3的call,然后后面的工作就是OkHttp去完成了。

流程总结

Retrofit流程的总结:首先是建立一个通过Retrofit的Build()方法新建一个Retrofit对象,这里使用的是构造者模式,Build()方法会将后面构建Http请求的资源在这里配置好,比如url地址,网络请求工厂等等。然后会根据网络访问接口生成一个实现了接口的动态代理类。这个动态代理类会生成一个ServiceMethod对象,调用这个对象的invoke()方法是把网络请求信息封装成一个完整的Http请求,还记得Retrofit的Build()里面配置了一些资源吗,比如说baseUrl等等。ServiceMethod对象正是使用了这些资源,把baseUrl,注解以及参数等等去配置http请求,然后封装成以一个OkHttpCall,进而再封装成一个okhttp3的call。

[Q:Retrofit使用建造者模式的好处?]

使用建造者模式可以轻松地构建一个复杂的对象,而无需直接调用构造函数,并且可以在构建过程中设置不同的参数。这使得在构建对象时可以根据需求进行灵活的配置,而不需要创建多个构造函数。

在 Retrofit 中使用建造者模式,你可以通过 Retrofit.Builder 类来配置和创建 Retrofit 对象。这使得在构建 Retrofit 对象时可以方便地设置基本的配置信息,如 base URL、转换器等,同时也可以通过链式调用添加自定义的拦截器、调试信息等

[Q:为什么Retrofit要使用动态代理?]

因为一个应用的网络请求接口可能有很多个,通过动态代理模式,能动态为每个接口都生成一个具体的代理类,并且实现了我们的接口,我们只需要声明网络请求接口,然后传递给Retrofit即可,不需要关心具体请求细节,通过Retrofit动态生成的代理类对象发起请求,然后得到结果,使用起来非常方便。

相关推荐
simplepeng44 分钟前
我的天,我真是和androidx的字体加载杠上了
android
小猫猫猫◍˃ᵕ˂◍2 小时前
备忘录模式:快速恢复原始数据
android·java·备忘录模式
CYRUS_STUDIO4 小时前
使用 AndroidNativeEmu 调用 JNI 函数
android·逆向·汇编语言
梦否4 小时前
【Android】类加载器&热修复-随记
android
徒步青云4 小时前
Java内存模型
android
今阳5 小时前
鸿蒙开发笔记-6-装饰器之@Require装饰器,@Reusable装饰器
android·app·harmonyos
-优势在我9 小时前
Android TabLayout 实现随意控制item之间的间距
android·java·ui
hedalei9 小时前
android13修改系统Launcher不跟随重力感应旋转
android·launcher
Indoraptor11 小时前
Android Fence 同步框架
android
峥嵘life11 小时前
DeepSeek本地搭建 和 Android
android