当前位置: 首页 > news >正文

Retrofit 网络框架源码解析(二)

目录

  • 一、Okhttp请求
  • 二、Retrofit 请求
    • retrofit是如何封装请求的
  • 三、Retrofit的构建过程
  • 四、Retrofit构建IxxxService对象的过程(Retrofit.create())
    • 4.1 动态代理
    • 4.2 ServiceMethod
    • 4.3 okHttpCall
    • 4.4 callAdapter
  • 五、Retrofit网络请求操作

一、Okhttp请求

示例代码:

private void testOkHttp() {// step1OkHttpClient client = new OkHttpClient();// step2Request request = new Request.Builder().url("https://www.google.com.hk").build();// step3Call call = client.newCall(request);// step4 发送网络请求,获取数据,进行后续处理call.enqueue(new okhttp3.Callback() {@Overridepublic void onFailure(okhttp3.Call call, IOException e) {}@Overridepublic void onResponse(okhttp3.Call call, okhttp3.Response response) throws IOException {}});}

上面示例代码解析:
step1:创建 HttpClient 对象,也就是构建一个网络类型的实例,一般会将所有的网络请求使用同一个单例对象。
step2:构建 Request,也就是构建一个具体的网络请求对象,具体的请求 url,请求头,请求体等等。
step3:构建请求 Call,也就是将具体的网络请求与执行请求的实体进行绑定,形成一个具体的正式的可执行体。
step4:后面就是进行网络请求了,然后处理网络请求的数据。

总结一下:
OkHttp 的意义:OkHttp 是基于 Http 协议封装的一套请求客户端,虽然它可以开线程,但根本上它更便向真正的请求,跟 HttpClient,HttpUrlConnection 的职责是一样的。
OkHttp 的职责:OkHttp 主要负责 socket 部分的优化,比如多路复用,buffer 缓存,数据压缩等等。
OkHttp 给用户留下的问题:
1)用户网络请求的接口配置繁琐,尤其是需要配置请求 body,请求头,参数的时候;
2)数据解析过程需要用户手动拿到 responsebody 进行解析,不能复用;
3)无法适配自动进行线程的切换;
4)万一存在网络嵌套网络请求就会陷入“回调陷阱”

二、Retrofit 请求

示例代码使用:
导入相关包:

// Retrofit的依赖
implementation 'com.squareup.retrofit2:retrofit:2.1.0'
implementation 'com.squareup.retrofit2:converter-gson:2.1.0'

代码示例

// step1  建立一个Retrofit对象,配置好Retrofit类的成员变量
Retrofit retrofit = new Retrofit.Builder().baseUrl("http://www.wanandroid.com")  //网络请求的url地址.addConverterFactory(GsonConverterFactory.create(new Gson())).addCallAdapterFactory(RxJava2CallAdapterFactory.create()).build();
// step2 创建一个ISharedListService接口类的对象,create函数内部使用了动态代理来创建接口
//对象,这样的设计可以让所有的访问请求都被代理
ISharedListService sharedListService = retrofit.create(ISharedListService.class);// step3 调用getSharedList的时候,在动态代理里面,会存在一个函数 getSharedLIst,
// 这个函数里面会调用 invoke,这个 invoke 函数也就是retrofit 里面的 invoke 函数
// 目的:将接口转换成网络请求
Call<SharedListBean> sharedListCall = sharedListService.getSharedList(2, 1);  //这里返回的是ExecutorCallbackCall对象// step4
sharedListCall.enqueue(new Callback<SharedListBean>(){@Overridepublic void onResponse(Call<SharedListBean> call, Response<SharedListBean> response) {if (response.isSuccessful()) {System.out.println(response.body().toString());}}@Overridepublic void onFailure(Call<SharedListBean> call, Throwable t) {t.printStackTrace();}
})

上面示例代码解析:
step1: 创建 retrofit 对象,构建一个网络请求的载体对象,和 OkHttp 构建 OKHttpClient 对象有一样的意义,只不过 retrofit 在 build 的时候有非常多的初始化内容,这些内容可以为后面网络请求提供准备,如准备 线程转换Executor, Gson convert,RxjavaCallAdapter。
step2:Retrofit 的精髓,为统一配置网络请求完成动态代理的设置。
step3:构建具体网络请求对象 Request(service), 在这个阶段要完成的任务:
1)将接口中的注解翻译成对应的参数;
2)确定网络请求参数的返回值 response 类型以及对应的转换器;
3)将 OkHttp 的 Request 封装成为 Retrofit 的 OkhttpCall。
总的来说,就是根据请求 service 的 interface 来封装 Okhttp 请求 Request。
step4: 后面就进行网络请求,然后处理网络请求的数据。

