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…...

Baumer工业相机堡盟相机如何使用BGAPI SDK和Opencv联动实现图像转换成视频(C#)
Baumer工业相机堡盟相机如何使用BGAPI SDK和Opencv联动实现图像转换成视频Baumer工业相机Baumer工业相机SDK技术背景代码分析第一步:先引用OpenCV库第二步:引用图像文件夹生成视频工业相机图像通过OpenCV转为视频的优点工业相机图像转为视频的行业应用…...

Redis常用命令以及如何在Java中操作Redis
前言Redis是一个基于内存的key-value结构数据库,是互联网技术领域使用最为广泛的存储中间件。Redis基于内存存储,读写性能高,适合存储热点数据(热点商品、资讯、新闻)。Redis是一个开源的内存中的数据结构存储系统&…...

ASEMI代理AD7980BRMZRL7原装ADI(亚德诺)车规级AD7980BRMZRL7
编辑:ll ASEMI代理AD7980BRMZRL7原装ADI(亚德诺)车规级AD7980BRMZRL7 型号:AD7980BRMZRL7 品牌:ADI/亚德诺 封装:MSOP-10 批号:2023 安装类型:表面贴装型 AD7980BRMZRL7 汽车…...

leetcode141:环形链表
给你一个链表的头节点 head ,判断链表中是否有环。 如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(…...

lattice diamond软件使用
1.diamond软件破解: lisence坚果云下载;或者这个博主操作环境变量设置: 2. 调用IP 有两种方式,依据芯片或者软件版本改变。 传统的IPexpress,每个IP单独例化。 新出的Clarity,多个IP在同一个顶层内调用…...

scala泛型
目录 类型参数 泛型函数: 协变,逆变,不变 泛型上下限: 上下文限定: 泛型是一种类型参数,该类型参数可以用在类、接口和方法中,分别被称为泛型类、泛型接口、泛型方法 类型参数 调用时不指定…...

程序员与ChatGPT的日常问答
程序员与ChatGPT的日常问答GPT3.5与GPT4.0能力对比技术问题工具问题编解码问题其他问题本文记录下调教ChatGPT的日常。 GPT3.5与GPT4.0能力对比 Q:采用同一个问题提问,对比下GPT3.5和GPT4.0的能力区别,比如:帮我列一个小白入门音频…...

如何创建高效的Prompt和ChatGPT等大语言模型AI对话
大语言模型,如OpenAI的GPT-4,是一种基于深度学习技术的自然语言处理工具,它可以理解自然语言并为用户提供有价值的回答。然而,要从大语言模型中获得高质量的回答,你需要学会如何高效地提问。本文将从原理出发ÿ…...

043:cesium加载Bing地图(多种形式)
第043个 点击查看专栏目录 本示例的目的是介绍如何在vue+cesium中加载加载Bing地图。这里显示4种形式的地图,分别为:AERIAL、ROAD、CANVAS_DARK、AERIAL_WITH_LABELS。参考后面的API,还有其他几种形式。 直接复制下面的 vue+cesium源代码,操作2分钟即可运行实现效果. 文章…...

vscode代码片段生成
在刚学习vue的时候,有些代码片段是经常写的,在vscode中写一个代码片段可以帮助快速生成。 生成步骤: VSCode中的代码片段有固定的格式,所以我们一般会借助于一个在线工具来完成。 具体的步骤如下: 第一步,复制自己需…...