当前位置: 首页 > 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;叫做公有…...

2026权威代运营排行

嘿&#xff0c;朋友们&#xff01;在如今竞争激烈的商业世界里&#xff0c;代运营服务就像是企业发展的助推器&#xff0c;能帮助企业在市场中披荆斩棘。最近&#xff0c;2026权威代运营排行新鲜出炉啦&#xff0c;今天咱们就来好好聊聊这个事儿&#xff0c;看看哪家代运营公司…...

搭建虚拟机环境Linux

1.安装VMware Workstation2.点击创建虚拟机选项&#xff0c;在弹出的“新建虚拟机向导”&#xff0c;对话框中选择“典型”单选按钮&#xff0c;然后再点“下一步”3.在安装客户机操作系统界面&#xff0c;选中“稍后安装操作系统”单选按钮&#xff0c;然后单击“下一步”4.选…...

ERNIE-4.5-0.3B-PT快速部署教程:vLLM+Chainlit 5分钟搭建文本生成服务

ERNIE-4.5-0.3B-PT快速部署教程&#xff1a;vLLMChainlit 5分钟搭建文本生成服务 想快速体验百度最新轻量级大模型ERNIE-4.5-0.3B-PT的强大文本生成能力吗&#xff1f;今天我就带你用最简单的方式&#xff0c;5分钟搭建一个完整的文本生成服务。不需要复杂的配置&#xff0c;不…...

Unity UGUI性能优化实战:从12个DrawCall降到2个的完整配置流程

Unity UGUI性能优化实战&#xff1a;从12个DrawCall降到2个的完整配置流程 在移动端游戏开发中&#xff0c;UI性能往往是制约流畅度的关键瓶颈。当项目中的UI元素逐渐增多&#xff0c;DrawCall数量会呈指数级增长&#xff0c;导致帧率下降、发热增加等一系列问题。本文将带你深…...

拆解T265鱼眼视觉:用Python+OpenCV玩转200Hz姿态数据的5种创意用法

拆解T265鱼眼视觉&#xff1a;用PythonOpenCV玩转200Hz姿态数据的5种创意用法 当计算机视觉遇上嵌入式AI芯片&#xff0c;会碰撞出怎样的火花&#xff1f;Intel RealSense T265凭借独特的鱼眼双摄与Myriad 2 VPU的完美配合&#xff0c;将V-SLAM算法运行功耗控制在1.5W的同时&am…...

Web Scraper完全攻略:无需编程的网页数据提取终极方案

Web Scraper完全攻略&#xff1a;无需编程的网页数据提取终极方案 【免费下载链接】web-scraper-chrome-extension Web data extraction tool implemented as chrome extension 项目地址: https://gitcode.com/gh_mirrors/we/web-scraper-chrome-extension Web Scraper是…...

【异常】解决Attu连接超时No connection established. Last error: connect ETIMEDOUT 10.x.x.x:19530

解决 connect ETIMEDOUT 连接超时问题 在日常开发和运维过程中,我们经常会遇到网络连接超时类错误,这类问题往往涉及网络、服务、配置等多个层面,本文将以 connect ETIMEDOUT 错误为例,详细解析报错原因及完整解决思路。 一、报错内容 No connection established. Last …...

从零到一:在本地环境搭建Arize Phoenix模型监控平台

1. 为什么选择本地部署Phoenix&#xff1f; 当你训练了一个机器学习模型并部署到生产环境后&#xff0c;最头疼的问题是什么&#xff1f;对我来说&#xff0c;就是模型在线上环境的表现和线下测试时完全不同。你可能也遇到过这种情况&#xff1a;测试集上准确率95%的模型&#…...

深度优先搜索:从全排列到记忆化搜索

深度优先搜索&#xff08;DFS&#xff09;的进化之路&#xff1a;从全排列到记忆化搜索 在算法竞赛中&#xff0c;搜索算法是解决问题的基础。然而&#xff0c;面对不同类型的问题&#xff0c;选用错误的 DFS 模型不仅会导致超时&#xff08;TLE&#xff09;&#xff0c;还容易…...

Spring Cloud Circuit Breaker 2.0.0 M1(Milestone 1)是 Spring Cloud 官方在 2022 年初发布的

Spring Cloud Circuit Breaker 2.0.0 M1(Milestone 1)是 Spring Cloud 官方在 2022 年初发布的 Spring Cloud Circuit Breaker 2.x 系列的首个里程碑版本,标志着该项目从旧版 spring-cloud-netflix-hystrix(已停更)和早期 spring-cloud-circuitbreaker(1.x)向统一、轻量…...