其中:
baseUrl:网络请求的url地址
callFactory:网络请求工厂
callbackExecutor:回调方法执行器
adapterFactories:网络请求适配器工厂的集合
coverterFatories:数据转换器工厂的集合

总结一下:
Retrofit 主要负责应用层面的封装,就是说主要面向开发者,方便使用,比如请求参数,响应数据的处理,错误处理等等。
Retrofit 封装了具体的请求,线程切换以及数据转换。
网上一般都推荐 RxJava + Retrofit + OkHttp 框架,Retrofit 负责请求的数据和请求的结果,使用接口的方式呈现,OkHttp 负责请求的过程,Rxjava 负责异步,各种线程之间的切换,用起来非常便利。

ISharedListService.java

public interface ISharedListService {@GET("user/{userid}/share_articles/{pageid}/json")Call<SharedListBean> getSharedList(@Path("userid") int it, @Path("pageid") int pageid);}

retrofit是如何封装请求的

在这里插入图片描述
  大体的网络流程是一致的,毕竟都是通过okhttp进行网络请求。主要的步骤都是:创建网络请求实体client -> 构建真正的网络请求 -> 将网络请求方案与真正的网络请求实体结合构成一个请求Call -> 执行网络请求 -> 处理返回数据 -> 处理 Android 平台的线程问题。
  在上图中,我们看到的对比最大的区别是:
1)okhttp创建的是OkHttpClient,然而retrofit创建的是Retrofit实例
2)构建Request的流程,retrofit是通过注解来进行适配
3)配置Call的过程中,retrofit是利用Adapter适配的OkHttp的Call
4)相对okhttp,retrofit会对responseBody进行自动的Gson解析
5)相对okhttp,retrofit会自动的完成线程的切换

三、Retrofit的构建过程

  Retrofit 通过build模式来生成一个Retrofit对象,通过代码我们知道,Retrofit默认会使用Okhttp来发送网络请求,当然,我们也可以自己定制。
  执行step1中的build()操作,进入下面的build流程

