Retrofit源码分析小结
Retrofit源码分析&小结
简介
Retrofit是对Okhttp网络请求的二次封装,通过注解+动态代理的方式,简化了Okhttp的使用,使得通过简单的配置就可以像调用接口一样去请求网络接口;除此之外Retrofit还支持RxJava和kotlin的协程
基本使用
- 引入依赖库
//网络请求
implementation 'com.squareup.retrofit2:retrofit:2.9.0'//Retrofit基本库
implementation 'com.squareup.retrofit2:converter-gson:2.1.0'//用于将结果转换成对象
implementation 'com.squareup.retrofit2:adapter-rxjava2:2.3.0'//用于转换成RxJava支持的Observer对象
implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'//RxJava对Android的支持
implementation 'com.squareup.okhttp3:logging-interceptor:3.9.0'//Okhttp日志拦截器,用于打印接口请求相关log
//协程
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4'//Retrofit对Kotlin协程的支持
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4'//协程对Android的支持
implementation('androidx.lifecycle:lifecycle-runtime-ktx:2.6.0-alpha02')//协程对LifeCycle的支持
- 定义一个用于网络请求的服务接口
package com.example.myapplication.retrofitimport android.service.autofill.UserData
import io.reactivex.Observable
import retrofit2.Call
import retrofit2.http.*interface IUserServer {/*** 以GET方式请求*/@GET("getUserInfo/")fun getUserInfo(@Query("userId") userId: String): Call<UserData>/*** 以POST方式请求,并结合RxJava*/@POST("getUserInfo/")@FormUrlEncodedfun getUserInfoByRxJava(@Field("userId") userId: String): Observable<UserData>/*** 结合kotlin协程完成线程切换*/@GET("banner/json")suspend fun getUserInfoBySuspend2(@Query("userId") userId: String): UserData}
- 创建Retrofit对象和自定义接口代理实现对象
val retrofit = Retrofit.Builder().baseUrl("http://www.xxx.com/").addConverterFactory(GsonConverterFactory.create())//支持Gson.addCallAdapterFactory(RxJava2CallAdapterFactory.create())//支持Rxjava.build()
val server = retrofit.create(IUserServer::class.java)
- 普通发起网络请求
val userInfo = server.getUserInfo("1").execute()//同步执行
server.getUserInfo("1").enqueue(object : Callback<UserData> {//异步执行override fun onResponse(call: Call<UserData>, response: Response<UserData>) {//网络请求返回结果}override fun onFailure(call: Call<UserData>, t: Throwable) {//网络请求错误}
})
- 结合RxJava
server.getUserInfoByRxJava("1").subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : Observer<UserData> {override fun onSubscribe(d: Disposable?) {//网络请求前}override fun onNext(value: UserData?) {//网络请求返回结果}override fun onError(e: Throwable?) {//网络请求错误}override fun onComplete() {//网络请求结束}
})
- 结合Kotlin协程
lifecycleScope.launch {val userInfo = withContext(Dispatchers.IO) {val userInfo = server.getUserInfoBySuspend2("1")//子线程中请求网络}//回到主线程Log.i("testRetrofit", "userInfo:$userInfo")
}
核心源码分析
1. Retrofit.create方法,根据注解为我们声明的接口创建动态代理
```
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];@Overridepublic @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;return platform.isDefaultMethod(method)//判断该方法是否用户自定义的? platform.invokeDefaultMethod(method, service, proxy, args): loadServiceMethod(method).invoke(args);//开始解析该方法并返回接口请求对象,比如Call、Observable(需结合Rxjava)}});
}
```
-
在validateServiceInterface方法中,如果配置了Retrofit.Builder().validateEagerly(true),会立刻根据注解解析接口中定义的所有方法,如果是false,则会等待方法调用时才会解析接口中的方法;
设置为true的好处是在创建接口代理时就能检查出各个方法配置的注解、返回值等是否正确,如果为默认的false,则只有在方法调用时才能发现问题;
所以建议在debug阶段设置为true便于及早发现问题,release阶段设置false以提高性能
private void validateServiceInterface(Class<?> service) {... ...if (validateEagerly) {Platform platform = Platform.get();for (Method method : service.getDeclaredMethods()) {//遍历所有方法,根据注解进行解析if (!platform.isDefaultMethod(method) && !Modifier.isStatic(method.getModifiers())) {loadServiceMethod(method);}}} }
-
默认实现中接口返回的Call实际上是一个Retrofit中定义的一个接口,定义了同步请求和异步请求的方法;
public interface Call<T> extends Cloneable {... .../*** Synchronously send the request and return its response.** @throws IOException if a problem occurred talking to the server.* @throws RuntimeException (and subclasses) if an unexpected error occurs creating the request or* decoding the response.*/Response<T> execute() throws IOException;/*** Asynchronously send the request and notify {@code callback} of its response or if an error* occurred talking to the server, creating the request, or processing the response.*/void enqueue(Callback<T> callback);... ... }
-
Call的实现类根据是否有注解@SkipCallbackExecutor来决定,当有该注解时,Call的实现类是OkHttpCall.java,里面封装了OkHttp的网络请求,如果没有该注解,则Call实现类是DefaultCallAdapterFactory.ExecutorCallbackCall;这个类仅仅是个代理类,真正实现网络请求的还是OkHttpCall;
代理类存在的意义是Okhttp进行异步请求返回结果后,会先通过Handler将线程切换到主线程再返回结果final class OkHttpCall<T> implements Call<T> {@Overridepublic void enqueue(final Callback<T> callback) {...okhttp3.Call call;call.enqueue(new okhttp3.Callback() {@Overridepublic void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {}@Overridepublic void onFailure(okhttp3.Call call, IOException e) {callFailure(e);}}...}@Overridepublic Response<T> execute() throws IOException {okhttp3.Call call;...return parseResponse(call.execute());} }
static final class ExecutorCallbackCall<T> implements Call<T> {final Executor callbackExecutor;//在主线程执行,实现类是Platform.Android.MainThreadExecutorfinal Call<T> delegate;//这个实现类是OkHttpCall@Overridepublic void enqueue(final Callback<T> callback) {delegate.enqueue(new Callback<T>() {@Overridepublic void onResponse(Call<T> call, final Response<T> response) {callbackExecutor.execute(() -> {//切换到主线程执行callback.onResponse(ExecutorCallbackCall.this, response);}});}@Overridepublic void onFailure(Call<T> call, final Throwable t) {//切换到主线程执行callbackExecutor.execute(() -> callback.onFailure(ExecutorCallbackCall.this, t));}});}@Overridepublic Response<T> execute() throws IOException {return delegate.execute();//在当前所在线程执行} }
2. 注解的解析过程和使用
-
在Retrofit.create方法中,loadServiceMethod方法中会先从缓存中查找ServiceMethod对象,如果之前有解析过则直接返回,如果没有则调用ServiceMethod.parseAnnotations方法返回一个ServiceMethod对象
ServiceMethod<?> loadServiceMethod(Method method) {ServiceMethod<?> result = serviceMethodCache.get(method);if (result != null) return result;synchronized (serviceMethodCache) {result = serviceMethodCache.get(method);if (result == null) {result = ServiceMethod.parseAnnotations(this, method);serviceMethodCache.put(method, result);}}return result; }
-
接着会调用RequestFactory.parseAnnotations方法真正进行注解的解析,并构建一个HttpServiceMethod对象返回
abstract class ServiceMethod<T> {static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {//真正解析方法上的注解入口RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);//检查方法返回类型是否符合规范Type returnType = method.getGenericReturnType();if (Utils.hasUnresolvableType(returnType)) {throw methodError(method,"Method return type must not include a type variable or wildcard: %s",returnType);}if (returnType == void.class) {throw methodError(method, "Service methods cannot return void.");}return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory); }
-
RequestFactory.Builder中解析方法中的注解,可以理解为RequestFactory对象保存着所有请求的数据
final class RequestFactory {static RequestFactory parseAnnotations(Retrofit retrofit, Method method) {return new Builder(retrofit, method).build();}static final class Builder {final Annotation[] methodAnnotations;//方法上的注解final Annotation[][] parameterAnnotationsArray;//方法参数上的注解final Type[] parameterTypes;//参数类型@Nullable String httpMethod;//请求的方式get、post等@Nullable String relativeUrl;//解析处理后的请求接口@Nullable Headers headers;//封装的请求头信息boolean isKotlinSuspendFunction; //用于标记是否kotlin协程中suspend方法...private Headers parseHeaders(String[] headers) {}//解析请求头信息private void parseHttpMethodAndPath(String httpMethod, String value, boolean hasBody) //解析请求方式和接口路径private void parseMethodAnnotation(Annotation annotation) {//解析方法上的注解if (annotation instanceof DELETE) {parseHttpMethodAndPath("DELETE", ((DELETE) annotation).value(), false);} else if (annotation instanceof GET) {parseHttpMethodAndPath("GET", ((GET) annotation).value(), false);} else if (annotation instanceof HEAD) {parseHttpMethodAndPath("HEAD", ((HEAD) annotation).value(), false);} else if (annotation instanceof PATCH) {parseHttpMethodAndPath("PATCH", ((PATCH) annotation).value(), true);} else if (annotation instanceof POST) {parseHttpMethodAndPath("POST", ((POST) annotation).value(), true);} else if (annotation instanceof PUT) {parseHttpMethodAndPath("PUT", ((PUT) annotation).value(), true);}...}private ParameterHandler<?> parseParameterAnnotation(){//解析参数上注解if (annotation instanceof Path) {} else if (annotation instanceof Query) {} else if (annotation instanceof QueryName) {} else if (annotation instanceof QueryMap) {} else if (annotation instanceof Header) {} else if (annotation instanceof HeaderMap) {} else if (annotation instanceof Field) {} else if (annotation instanceof FieldMap) {} else if (annotation instanceof Body) {}...}} }
-
请求参数的使用,是在真正发起网络请求OkhttpCall.createRawCall()方法中创建Okhttp Call对象的时候,通过RequestFactory.create方法将所有请求参数封装成OkHttp的Request对象
class OkHttpCall{private okhttp3.Call createRawCall() throws IOException {okhttp3.Call call = callFactory.newCall(requestFactory.create(args));return call;}
}
class RequestFactory{okhttp3.Request create(Object[] args) throws IOException {RequestBuilder requestBuilder =new RequestBuilder(httpMethod,baseUrl,relativeUrl,headers,contentType,hasBody,isFormEncoded,isMultipart);if (isKotlinSuspendFunction) {//协程suspen方法会自动在最后面加一个Continuation对象类型参数,所以实际请求时要去掉// The Continuation is the last parameter and the handlers array contains null at that index.argumentCount--;}List<Object> argumentList = new ArrayList<>(argumentCount);for (int p = 0; p < argumentCount; p++) {argumentList.add(args[p]);handlers[p].apply(requestBuilder, args[p]);}//构建okhttp3.Request对象return requestBuilder.get().tag(Invocation.class, new Invocation(method, argumentList)).build();}
}
3. RxJava2CallAdapterFactory:对Rxjava的支持,将默认返回的的Call对象转换成Observer对象
- 当调用接口方法的时候,通过动态代理默认会调用HttpServiceMethod.CallAdapted的invoke方法,而CallAdapted继承自HttpServiceMethod,并重写了adapt方法;
adapt方法用于封装Call对象并将结果转化成我们定义的接口方法所声明的返回对象
class HttpServiceMethod{@Overridefinal @Nullable ReturnT invoke(Object[] args) {Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);return adapt(call, args);}protected abstract @Nullable ReturnT adapt(Call<ResponseT> call, Object[] args);static final class CallAdapted<ResponseT, ReturnT> extends HttpServiceMethod<ResponseT, ReturnT> {private final CallAdapter<ResponseT, ReturnT> callAdapter;//默认的适配器是通过DefaultCallAdapterFactory动态生成的CallAdapter实现类CallAdapted(RequestFactory requestFactory,okhttp3.Call.Factory callFactory,Converter<ResponseBody, ResponseT> responseConverter,CallAdapter<ResponseT, ReturnT> callAdapter) {super(requestFactory, callFactory, responseConverter);this.callAdapter = callAdapter;//通过RxJava2CallAdapterFactory生成的Rxjava2适配器RxJava2CallAdapter}@Overrideprotected ReturnT adapt(Call<ResponseT> call, Object[] args) {return callAdapter.adapt(call);//调用适配器,将返回结果转换成我们定义的返回类型,比如默认是返回Call,Rxjava返回的是Observer对象}}
}
- RxJava2CallAdapterFactory在创建时,可以选择调用create、createAsync、createWithScheduler(Scheduler)方法进行创建,他们的主要区别是create是调用Call.execute方法请求网络,也就是在当前线程执行;
而createAsync则是调用Call.enqueue方法请求网络,也就是异步请求;createWithScheduler可以传入一个Scheduler,指定网络请求在哪个线程上执行,通过observable.subscribeOn(scheduler)实现
public final class RxJava2CallAdapterFactory extends CallAdapter.Factory {/*** Returns an instance which creates synchronous observables that do not operate on any scheduler* by default.*/public static RxJava2CallAdapterFactory create() {return new RxJava2CallAdapterFactory(null, false);}/*** Returns an instance which creates asynchronous observables. Applying* {@link Observable#subscribeOn} has no effect on stream types created by this factory.*/public static RxJava2CallAdapterFactory createAsync() {return new RxJava2CallAdapterFactory(null, true);}public static RxJava2CallAdapterFactory createWithScheduler(Scheduler scheduler) {if (scheduler == null) throw new NullPointerException("scheduler == null");return new RxJava2CallAdapterFactory(scheduler, false);}@Overridepublic CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {...return new RxJava2CallAdapter(responseType, scheduler, isAsync, isResult, isBody, isFlowable,isSingle, isMaybe, false);}
}
- 调用RxJava2CallAdapter.adapt方法,将Call传入并返回Observer
final class RxJava2CallAdapter<R> implements CallAdapter<R, Object> {@Override public Object adapt(Call<R> call) {Observable<Response<R>> responseObservable = isAsync? new CallEnqueueObservable<>(call): new CallExecuteObservable<>(call);//真正网络请求包装在在这里面...Observable<?> observable;if (isResult) {observable = new ResultObservable<>(responseObservable);} else if (isBody) {observable = new BodyObservable<>(responseObservable);} else {observable = responseObservable;}if (scheduler != null) {observable = observable.subscribeOn(scheduler);}...return observable;}
}
- 当外部调用Observer的subscribe方法时,后就会立刻执行CallExecuteObservable/CallExecuteObservable里的subscribeActual方法,从而调用Call的execute或者enqueue方法实现网络请求
class Observable{public final void subscribe(Observer<? super T> observer) {subscribeActual(observer);}
}final class CallExecuteObservable<T> extends Observable<Response<T>> {@Override protected void subscribeActual(Observer<? super Response<T>> observer) {Call<T> call = originalCall.clone();Response<T> response = call.execute();//发起网络请求并返回结果if (!call.isCanceled()) {//传递给onNext方法observer.onNext(response);}if (!call.isCanceled()) {terminated = true;observer.onComplete();}}
}
4. GsonConverterFactory:网络请求返回结果的处理
- 在OkHttpCall发起网络请求后,会调用parseResponse方法解析返回结果,然后通过结果转换器进行转换,默认转换器是BuiltInConverters,如果配置了GsonConverterFactory,则支持转换成我们自定义对象,实现类是GsonResponseBodyConverter
class OkHttpCall{@Overridepublic Response<T> execute() throws IOException {okhttp3.Call call;call = getRawCall();...return parseResponse(call.execute());}Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {ResponseBody rawBody = rawResponse.body();...ExceptionCatchingResponseBody catchingBody = new ExceptionCatchingResponseBody(rawBody);try {//调用结果转换器进行转换,这里会根据声明方法返回值类型选择不同转换器,比如返回默认的ResponseBody,则使用自带的BuiltInConverters转换,返回自定义对象则会使用GsonResponseBodyConverter进行转换T body = responseConverter.convert(catchingBody);return Response.success(body, rawResponse);} catch (RuntimeException e) {// If the underlying source threw an exception, propagate that rather than indicating it was// a runtime exception.catchingBody.throwIfCaught();throw e;}}
}
- Retrofit自带的默认转换器BuiltInConverters,支持处理返回类型是ResponseBody/Void 或者kotlin的Unit
final class BuiltInConverters extends Converter.Factory {/** Not volatile because we don't mind multiple threads discovering this. */private boolean checkForKotlinUnit = true;@Overridepublic @Nullable Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {if (type == ResponseBody.class) {return Utils.isAnnotationPresent(annotations, Streaming.class)? StreamingResponseBodyConverter.INSTANCE: BufferingResponseBodyConverter.INSTANCE;}if (type == Void.class) {return VoidResponseBodyConverter.INSTANCE;}if (checkForKotlinUnit) {try {if (type == Unit.class) {return UnitResponseBodyConverter.INSTANCE;}} catch (NoClassDefFoundError ignored) {checkForKotlinUnit = false;}}return null;}
}
- Gson转换器GsonResponseBodyConverter,支持自定义对象类型的转换
final class GsonResponseBodyConverter<T> implements Converter<ResponseBody, T> {private final Gson gson;private final TypeAdapter<T> adapter;GsonResponseBodyConverter(Gson gson, TypeAdapter<T> adapter) {this.gson = gson;this.adapter = adapter;}@Override public T convert(ResponseBody value) throws IOException {JsonReader jsonReader = gson.newJsonReader(value.charStream());try {return adapter.read(jsonReader);} finally {value.close();}}
}
5. 对kotlin协程的支持
- 判断是否kotlin suspend方法,在kotlin中suspend修饰的方法在编译成字节码并反编译成java代码后会发现方法参数里最后会多一个Continuation类型的参数,用于对协程的支持
kotlin字节码反编译后的java代码
public interface IUserServer {... ...@GET("banner/json")@NullableObject getUserInfoBySuspend2(@Query("userId") @NotNull String var1, @NotNull Continuation var2);
}
- Retrofit在RequestFactory解析方法参数时做判断
class RequestFactory{class Builder{boolean isKotlinSuspendFunction;private @Nullable ParameterHandler<?> parseParameter(int p, Type parameterType, @Nullable Annotation[] annotations, boolean allowContinuation) {try {if (Utils.getRawType(parameterType) == Continuation.class) {//判断是否suspend协程方法isKotlinSuspendFunction = true;return null;}} catch (NoClassDefFoundError ignored) {}}}
}
- 根据方法返回类型,返回不同的HttpServiceMethod,当suspend方法直接返回自定义数据类型时(比如 UserData),返回的是SuspendForBody对象; 如果方法返回的是Response,则返回SuspendForResponse对象,这两个返回对象其实差不多,只是返回类型不一样
class HttpServiceMethod{static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(Retrofit retrofit, Method method, RequestFactory requestFactory) {boolean isKotlinSuspendFunction = requestFactory.isKotlinSuspendFunction;boolean continuationWantsResponse = false;if (getRawType(responseType) == Response.class && responseType instanceof ParameterizedType) {// Unwrap the actual body type from Response<T>.responseType = Utils.getParameterUpperBound(0, (ParameterizedType) responseType);continuationWantsResponse = true;}if (continuationWantsResponse) {//返回Response<UserData>类型结果return (HttpServiceMethod<ResponseT, ReturnT>)new SuspendForResponse<>(requestFactory,callFactory,responseConverter,(CallAdapter<ResponseT, Call<ResponseT>>) callAdapter);} else {//返回UserData类型结果return (HttpServiceMethod<ResponseT, ReturnT>)new SuspendForBody<>(requestFactory,callFactory,responseConverter,(CallAdapter<ResponseT, Call<ResponseT>>) callAdapter,continuationBodyNullable);}}
}
- 继续以SuspendForBody分析,在adapt方法被调用时,Retrofit会调用kotlin的扩展方法await/awaitNullable
static final class SuspendForBody<ResponseT> extends HttpServiceMethod<ResponseT, Object> {private final CallAdapter<ResponseT, Call<ResponseT>> callAdapter;private final boolean isNullable;@Overrideprotected Object adapt(Call<ResponseT> call, Object[] args) {call = callAdapter.adapt(call);//这里是默认的DefaultCallAdapterFactorys产生的Adapter//获取suspend最后一个参数用于协程Continuation<ResponseT> continuation = (Continuation<ResponseT>) args[args.length - 1];try {//调用Retrofit使用kotlin对Call类的扩展方法return isNullable //返回的对象是否可为空? KotlinExtensions.awaitNullable(call, continuation): KotlinExtensions.await(call, continuation);} catch (Exception e) {return KotlinExtensions.suspendAndThrow(e, continuation);}}
}
- kotlin对Call方法扩展解析,协程方法中会异步发起网络请求并返回结果,当结束后会恢复到父协程地方继续执行
//这里是suspend方法,执行时协程会挂起
suspend fun <T : Any> Call<T>.await(): T {return suspendCancellableCoroutine { continuation ->continuation.invokeOnCancellation {cancel()//设置协程取消时的回调,调用Call的cancel方法}enqueue(object : Callback<T> {//调用Call方法异步请求网络override fun onResponse(call: Call<T>, response: Response<T>) {if (response.isSuccessful) {val body = response.body()if (body == null) {val invocation = call.request().tag(Invocation::class.java)!!val method = invocation.method()val e = KotlinNullPointerException("Response from " +method.declaringClass.name +'.' +method.name +" was null but response body type was declared as non-null")continuation.resumeWithException(e)//恢复协程并抛出异常} else {continuation.resume(body)//恢复协程并返回结果}} else {//恢复协程并抛出异常continuation.resumeWithException(HttpException(response))}}override fun onFailure(call: Call<T>, t: Throwable) {continuation.resumeWithException(t)//恢复协程并抛出异常}})}
}
小结
-
Retrofit实现原理:
答:Retrofit通过Create方法创建我们自定义接口实例时,会为我们接口创建一个动态代理;当调用接口方法的时候,会先根据配置的注解解析这个方法的信息,包括请求路径、请求参数、返回值等等,并把解析后的信息封装成对象并缓存起来;
然后根据方法的信息封装成一个Call对象,这个Call对象的默认实现类是OkHttpCall,内部是通过Okhttp发起同步或者异步网络请求;
然后调用Call的execute同步或者enqueue异步方法进行网络请求,返回结果后,会调用转换器对结果进行转换,默认是返回ResponseBody,也可以通过配置Gson转换器转成我们自定义类型;
如果需要对RxJava支持,返回Observer对象,则是需要配置一个Rxjava的CallAdapter,在适配器中将Call对象封装到Observer对象中并返回,当Observer的subscribe方法调用时会触发Call的网络请求操作,最后通过RxJava对结果分发;
如果需要对kotlin协程支持,Retrofit在对方法解析时会判断是否suspend方法,如果是的话,会执行Call的kotlin扩展方法,扩展方法也是suspend类型,在扩展方法中会挂起协程,通过Call对象执行网络请求操作,最后通过Continuation.resume方法恢复协程到父协程调用的地方 -
静态代理和动态代理的区别?
答:静态代理是指在代码编写的时候,代理类和被代理类的关系就确定了,编译后也会生成代理类字节码文件;而动态代理是指在运行时期,动态的通过反射方式为被代理的接口生成一个代理类,当调用这个接口的方法时,都会进入InvokeHandler的invoke方法中,从而实现对这个被代理接口的统一处理;
静态代理适合被代理的类比较少的情况,如果代理的类比较多,而且不需要做统一处理,则动态代理方便很多 -
Retrofit返回结果是如何切换到主线程的?
答:Retrofit会通过系统属性来判断是否Android平台,如果是Android平台,则会创建一个执行器,内部会创建一个主线程Looper的Handler,当网络请求结束返回结果时,会封装一个Runnable通过这个主线程Handler去执行,从而切换到主线程;
或者通过RxJava或者协程进行线程切换 -
对Kotlin协程(suspend方法)的支持
- 判断协程方法条件:判断方法最后一个参数是否是Continuation.class类型
- Retrofit使用kotlin对Call类扩展了suspend方法,在suspend方法中执行Call的网络请求,然后通过Continuation的resume方法恢复协程挂起并返回结果
- 什么时候切回主线程的?在协程中调用continuation.resume方法后会自动回到它父协程所在线程,也就是主线程继续执行
相关文章:
Retrofit源码分析小结
Retrofit源码分析&小结 简介 Retrofit是对Okhttp网络请求的二次封装,通过注解动态代理的方式,简化了Okhttp的使用,使得通过简单的配置就可以像调用接口一样去请求网络接口;除此之外Retrofit还支持RxJava和kotlin的协程 基本…...
【从零开始学习 UVM】11.4、UVM Register Layer —— UVM Register Model 实战项目(RAL实战,交通灯为例)
文章目录 DesignInterfaceRegister Model ExampleRegister EnvironmentAPB Agent ExampleTestbench EnvironmentSequencesTest在之前的几篇文章中,我们已经了解了寄存器模型是什么以及如何使用它来访问给定设计中的寄存器。现在让我们看一个完整的例子,展示如何为给定设计编写…...
session和token的登录机制
做登录的时候遇到了token ,web和smtp的登录情况,这里 记录一下我所学习的两种登录方式,一种是token ,一种是session session 登录机制 当用户请求登录接口进行登录服务端 获得登录的信息,从而在数据库中查到相应的用…...

大厂研发成本大曝光,研发占大头
近日,腾讯发布《2022 年腾讯研发大数据报告》,披露了 2022 年腾讯在研发投入、研发效能、开源协同等方面的重要数据。 《报告》显示,2022 年腾讯内部研发人员占比达到 74%,这意味着,平均每四个腾讯员工中,…...
python爬虫第一节基础概念
爬虫是一种自动化抓取互联网上数据的技术。在网络信息爆炸的今天,爬虫技术已经成为数据获取和信息分析的重要手段。本文将详细介绍爬虫的基础知识和操作,帮助初学者快速入门。 一、爬虫的基本原理 爬虫的基本原理是通过网络请求获取网页源代码…...

web学习---Vue---笔记(1)
该笔记是记录尚硅谷的Vue学习视频的笔记,视频地址为:学习视频地址 初始Vue Vue组件化的特点 组件化声明式编码虚拟DOMDiff算法,尽量复用DOM节点 H5的组件,是把某一个模块封装,里面写HTML\CSS\JS等,算是一…...

【前端面试题——微信小程序】
目录1.请谈谈wxml与标准的html的异同?2.请谈谈WXSS和CSS的异同?3.请谈谈微信小程序主要目录和文件的作用?4.请谈谈小程序的双向绑定和vue的异同?5.简单描述下微信小程序的相关文件类型?6.微信小程序有哪些传值(传递数据…...

gpt模型训练-gpt3模型详解
训练一个GPT模型需要大量的数据集和计算资源。在这里,我提供一些较为通用的训练步骤以供参考: 获取数据集 首先需要收集一些数据集,数据集建议获取大型的常用文本数据集。常见的例如维基百科、各种在线文章、小说、论文等,数据集…...

vue尚品汇商城项目-day04【27.分页器静态组件(难点)】
文章目录27.分页器静态组件(难点)本人其他相关文章链接27.分页器静态组件(难点) 难点: 考虑点1:为啥需要分页呢? 答案:按需加载 考虑点2:分页器展示,需要哪…...

使用SeaFile搭建私有云盘并公网访问【cpolar内网穿透】
文章目录1. 前言2. SeaFile云盘设置2.1 Owncould的安装环境设置2.2 SeaFile下载安装2.3 SeaFile的配置3. cpolar内网穿透3.1 Cpolar下载安装3.2 Cpolar的注册3.3 Cpolar云端设置3.4 Cpolar本地设置4. 公网访问测试5. 结语1. 前言 现在我们身边的只能设备越来越多,各…...

蓝桥杯第26天(Python)考前挣扎
题型: 1.思维题/杂题:数学公式,分析题意,找规律 2.BFS/DFS:广搜(递归实现),深搜(deque实现) 3.简单数论:模,素数(只需要…...
WuThreat身份安全云-TVD每日漏洞情报-2023-04-04
漏洞名称:RSA NetWitness Platform 内存损坏漏洞 漏洞级别:中危 漏洞编号:CVE-2022-47529,CNNVD-202303-2419 相关涉及:RSA NetWitness Platform 12.2之前版本 漏洞状态:POC 参考链接:https://tvd.wuthreat.com/#/listDetail?TVD_IDTVD-2023-07193 漏洞名称:EyouCms <1.5.…...

【C++】Step by Step的格式化代码风格是这样的吗?
文章目录前言一、依赖二、配置总结前言 本节从0开始讲解如何格式化自己的代码风格,使用vscode插件来完成,本节的所有配置都会在星球同步哦~ 一、依赖 本次使用的是clang-format插件,具体安装比较简单: mac系统: br…...

aspnet030高校学生团体管理系统sqlserver
net030高校学生团体管理系统 . 1.用户基本信息管理模块:录入、修改、删除、查询、统计、打印等功能 2.学生成绩管理模块:录入、修改、删除、查询、统计、打印等功能 3.学生团体信息管理模块:录入、修改、删除、查询、统计、打印等功能 4.教…...

学习HM微博项目第10天
步骤:发微博12-表情键盘06-点击表情 -> 发微博13-表情键盘07-插入表情和封装textView -> 发微博14-表情键盘08-长按表情 -> 发微博15-表情键盘09-最近表情 -> 发微博16-表情键盘10-最近表情完善 发微博12-表情键盘06-点击表情 APP的演示动画ÿ…...

0204强连通性-有向图-数据结构和算法(Java)
文章目录1 概述2 强连通分量2.1 定义2.2 Kosaraju算法2.2.1 算法实现2.2.2算法测试2.2.3 算法理解3 强连通性结语1 概述 定义。如果2个顶点是相互可达的,则称它们为强连通的。如果一幅有向图中的任意两个顶点都是强连通的,则称这幅有向图也是强连通的。 …...

ElasticSearch集群
5.2 IK分词器简介 IKAnalyzer是一个开源的,基于java语言开发的轻量级的中文分词工具包。从2006年12月推出1.0版开始,IKAnalyzer已经推出 了3个大版本。最初,它是以开源项目Lucene为应用主体的,结合词典分词和文法分析算法的中文分…...

音视频基础概念(6)——视频基础
网上冲浪时,我们会接触到网络流媒体和本地视频文件。常见的视频文件格式有MP4、MKV、AVI等。在流媒体网站上看见视频常用的协议有HTTP、RTSP、RTMP、HLS等。视频技术较为复杂,包括视频封装、视频编解码、视频播放和视频转码等内容。1 视频基础概念当下市…...
【Python网络蜘蛛】基础 - 多线程和多进程的基本原理
文章目录多线程和多进程的基本原理多线程的含义并发和并行Python中的多线程和多进程多线程和多进程的基本原理 在编写爬虫程序的时候,为了提高爬取效率,我们可能会同时运行多个爬虫任务,其中同样涉及多进程和多线程。 多线程的含义 先了解一…...
linux C/C++文件路径操作
标题1、 access函数查找文件夹是否存在/文件是否有某权限 头文件: 在windows环境下头文件为: #include <io.h> 在linux环境下头文件为: #include <unistd.h> 函数原型: int access(const char* _Filename, int _Acce…...

盘古信息PCB行业解决方案:以全域场景重构,激活智造新未来
一、破局:PCB行业的时代之问 在数字经济蓬勃发展的浪潮中,PCB(印制电路板)作为 “电子产品之母”,其重要性愈发凸显。随着 5G、人工智能等新兴技术的加速渗透,PCB行业面临着前所未有的挑战与机遇。产品迭代…...
【Java学习笔记】Arrays类
Arrays 类 1. 导入包:import java.util.Arrays 2. 常用方法一览表 方法描述Arrays.toString()返回数组的字符串形式Arrays.sort()排序(自然排序和定制排序)Arrays.binarySearch()通过二分搜索法进行查找(前提:数组是…...

vscode(仍待补充)
写于2025 6.9 主包将加入vscode这个更权威的圈子 vscode的基本使用 侧边栏 vscode还能连接ssh? debug时使用的launch文件 1.task.json {"tasks": [{"type": "cppbuild","label": "C/C: gcc.exe 生成活动文件"…...

高频面试之3Zookeeper
高频面试之3Zookeeper 文章目录 高频面试之3Zookeeper3.1 常用命令3.2 选举机制3.3 Zookeeper符合法则中哪两个?3.4 Zookeeper脑裂3.5 Zookeeper用来干嘛了 3.1 常用命令 ls、get、create、delete、deleteall3.2 选举机制 半数机制(过半机制࿰…...
【ROS】Nav2源码之nav2_behavior_tree-行为树节点列表
1、行为树节点分类 在 Nav2(Navigation2)的行为树框架中,行为树节点插件按照功能分为 Action(动作节点)、Condition(条件节点)、Control(控制节点) 和 Decorator(装饰节点) 四类。 1.1 动作节点 Action 执行具体的机器人操作或任务,直接与硬件、传感器或外部系统…...

如何在网页里填写 PDF 表格?
有时候,你可能希望用户能在你的网站上填写 PDF 表单。然而,这件事并不简单,因为 PDF 并不是一种原生的网页格式。虽然浏览器可以显示 PDF 文件,但原生并不支持编辑或填写它们。更糟的是,如果你想收集表单数据ÿ…...
Python ROS2【机器人中间件框架】 简介
销量过万TEEIS德国护膝夏天用薄款 优惠券冠生园 百花蜂蜜428g 挤压瓶纯蜂蜜巨奇严选 鞋子除臭剂360ml 多芬身体磨砂膏280g健70%-75%酒精消毒棉片湿巾1418cm 80片/袋3袋大包清洁食品用消毒 优惠券AIMORNY52朵红玫瑰永生香皂花同城配送非鲜花七夕情人节生日礼物送女友 热卖妙洁棉…...

(一)单例模式
一、前言 单例模式属于六大创建型模式,即在软件设计过程中,主要关注创建对象的结果,并不关心创建对象的过程及细节。创建型设计模式将类对象的实例化过程进行抽象化接口设计,从而隐藏了类对象的实例是如何被创建的,封装了软件系统使用的具体对象类型。 六大创建型模式包括…...

day36-多路IO复用
一、基本概念 (服务器多客户端模型) 定义:单线程或单进程同时监测若干个文件描述符是否可以执行IO操作的能力 作用:应用程序通常需要处理来自多条事件流中的事件,比如我现在用的电脑,需要同时处理键盘鼠标…...

论文阅读笔记——Muffin: Testing Deep Learning Libraries via Neural Architecture Fuzzing
Muffin 论文 现有方法 CRADLE 和 LEMON,依赖模型推理阶段输出进行差分测试,但在训练阶段是不可行的,因为训练阶段直到最后才有固定输出,中间过程是不断变化的。API 库覆盖低,因为各个 API 都是在各种具体场景下使用。…...