Retrofit.java

  public Retrofit build() {if (baseUrl == null) {throw new IllegalStateException("Base URL required.");}// 代码1// 默认只支持 okhttp 请求,不支持 httpurlconnection 和 httpclientokhttp3.Call.Factory callFactory = this.callFactory;if (callFactory == null) {callFactory = new OkHttpClient();}// 代码2// 添加一个线程管理 Executor, okhttp 切换线程需要手动处理,但是 retrofit//不需要,就是因为这个 Executor 的存在,其实它是handlerExecutor callbackExecutor = this.callbackExecutor;if (callbackExecutor == null) {callbackExecutor = platform.defaultCallbackExecutor();}// 代码3// Make a defensive copy of the adapters and add the default Call adapter.List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories);adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor)); // 添加一个默认的callAdapterFatory// Make a defensive copy of the converters.List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories);return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,callbackExecutor, validateEagerly);}
其中,build流程采用构建/建造者设计模式和外观/门面设计模式。

1: 在代码1处
  初始化构建call的工厂,但是这个地方直接就是使用了okhttp的call,没有使用到工厂设计模式去添加构建httpclient 或者 httpurlconnection的方法来创建call,说明retrofit已经铁下心只支持okhttp创建call请求。
  此次调用,目的就是创建一个OkHttpClient,换句话说,这里的调用就是生产Okhttp网络请求需要的请求Call,以备后面进行真正的网络请求。

2:在代码2处
  网络请求需要在子线程中执行,那么就需要线程管理,所有就有了代码2的存在,深入源码后发现,这个地方就是运用handler进行线程切换,当网络请求回来了进行线程切换,可以看下面的源码:
Platform.java

static class Android extends Platform {@Override public Executor defaultCallbackExecutor() {return new MainThreadExecutor();}@Override CallAdapter.Factory defaultCallAdapterFactory(@Nullable Executor callbackExecutor) {if (callbackExecutor == null) throw new AssertionError();return new ExecutorCallAdapterFactory(callbackExecutor);}static class MainThreadExecutor implements Executor {private final Handler handler = new Handler(Looper.getMainLooper());@Override public void execute(Runnable r) {handler.post(r);}}}

  所以,此次调用,目的是构建一个用handler封装的Executor,以备后面进行网络请求成功后的线程切换用。

3:在代码3处
设置默认CallAdapterFactory
  在此添加的CallAdapterFactory属于系统默认的,当然,我们可以添加RxJavaCallAdapterFactory。默认的CallAdapterFactory是ExecutorCallAdapter类的对象,在Platform.java Class里面可以梳理出来
Platform.java

  CallAdapter.Factory defaultCallAdapterFactory(@Nullable Executor callbackExecutor) {if (callbackExecutor != null) {return new ExecutorCallAdapterFactory(callbackExecutor);}return DefaultCallAdapterFactory.INSTANCE;}

  所以构建的Retrofit都是用于进行后面请求的需要的内容的一个准备工作。也就是封装OkHttp需要的准备工作。

最后new 了一个Retrofit对象
Retrofit.java

public final class Retrofit {// 网络请求配置对象(对网络请求接口中方法注解进行解析后得到的对象)// 作用:存储网络请求相关的配置,如网络请求的方法、数据转换器、网络请求适配器private final Map<Method, ServiceMethod<?, ?>> serviceMethodCache = new ConcurrentHashMap<>();// 网络请求器的工厂// 作用:生产网络请求器(Call)// Retrofit 是默认使用okhttpfinal okhttp3.Call.Factory callFactory;// 网络请求的url地址final HttpUrl baseUrl;// 数据转换器工厂的集合// 作用:放置数据转换器工厂// 数据转换器工厂作用:生产数据转换器(converter)final List<Converter.Factory> converterFactories;// 网络请求适配器工厂的集合// 作用:放置网络请求适配器工厂// 网络请求适配器工厂作用:生产网络请求适配器(CallAdapter)final List<CallAdapter.Factory> adapterFactories;// 回调方法执行器final @Nullable Executor callbackExecutor;// 标志位// 作用:是否提前对业务接口中的注解进行验证转换的标志位final boolean validateEagerly;Retrofit(okhttp3.Call.Factory callFactory, HttpUrl baseUrl,List<Converter.Factory> converterFactories, List<CallAdapter.Factory> adapterFactories,@Nullable Executor callbackExecutor, boolean validateEagerly) {this.callFactory = callFactory;this.baseUrl = baseUrl;this.converterFactories = unmodifiableList(converterFactories); // Defensive copy at call site.this.adapterFactories = unmodifiableList(adapterFactories); // Defensive copy at call site.this.callbackExecutor = callbackExecutor;this.validateEagerly = validateEagerly;}... 
}

四、Retrofit构建IxxxService对象的过程(Retrofit.create())

看下面的代码:

ISharedListService sharedListService = retrofit.create(ISharedListService.class);
Call<SharedListBean> sharedListCall = sharedListService.getSharedList(2, 1);

  上面两行代码需要连起来才能正确的被阅读,因为,在create里面是使用了动态代理的技术方案,而动态代理是运行时生效的
  执行step2,调用Retrofit的create()方法,create函数内部使用了动态代理来创建接口对象,这样的设计可以让所有的访问请求都被代理

create()方法里面采用动态代理设计模式

Retrofit.java

public <T> T create(final Class<T> service) {Utils.validateServiceInterface(service);if (validateEagerly) {eagerlyValidateMethods(service);}// 通过动态代理方式生成具体的网络请求实体对象return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },new InvocationHandler() {  // 统一处理所有的请求方法private final Platform platform = Platform.get();@Override public 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);}// 默认情况下,java的接口是不能够有默认实现的,但是Java8 可以默认实现if (platform.isDefaultMethod(method)) {return platform.invokeDefaultMethod(method, service, proxy, args);}// 根据方法生成一个ServiceMethod对象(内部会将生成的ServiceMethod放入缓存中)// 如果已经生成过则直接从缓存中获取ServiceMethod<Object, Object> serviceMethod =(ServiceMethod<Object, Object>) loadServiceMethod(method);// 根据ServiceMethod对象和请求参数生成一个OkHttpCall对象,// 这个OkHttpCall能够调用OkHttp的接口发起网络请求OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);// 调用serviceMethod 的 callAdapter 的 adapter方法,并传入 okhttpCall,返回一个对象,// 这个的目的主要是为了适配返回类型,其内部会对OkHttpCall对象进行包装return serviceMethod.callAdapter.adapt(okHttpCall);}});}

  1)Retrofit的create方法通过动态代理的模式,生成了实现具体的网络请求接口的对象,并在InvocationHandler的invoke方法中统一处理网络请求接口实体对象的方法;2)invoke方法会通过方法构造一个ServiceMethod对象,并将其放入缓存中;3)然后根据ServiceMethod对象和网络请求的参数args去构造一个OkhttpCall对象;4)最后调用serviceMethod的callAdapter的adapter方法,传入将OkHttpCall对象,callAdapter的目的主要是为了适配OkHttpCall对象,其内部会对OkHttpCall对象进行包装,生成对应返回类型的对象。

4.1 动态代理

  动态代理的原理主要是在运行时动态生成代理类,然后根据代理类生成一个代理对象,在这个代理对象的方法中又会调用InvocationHandler的invoke来转发对方法的处理。
  在使用retrofit的时候,对每一个网络请求的产生都必须要先调用create函数,也就意味着,我们的请求都是通过代理类来进行处理的。但是代理类具体的代理行为是发生在哪里呢?很明显,它并不是在create函数执行的时候,而是在使用具体的接口创建具体网络请求Call的时候,当调用具体网络请求Call的代码示例如下:

Call<SharedListBean> sharedListCall = sharedListService.getSharedList(2, 1);

  在执行上面 代码的时候,它会走代码设计模式的InvocationHandler里面的invoke()函数,也就是所有的网络请求在创建具体网络请求Call的时候,都会走invoke, 从而我们可以在invoke里面进行各种行为的统一处理,比如:接口的统一处理,也就是注解的解读和网络请求参数的拼接。

  在代码执行create()方法时,执行到Proxy.newProxInstance()时,在运行时会动态创建一个代理类对象类如ISharedListServiceProxy,并且该类实现了ISharedListService接口

代理类:

class ISharedListServiceProxy implement ISharedListService{// toString();// hashCode();  等等一系列方法... // 接口方法public void getSharedList(int it, int pageid) {invocationHandler.invoke(it, pageid);}}

4.2 ServiceMethod

  执行动态代理的invoke()方法,会执行 loadServiceMethod()方法
其实就将注解、参数都转化成网络请求Call的参数,一个请求对于一个ServiceMethod

Retrofit.java

ServiceMethod<?, ?> loadServiceMethod(Method method) {// 为什么会缓存--》为了效率ServiceMethod<?, ?> result = serviceMethodCache.get(method);if (result != null) return result;synchronized (serviceMethodCache) {result = serviceMethodCache.get(method);if (result == null) {result = new ServiceMethod.Builder<>(this, method).build(); // 采用一个建造者设计模式serviceMethodCache.put(method, result);}}return result;}

  loadServiceMethod首先会从缓存中获取ServiceMethod对象,如果没有,则通过Method和Retrofit对象构造一个ServiceMethod对象,并将其放入缓存中。
  每一个method都有一个自己的ServiceMethod,这就意味着ServiceMethod是属于函数的,而不是类的。也就是我们定义的网络访问接口类,在接口类里面的每一个函数都会在反射阶段形成自己的serviceMethod。
  ServiceMethod其实是用来存储一次网络请求的基本信息的,比如Host、URL、请求方法等,同时ServiceMethod还会存储用来适配OkHttpCall对象的CallAdapter。ServiceMethod的build方法会解读传入的Method,首先ServiceMethod会在CallAdapterFactory列表中寻找合适的CallAdapter来包装OkHttpCall对象的,这一步主要是根据Method的返回参数来匹配的,比如如果方法的返回参数是Call对象,那么ServiceMethod就会使用默认的CallAdapterFactory来生成CallAdapter,而如果返回对象是RxJava的Observable对象,则会使用RxJavaCallAdapterFactory提供的CallAdapter。如果build方法会解读Method的注解,来获得注解上配置的网络请求信息,比如请求方法、URL、Header等。

ServiceMethod.java

final class ServiceMethod<R, T> {// Upper and lower characters, digits, underscores, and hyphens, starting with a character.static final String PARAM = "[a-zA-Z][a-zA-Z0-9_-]*";static final Pattern PARAM_URL_REGEX = Pattern.compile("\\{(" + PARAM + ")\\}");static final Pattern PARAM_NAME_REGEX = Pattern.compile(PARAM);// 网络请求工厂    也就是okhttp的网络请求 final okhttp3.Call.Factory callFactory;// 网络请求适配器   把call请求适配各个平台final CallAdapter<R, T> callAdapter;// 网络基地址private final HttpUrl baseUrl;// 数据转换器private final Converter<ResponseBody, R> responseConverter;// 网络请求的http方法    比如 get、post方法private final String httpMethod;// 网络请求的相对地址   和 baseUrl 拼接起来就是实际地址private final String relativeUrl;// 请求头private final Headers headers;// 网络请求的 http 报文的 typeprivate final MediaType contentType;// 三个标志位private final boolean hasBody;private final boolean isFormEncoded;private final boolean isMultipart;// 方法参数的处理器   比如我们定义的方法   上面的 get 和后面的参数private final ParameterHandler<?>[] parameterHandlers;// 构建一系列的变量ServiceMethod(Builder<R, T> builder) {this.callFactory = builder.retrofit.callFactory();this.callAdapter = builder.callAdapter;this.baseUrl = builder.retrofit.baseUrl();this.responseConverter = builder.responseConverter;this.httpMethod = builder.httpMethod;this.relativeUrl = builder.relativeUrl;this.headers = builder.headers;this.contentType = builder.contentType;this.hasBody = builder.hasBody;this.isFormEncoded = builder.isFormEncoded;this.isMultipart = builder.isMultipart;this.parameterHandlers = builder.parameterHandlers;}static final class Builder<T, R> {final Retrofit retrofit;final Method method;final Annotation[] methodAnnotations;final Annotation[][] parameterAnnotationsArray;final Type[] parameterTypes;Type responseType;boolean gotField;boolean gotPart;boolean gotBody;boolean gotPath;boolean gotQuery;boolean gotUrl;String httpMethod;boolean hasBody;boolean isFormEncoded;boolean isMultipart;String relativeUrl;Headers headers;MediaType contentType;Set<String> relativeUrlParamNames;ParameterHandler<?>[] parameterHandlers;Converter<ResponseBody, T> responseConverter;CallAdapter<T, R> callAdapter;Builder(Retrofit retrofit, Method method) {this.retrofit = retrofit;this.method = method;this.methodAnnotations = method.getAnnotations();this.parameterTypes = method.getGenericParameterTypes();this.parameterAnnotationsArray = method.getParameterAnnotations();}public ServiceMethod build() {callAdapter = createCallAdapter();  //查找能适配返回类型的CallAdapterresponseType = callAdapter.responseType();if (responseType == Response.class || responseType == okhttp3.Response.class) {throw methodError("'"+ Utils.getRawType(responseType).getName()+ "' is not a valid response body type. Did you mean ResponseBody?");}// 设置请求的数据适配器converterresponseConverter = createResponseConverter();// 解读方法的注解,methodAnnotations表示方法上的所有注解 for (Annotation annotation : methodAnnotations) {parseMethodAnnotation(annotation);}if (httpMethod == null) {throw methodError("HTTP method annotation is required (e.g., @GET, @POST, etc.).");}if (!hasBody) {if (isMultipart) {throw methodError("Multipart can only be specified on HTTP methods with request body (e.g., @POST).");}if (isFormEncoded) {throw methodError("FormUrlEncoded can only be specified on HTTP methods with "+ "request body (e.g., @POST).");}}int parameterCount = parameterAnnotationsArray.length;parameterHandlers = new ParameterHandler<?>[parameterCount];for (int p = 0; p < parameterCount; p++) {Type parameterType = parameterTypes[p];if (Utils.hasUnresolvableType(parameterType)) {throw parameterError(p, "Parameter type must not include a type variable or wildcard: %s",parameterType);}Annotation[] parameterAnnotations = parameterAnnotationsArray[p];if (parameterAnnotations == null) {throw parameterError(p, "No Retrofit annotation found.");}parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);}if (relativeUrl == null && !gotUrl) {throw methodError("Missing either @%s URL or @Url parameter.", httpMethod);}if (!isFormEncoded && !isMultipart && !hasBody && gotBody) {throw methodError("Non-body HTTP method cannot contain @Body.");}if (isFormEncoded && !gotField) {throw methodError("Form-encoded method must contain at least one @Field.");}if (isMultipart && !gotPart) {throw methodError("Multipart method must contain at least one @Part.");}return new ServiceMethod<>(this);  // new了一个ServiceMethod}

  ServiceMethod 作用就是解析注解、解析请求的返回值这一系列过程
  retrofit是对整个网络请求的一个保存,ServiceMethod是对某一个接口请求(具体的网络请求)的保存,就是从retrofit中所保存的一系列参数列表中找到对应的参数
在这里插入图片描述

4.3 okHttpCall

OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);

  我们知道,ServiceMethod封装了网络请求的基本信息,比如Host、URL等,我们根据ServiceMethod和请求参数args就可以确定本次网络请求的所有信息了,OkHttpCall主要是将这些信息封装起来,并调用OkHttp的接口去发送网络请求,这里,我们就将OkHttpCall看成是一个处理网络请求的类即可。

4.4 callAdapter

在retrofit中,invoke()里面的最后一行代码,

return serviceMethod.callAdapter.adapt(okHttpCall);

  那么我们可以设想一下为什么Retrofit还要设计一个CallAdapter接口呢?
先来说一个客观事实,Retrofit真正使用okhttp进行网络请求的就是OkHttpCall这个类

  曾提到了Call对象的创建是通过ServiceMethod.adapter()完成的,这里在看看该方法的源码:
ServiceMethod.adapter()方法:

T adapte(Call<R> call) {return callAdapter.adapte(call);
}

  通过上述源码可知,最终Call对象是调用CallAdapter.adapter(Call)方法创建的,那么CallAdapter及具体的Call对象又是如何生成的呢?
如果没有这个适配器模式,会出现上面情况?
  很明显,没有适配器的时候此时我们网络请求的返回接口只能直接返回OkHttpCall,那么所有的网络请求都是用okhttpCall进行,这样的话就失去了retrofit封装网络请求call的意义了,譬如:RxjavaCallAdapterFactory就没有办法支持。
如果我们想要返回的不是Call呢?比如Rxjava的Observable,这种情况下该怎么办呢?
适配器模式在此发挥了其应用的作用
  将网络请求的核心类OkHttpCall进行适配,你需要什么类型的数据就通过适配器适配,返回适配后的对象就是了,正是这种CallAdapter接口的设计,使得我们在使用Retrofit的时候可以自定义我们想要的返回类型。此接口的设计也为Rxjava的扩展使用做了很好的基础。

五、Retrofit网络请求操作

  一般的Retrofit网络请求的操作是指Call.execute() & Call.enqueue()的过程,这个过程才是真正的网络请求,因为,网络配置,请求地址配置,Call适配,网络请求requestBody & 返回值responseBody转化适配准备工作都已经完成。

sharedListCall.enqueue(new Callback()...);
sharedListCall.execute();

  在进行网络请求执行的时候,基本上就是调用,ServiceMethod中设置的各个内容如:
1)OkHttpCall进行网络请求,实则是进行okhttp的网络请求;
2)利用converter进行网络请求数据的转换,一般是Gson();
3)利用rxjava observable构建rxjava类型的责任链访问方案,并进行线程切换;
4)如果没有rxjava的添加,那么就使用默认的callAdapter里面的callbackExecutor进行线程的切换,进行网络请求。

整体网络请求的流程图:
在这里插入图片描述

相关文章:

Retrofit 网络框架源码解析(二)

目录一、Okhttp请求二、Retrofit 请求retrofit是如何封装请求的三、Retrofit的构建过程四、Retrofit构建IxxxService对象的过程&#xff08;Retrofit.create()&#xff09;4.1 动态代理4.2 ServiceMethod4.3 okHttpCall4.4 callAdapter五、Retrofit网络请求操作一、Okhttp请求 …...

SQL Server 2008新特性——更改跟踪

在大型的数据库应用中&#xff0c;经常会遇到部分数据的脱机和多个数据库的合并问题。比如现在有一个全省范围使用的应用程序&#xff0c;每个市都部署了单独的相同的应用程序服务器和数据库服务器&#xff0c;每个月需要将全省所有市的数据全部汇总起来用于出全省的报表&#…...

四六级真题长难句分析与应用

一、基本结构的长难句 基本结构的长难句主要考点&#xff1a;断开和简化 什么是长难句&#xff1f; 其实就是多件事连在了一块&#xff0c;这时候句子就变长、变难了 分析步骤&#xff1a; 第一件事就是要把长难句给断开&#xff0c;把多件事断开成一件一件的事情&#xff0…...

华为OD机试 - 玩牌高手(Python) | 机试题算法+思路 【2023】

最近更新的博客 华为OD机试 - 寻找路径 | 备考思路,刷题要点,答疑 【新解法】 华为OD机试 - 五键键盘 | 备考思路,刷题要点,答疑 【新解法】 华为OD机试 - IPv4 地址转换成整数 | 备考思路,刷题要点,答疑 【新解法】 华为OD机试 - 对称美学 | 备考思路,刷题要点,答疑 …...

【论文阅读】 Few-shot object detection via Feature Reweighting

Few-shot object detection的开山之作之一 ~~ 特征学习器使用来自具有足够样本的基本类的训练数据来 提取 可推广以检测新对象类的meta features。The reweighting module将新类别中的一些support examples转换为全局向量&#xff0c;该全局向量indicates meta features对于检…...

现代卷积神经网络经典架构图

卷积神经网络&#xff08;LeNet&#xff09; LeNet 的简化版深层卷积神经网络&#xff08;AlexNet&#xff09; 从LeNet&#xff08;左&#xff09;到AlexNet&#xff08;右&#xff09;改进&#xff1a; dropOut层 - 不改变期望但是改变方差ReLU层 - 减缓梯度消失MaxPooling数…...

有关eclipse的使用tips

一、alt/键 会产生单词提示&#xff0c;可以提高编程速度。例如不需要辛辛苦苦的打出&#xff1a;System.out.println();整句&#xff0c;只需要在eclipse中输入syso&#xff0c;然后按住ALT/就会出来System.out.println();在alt键/不管用的情况下&#xff0c;可使用以下方法来…...

Mybatis(4)之CRUD

首先是 增 &#xff0c;我们要在数据库中增加一个数据 先来看看我们之前的插入语句 <insert id"insertRole">insert into try(id,name,age) values(3,nuonuo,20)</insert> 请注意&#xff0c;我们这里的 insert 是固定的&#xff0c;但在实际的业务场…...

OSG三维渲染引擎编程学习之五十七:“第五章:OSG场景渲染” 之 “5.15 光照”

目录 第五章 OSG场景渲染 5.15 光照 5.15.1 osg::Light光 5.15.2 osg::LightSource光源 第五章 OSG场景渲染 OSG存在场景树和渲染树,“场景数”的构建在第三章“OSG场景组织”已详细阐明,本章开始深入探讨“渲染树”。 渲染树一棵以状态集(StateSet)和渲染叶(RenderLe…...

[教你传话,表白,写信]

第一步 关注飞鸽传话助手 第二部 点击链接进入 第三步 点击发送,输入内容 第四步 就可以收到了...

物联网在智慧农业中的应用

随看现代科技的不断发展&#xff0c;近年来我国农业的进步是显而易见的。从八九十年代农业生产以人力为主&#xff0c;到之后的机械渐渐代替人力&#xff0c;再到如今物联网技术在农业领域的应用&#xff0c;多种前沿技术应用于农业物联网&#xff0c;对智慧农业生产的各个环节…...

【RabbitMQ】Windows 安装 RabbitMQ

文章目录工具下载Eralng 安装与配置RabbitMQ 安装工具下载 RabbitMQ 3.7.4版本 网盘链接&#xff1a;https://pan.baidu.com/s/1pO6Q8fUbiMrtclpq2KqVVQ?pwdgf29 提取码&#xff1a;gf29 Eralng 网盘链接&#xff1a;https://pan.baidu.com/s/1irf8fgK77k8T9QzsIRwa7g?pwd9…...

MQTT8-MQTT在智能汽车公司的实际应用

一、引言 智能汽车的发展概况 智能汽车作为一种新兴的汽车类型,它的发展历程可以追溯到20世纪90年代。近年来,随着人工智能、物联网、自动驾驶等技术的发展,智能汽车迅速崛起,已经成为汽车行业的一股重要趋势。 智能汽车通过安装传感器、通讯设备和计算设备等,实现了车…...

在elasticsearch8.3中安装elasticsearch-analysis-ik中文分词插件

title: 在elasticsearch8.3中安装elasticsearch-analysis-ik中文分词插件 date: 2022-08-28 00:00:00 tags: ElasticSearchelasticsearch-analysis-ik中文分词插件 categories:ElasticSearch 安装 手动下载 在官方发布页面下载安装包 elasticsearch-analysis-ik-[版本].zip&…...

初识K8s

概览 k8s 概念和架构从零搭建K8s 集群k8s 核心概念搭建集群监控平台搭建高可用k8s集群集群环境 实际部署项目 k8s 概念和架构 1、K8S概述和特性 概述&#xff1a; k8s是谷歌在2014年开源的容器化集群管理系统使用k8s进行容器化应用部署使用k8s利于应用扩展k8s目标实施让部…...

搭建企业级docker仓库—Harbor

一、简介 docker 官方提供的私有仓库 registry&#xff0c;用起来虽然简单 &#xff0c;但在管理的功能上存在不足。 Harbor是一个用于存储和分发Docker镜像的企业级Registry服务器&#xff0c;harbor使用的是官方的docker registry(v2命名是distribution)服务去完成。harbor在…...

【Linux】shell中运算符(整数、字符串)

文章目录1. 整数1.1、算数运算符1.1.1 加减乘除运算1.1.2 号关系运算1.1.2.1 (赋值)、(等于)、!(不等于)的使用1.1.2.2 >、>、<、<的使用1.2 $((运算式)) 双括号形式 、 $[运算式] 语法 进行运算1.3 -eq关系运算符1.4 、、-eq的区别2 字符串2.1 字符串运算3 逻辑运…...

【从零单排Golang】第八话:通过cache缓存模块示范interface该怎么用

和许多面向对象的编程语言一样&#xff0c;Golang也存在interface接口这样的概念。interface相当于是一个中间层&#xff0c;下游只需要关心interface实现了什么行为&#xff0c;利用这些行为做些业务级别事情&#xff0c;而上游则负责实现interface&#xff0c;把这些行为具象…...

解析从Linux零拷贝深入了解Linux-I/O(上)

本文将从文件传输场景以及零拷贝技术深究 Linux I/O 的发展过程、优化手段以及实际应用。前言 存储器是计算机的核心部件之一&#xff0c;在完全理想的状态下&#xff0c;存储器应该要同时具备以下三种特性&#xff1a; 速度足够快&#xff1a;存储器的存取速度应当快于 CPU …...

JavaScript系列之公有、私有和静态属性和方法

文章の目录一、公有属性、公有方法1、定义2、理解3、ES54、ES6二、私有属性、私有方法1、定义2、理解3、ES54、ES6三、静态属性、静态方法1、定义2、理解3、ES54、ES6写在最后一、公有属性、公有方法 1、定义 指的是所属这个类的所有对象都可以访问的属性&#xff0c;叫做公有…...

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…...

19c补丁后oracle属主变化,导致不能识别磁盘组

补丁后服务器重启&#xff0c;数据库再次无法启动 ORA01017: invalid username/password; logon denied Oracle 19c 在打上 19.23 或以上补丁版本后&#xff0c;存在与用户组权限相关的问题。具体表现为&#xff0c;Oracle 实例的运行用户&#xff08;oracle&#xff09;和集…...

Lombok 的 @Data 注解失效,未生成 getter/setter 方法引发的HTTP 406 错误

HTTP 状态码 406 (Not Acceptable) 和 500 (Internal Server Error) 是两类完全不同的错误&#xff0c;它们的含义、原因和解决方法都有显著区别。以下是详细对比&#xff1a; 1. HTTP 406 (Not Acceptable) 含义&#xff1a; 客户端请求的内容类型与服务器支持的内容类型不匹…...

阿里云ACP云计算备考笔记 (5)——弹性伸缩

目录 第一章 概述 第二章 弹性伸缩简介 1、弹性伸缩 2、垂直伸缩 3、优势 4、应用场景 ① 无规律的业务量波动 ② 有规律的业务量波动 ③ 无明显业务量波动 ④ 混合型业务 ⑤ 消息通知 ⑥ 生命周期挂钩 ⑦ 自定义方式 ⑧ 滚的升级 5、使用限制 第三章 主要定义 …...

Spring Boot面试题精选汇总

&#x1f91f;致敬读者 &#x1f7e9;感谢阅读&#x1f7e6;笑口常开&#x1f7ea;生日快乐⬛早点睡觉 &#x1f4d8;博主相关 &#x1f7e7;博主信息&#x1f7e8;博客首页&#x1f7eb;专栏推荐&#x1f7e5;活动信息 文章目录 Spring Boot面试题精选汇总⚙️ **一、核心概…...

智能仓储的未来:自动化、AI与数据分析如何重塑物流中心

当仓库学会“思考”&#xff0c;物流的终极形态正在诞生 想象这样的场景&#xff1a; 凌晨3点&#xff0c;某物流中心灯火通明却空无一人。AGV机器人集群根据实时订单动态规划路径&#xff1b;AI视觉系统在0.1秒内扫描包裹信息&#xff1b;数字孪生平台正模拟次日峰值流量压力…...

Mysql8 忘记密码重置,以及问题解决

1.使用免密登录 找到配置MySQL文件&#xff0c;我的文件路径是/etc/mysql/my.cnf&#xff0c;有的人的是/etc/mysql/mysql.cnf 在里最后加入 skip-grant-tables重启MySQL服务 service mysql restartShutting down MySQL… SUCCESS! Starting MySQL… SUCCESS! 重启成功 2.登…...

从物理机到云原生:全面解析计算虚拟化技术的演进与应用

前言&#xff1a;我的虚拟化技术探索之旅 我最早接触"虚拟机"的概念是从Java开始的——JVM&#xff08;Java Virtual Machine&#xff09;让"一次编写&#xff0c;到处运行"成为可能。这个软件层面的虚拟化让我着迷&#xff0c;但直到后来接触VMware和Doc…...

HTTPS证书一年多少钱?

HTTPS证书作为保障网站数据传输安全的重要工具&#xff0c;成为众多网站运营者的必备选择。然而&#xff0c;面对市场上种类繁多的HTTPS证书&#xff0c;其一年费用究竟是多少&#xff0c;又受哪些因素影响呢&#xff1f; 首先&#xff0c;HTTPS证书通常在PinTrust这样的专业平…...

统计学(第8版)——统计抽样学习笔记(考试用)

一、统计抽样的核心内容与问题 研究内容 从总体中科学抽取样本的方法利用样本数据推断总体特征&#xff08;均值、比率、总量&#xff09;控制抽样误差与非抽样误差 解决的核心问题 在成本约束下&#xff0c;用少量样本准确推断总体特征量化估计结果的可靠性&#xff08;置…...