“This article has participated in the good article call order activity, click to see: back end, big front end double track submission, 20,000 yuan prize pool for you to challenge!”
Retrofit comprehensive parsing
A type-safe HTTP client for Android and Java
Source analysis version: implementation ‘com. Squareup. Retrofit2: retrofit: 2.9.0’
The use of the Retrofit
About the use of the Retrofit, here no longer than the interpretation of the, believe that as long as it is for Android development will use Retrofit square. The dead simple. IO/Retrofit/the use of the most simple way is as follows:
interface GitHubService {
@GET("users/{user}/repos")
fun listRepos(@Path("user") user:String?).:Call<List<Repo>>
}
val retrofit = Retrofit.Builder()
.baseUrl("https://api.github.com/")
.addConverterFactory(GsonConverterFactory.create()) // GSON's conversion plant
.build()
retrofit.create(GitHubService::class.java).listRepos("octocat")
.enqueue(object :Callback<List<Repo>>{
override fun onResponse(call: Call<List<Repo>>, response: Response<List<Repo> >){ println(response.body()? .size) }override fun onFailure(call: Call<List<Repo>>, t: Throwable) {
TODO("Not yet implemented")}})Copy the code
Look at the source code, focus on a line, until you understand the line.
So let’s focus oncreate
Method, this is route A, and then this isenqueue
This is route B.
Analyze from the Create method
The following code builds a Retrofit instance using the Builder pattern
val retrofit = Retrofit.Builder()
.baseUrl("https://api.github.com/")
.addConverterFactory(GsonConverterFactory.create()) // GSON's conversion plant
.build()
Copy the code
The retrofit.create() method is initialized with the following code: Using Java’s dynamic proxy method, to pass the Class proxy method, note that the Class must be an interface. Dynamic proxies in Java can only proxy interfaces. Let’s first look at the first step: what does the validateServiceInterface method do?
public <T> T create(final Class<T> service) {
//1. Initialize the service interface
validateServiceInterface(service);
//2. Return the proxy object
return (T)
// Use the dynamic proxy provided by Java
Proxy.newProxyInstance(
service.getClassLoader(),
newClass<? >[] {service},new InvocationHandler() {
private final Platform platform = Platform.get();
private final Object[] emptyArgs = new Object[0];
@Override
public @Nullable Object invoke(Object proxy, Method method, @Nullable Object[] args)
throws Throwable {
// If the method is a method from Object then defer to normal invocation.
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args); } args = args ! =null ? args : emptyArgs;
returnplatform.isDefaultMethod(method) ? platform.invokeDefaultMethod(method, service, proxy, args) : loadServiceMethod(method).invoke(args); }}); }Copy the code
ValidateServiceInterface validateServiceInterface validateServiceInterface validateServiceInterface validateServiceInterface validateServiceInterface validateServiceInterface
private void validateServiceInterface(Class
service) {
// The class must be an interface. If not, an error will be reported
if(! service.isInterface()) {throw new IllegalArgumentException("API declarations must be interfaces.");
}
// Add the interface to the queueDeque<Class<? >> check =new ArrayDeque<>(1);
check.add(service);
while(! check.isEmpty()) {// Fetch the interface class from the queueClass<? > candidate = check.removeFirst();GithubService
= GithubService
if(candidate.getTypeParameters().length ! =0) {
StringBuilder message =
new StringBuilder("Type parameters are unsupported on ").append(candidate.getName());
if(candidate ! = service) { message.append(" which is an interface of ").append(service.getName());
}
throw new IllegalArgumentException(message.toString());
}
// Check whether the interface has inheritance
Collections.addAll(check, candidate.getInterfaces());
}
// Switch, validate15, development and formal can be quickly verified
if (validateEagerly) {
Platform platform = Platform.get();
for (Method method : service.getDeclaredMethods()) {
// Filter the default and static methods of the interface
if(! platform.isDefaultMethod(method) && ! Modifier.isStatic(method.getModifiers())) {// Validates and loads the methods of the interface classloadServiceMethod(method); }}}}Copy the code
In fact, validatezh is mainly used to verify all method configurations in the interface during initialization, and is usually set to true during the development phase. Retrofit provides the following interpretation:
/**
* When calling {@link #create} on the resulting {@linkRetrofit} instance, eagerly validate the * configuration of all methods in the supplied interface. When in the result {@linkCall {on Retrofit} instance@link#create} is eagerly verifying the configuration of all methods in the provided interface. * /
public Builder validateEagerly(boolean validateEagerly) {
this.validateEagerly = validateEagerly;
return this;
}
Copy the code
After the initialization validation is passed, the proxy object is generated using the dynamic proxy pattern in Java.
A dynamic proxy
The invoke method is invoked when a method on the Proxy object is invoked. This is also the entry point at the heart of Retrofit.
return (T)
// Use the dynamic proxy provided by JavaProxy.newProxyInstance( service.getClassLoader(), new Class<? >[] {service}, new InvocationHandler() {private final Platform platform = Platform.get(a);private final Object[] emptyArgs = new Object[0];
@Override
public @Nullable Object invoke(Object proxy, Method method, @Nullable Object[] args)
throws Throwable {
// If the method is a method from Object then defer to normal invocation.
// The method of the Object class
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args); } args = args ! =null ? args : emptyArgs;
return platform.isDefaultMethod(method) // consider java8 compatibility issues
? platform.invokeDefaultMethod(method, service, proxy, args) // Call the default method directly if it is the default method: loadServiceMethod(method).invoke(args); }});Copy the code
The principle of dynamic proxy is very simple and is actually equivalent to the following code: focus on the Invoke callback method
class ProxyGitHubService : GitHubService {
val invocationHandler:InvocationHandler = InvocationHandler(object : (Any, Method, Array<Any>) -> Any{
override fun invoke(p1: Any, p2: Method, p3: Array<Any>): Any {
// 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;
returnplatform.isDefaultMethod(method) ? platform.invokeDefaultMethod(method, service, proxy, args) : loadServiceMethod(method).invoke(args); }})override fun listRepos(user: String?).: Call<List<Repo>> {
val method = GitHubService::class.java.getDeclaredMethod("listRepos",String::class.java)
invocationHandler.invoke(this,method,user)
}
}
Copy the code
The final core method is the loadServiceMethod method, which eventually calls the Invoke () method. Now what does loadServiceMethod do?
loadServiceMethod
The code of loadServiceMethod is as follows: Obtains ServiceMethod from the Map set and returns it. Retrifit does the cache processing. The next call directly obtains it from the cache. So what does this ServiceMethod class do?
ServiceMethod<? > loadServiceMethod(Method method) {// Retrieve the cache. ServiceMethodCache is a Map setServiceMethod<? > result = serviceMethodCache.get(method);
// Return it if it exists
if(result ! =null) return result;
synchronized (serviceMethodCache) {
result = serviceMethodCache.get(method);
// If not, it is stored in the cache
if (result == null) {
// Handle the interface method
result = ServiceMethod.parseAnnotations(this, method); serviceMethodCache.put(method, result); }}return result;
}
Copy the code
Let’s take a look at the ServiceMethod class, an abstract class that is not implemented by the Invoke method. We can see that we have returned an instance object from the ServiceMethod class using parseAnnotations. We found the HttpServiceMethod class that implements ServiceMethod. Let’s focus on the invoke method,
abstract class ServiceMethod<T> {
static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
// Parse the annotation of the method, return the type of value, and pass the parameters
RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);
// Get the return value type of the 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);
}
abstract @Nullable T invoke(Object[] args);
}
Copy the code
HttpServiceMethod
Let’s first look at the implementation of the Invoke method:
@Override
final @Nullable ReturnT invoke(Object[] args) {
Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
return adapt(call, args);
}
Copy the code
From the invoke method, you can see that there are two lines:OkHttpCall
As well asadapt
Through the source line analysis diagram as follows, the two lines we first look at OkHttpCall this line C.
Line C -> OkHttpCall
As you can see, OkHttpCall implements the Call interface. If the Call interface is familiar, in our use of Retrofit, the interface’s methods will return a Call
interface GitHubService {
@GET("users/{user}/repos")
fun listRepos(@Path("user") user:String?).:Call<List<Repo>>
}
Copy the code
That said, OkHttpCall implements the enqueue() method. The code is as follows:
@Override
public void enqueue(final Callback<T> callback) {
.........
// Call instance of okhttp
okhttp3.Call call;
Throwable failure;
synchronized (this) {... executed =true;
call = rawCall;
failure = creationFailure;
if (call == null && failure == null) {
try {
call = rawCall = createRawCall();
} catch(Throwable t) { throwIfFatal(t); failure = creationFailure = t; }}}...// The enqueue method of okHTTP is finally called
call.enqueue(
new okhttp3.Callback() {
@Override
public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
Response<T> response;
try {
// Parse the returned result
response = parseResponse(rawResponse);
} catch (Throwable e) {
throwIfFatal(e);
callFailure(e);
return; }... }@Override
public void onFailure(okhttp3.Call call, IOException e) {
callFailure(e);
}
privatevoid callFailure(Throwable e) { ....... }}); }Copy the code
There are two new lines in enqueue:createRawCall()
Method (creates the call object for OKHTTP) andparseResponse()
Method (parse the return value).
createRawCall
First analyze how line E creates an object instance of okhttp.call. The following code is primarily created using the callFactory and requestFactory and is passed in when the OkHttpCall instance is created.
private okhttp3.Call createRawCall() throws IOException {
okhttp3.Call call = callFactory.newCall(requestFactory.create(args));
if (call == null) {
throw new NullPointerException("Call.Factory returned null.");
}
return call;
}
OkHttpCall(
RequestFactory requestFactory,
Object[] args,
okhttp3.Call.Factory callFactory,
Converter<ResponseBody, T> responseConverter) {
this.requestFactory = requestFactory;
this.args = args;
this.callFactory = callFactory;
this.responseConverter = responseConverter;
}
Copy the code
We look up the link. At this time, the circuit diagram is as follows:Lookup process: HttpServiceMethod invoke – > HttpServiceMethod. ParseAnnotations () code is as follows: callFactory and requestFactory consist of external passed, continue to find up
static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
Retrofit retrofit, Method method, RequestFactory requestFactory) {
......
okhttp3.Call.Factory callFactory = retrofit.callFactory;
if(! isKotlinSuspendFunction) {returnnew CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter); }... }Copy the code
Here’s the code: We’ve seen the RequestFactory, finally found it, let’s go in and see what it’s done.
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.");
}
returnHttpServiceMethod.parseAnnotations(retrofit, method, requestFactory); }}Copy the code
RequestFactory: You can see that the RequestFactory mainly handles annotations to the methods of the interface class Service.class
RequestFactory build() {
for (Annotation annotation : methodAnnotations) {
parseMethodAnnotation(annotation); }... }Copy the code
When the processing is complete, the following values are obtainedWe call back to line EIs called in the createRawCall() methodrequestFactory.create()
Methods.
private okhttp3.Call createRawCall() throws IOException {
okhttp3.Call call = callFactory.newCall(requestFactory.create(args));
if (call == null) {
throw new NullPointerException("Call.Factory returned null.");
}
return call;
}
Copy the code
The requestFactory.create() code looks like this: the end result is okHttp3.request
okhttp3.Request create(Object[] args) throws IOException {
@SuppressWarnings("unchecked") // It is an error to invoke a method with the wrong arg types.ParameterHandler<Object>[] handlers = (ParameterHandler<Object>[]) parameterHandlers; int argumentCount = args.length; . RequestBuilder requestBuilder = new RequestBuilder( httpMethod, baseUrl, relativeUrl, headers, contentType, hasBody, isFormEncoded, isMultipart);if (isKotlinSuspendFunction) {
// 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]);
}
return requestBuilder.get().tag(Invocation.class, new Invocation(method, argumentList)).build();
}
Copy the code
Line E is okHttpClient.newCall (Request). This is the SAME API that okHTTP uses.OK, requestFactory is a requestFactory that handles the annotations in the method of the service class interface, determines get, POST, etc., and then returns okHttp3.request.
CallFactory (retrofit.callFactory); build (okHttpClient)
retrofit.callFactory
public Retrofit build() {
if (baseUrl == null) {
throw new IllegalStateException("Base URL required.");
}
okhttp3.Call.Factory callFactory = this.callFactory;
if (callFactory == null) { callFactory = new OkHttpClient(); }... }Copy the code
Let’s take a look at the wiring: this will make the process very clear
parseResponse
Let’s see how parseResponse line F is executed. The code is as follows (in the OkHttpCall class) : this is finally handled by the responseConverter convert method
Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
ResponseBody rawBody = rawResponse.body();
/ /...
ExceptionCatchingResponseBody catchingBody = new ExceptionCatchingResponseBody(rawBody);
try {
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();
throwe; }}//responseConverter is also created in the OkHttpCall constructor
OkHttpCall(
RequestFactory requestFactory,
Object[] args,
okhttp3.Call.Factory callFactory,
Converter<ResponseBody, T> responseConverter) {
this.requestFactory = requestFactory;
this.args = args;
this.callFactory = callFactory;
this.responseConverter = responseConverter;
}
Copy the code
So we have to look up: We have returned the responseConverter in the parseAnnotations method of HttpServiceMethod
static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
Retrofit retrofit, Method method, RequestFactory requestFactory) {
.........
Converter<ResponseBody, ResponseT> responseConverter =
createResponseConverter(retrofit, method, responseType);
okhttp3.Call.Factory callFactory = retrofit.callFactory;
if(! isKotlinSuspendFunction) {returnnew CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter); }...Copy the code
Let’s take a look at createResponseConverter method, called the retrofit. ResponseBodyConverter
private static <ResponseT> Converter<ResponseBody, ResponseT> createResponseConverter(
Retrofit retrofit, Method method, Type responseType) {
Annotation[] annotations = method.getAnnotations();
try {
return retrofit.responseBodyConverter(responseType, annotations);
} catch (RuntimeException e) { // Wide exception range because factories are user code.
throw methodError(method, e, "Unable to create converter for %s", responseType); }}Copy the code
The code looks like this: we’re actually going to iterate over the converterFactories collection, which stores the Converters, and we’re going to find out where we added the Converters from
public <T> Converter<ResponseBody, T> responseBodyConverter(Type type, Annotation[] annotations) {
return nextResponseBodyConverter(null, type, annotations); }...public <T> Converter<ResponseBody, T> nextResponseBodyConverter(
@Nullable Converter.Factory skipPast, Type type, Annotation[] annotations) {
.....
int start = converterFactories.indexOf(skipPast) + 1;
for(int i = start, count = converterFactories.size(); i < count; i++) { Converter<ResponseBody, ? > converter = converterFactories.get(i).responseBodyConverter(type, annotations, this);
if(converter ! =null) {
//noinspection unchecked
return(Converter<ResponseBody, T>) converter; }}}Copy the code
Found in the Builder, there is a method to set addConverterFactory
/** Add converter factory for serialization and deserialization of objects. */
public Builder addConverterFactory(Converter.Factory factory) {
converterFactories.add(Objects.requireNonNull(factory, "factory == null"));
return this;
}
Copy the code
I don’t know if you remember when we started with Retorfit, you see it’s addConverterFactory. We added GsonConverterFactory, a Javabean class that uses Gson to parse json data into Settings.
val retrofit = Retrofit.Builder()
.baseUrl("https://api.github.com/")
.addConverterFactory(GsonConverterFactory.create()) // GSON's conversion plant
.build()
Copy the code
OK, so the whole process of OkHttpCall thought is combed clear. Let’s look at the flow chart again. There are a lot of design patterns and good code to learn from Retrofit’s source code
Line D -> ADAPT
Let’s go back to the Invoke method of HttpServiceMethod to see the concrete implementation of ADAPT.
@Override
final @Nullable ReturnT invoke(Object[] args) {
Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
return adapt(call, args);
}
Copy the code
Adapt is an abstract method of HttpServiceMethod
protected abstract @Nullable ReturnT adapt(Call<ResponseT> call, Object[] args);
Copy the code
So how does it work? Let’s go back to parseAnnotations, which returns an instance of the HttpServerMethod, so adapt must be implemented.
if(! isKotlinSuspendFunction) {// Do we use kotlin's suspend tag to determine whether or not it is used
returnnew CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter); }...Copy the code
Let’s look at the implementation of CallAppstore, en….. Oddly enough, the callAdapter is passed in from the outside, and the callAdapter passing in from the outside calls the adapt method
static final class CallAdapted<ResponseT, ReturnT> extends HttpServiceMethod<ResponseT, ReturnT> {
private final CallAdapter<ResponseT, ReturnT> callAdapter;
CallAdapted(
RequestFactory requestFactory,
okhttp3.Call.Factory callFactory,
Converter<ResponseBody, ResponseT> responseConverter,
CallAdapter<ResponseT, ReturnT> callAdapter) {
super(requestFactory, callFactory, responseConverter);
this.callAdapter = callAdapter;
}
@Override
protected ReturnT adapt(Call<ResponseT> call, Object[] args) {
returncallAdapter.adapt(call); }}Copy the code
We need to go back to parseAnnotations and see this code:
. CallAdapter<ResponseT, ReturnT> callAdapter = createCallAdapter(retrofit, method, adapterType, annotations); .if(! isKotlinSuspendFunction) {returnnew CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter); }...Copy the code
The createCallAdapter method was called:…. The callAdapter returned by this method is returned by the callAdapter of retrofit.
private static <ResponseT, ReturnT> CallAdapter<ResponseT, ReturnT> createCallAdapter(
Retrofit retrofit, Method method, Type returnType, Annotation[] annotations) {
try {
//noinspection unchecked
return (CallAdapter<ResponseT, ReturnT>) retrofit.callAdapter(returnType, annotations);
} catch (RuntimeException e) { // Wide exception range because factories are user code.
throw methodError(method, e, "Unable to create call adapter for %s", returnType); }}Copy the code
Continuing back to Retrofit, the code looks like this: In fact, similar to the responseBodyConverter, which is user-definable, we can see that in the nextCallAdapter as long as the traversal adapter is not null it will return to terminate the traversal, So Retrofit can only have one CallAdapter, and Retrofit must have a default CallAdapter because we didn’t set it when we used it
publicCallAdapter<? ,? > callAdapter(Type returnType, Annotation[] annotations) {return nextCallAdapter(null, returnType, annotations);
}
publicCallAdapter<? ,? > nextCallAdapter(@Nullable CallAdapter.Factory skipPast, Type returnType, Annotation[] annotations) {
.....
int start = callAdapterFactories.indexOf(skipPast) + 1;
for (int i = start, count = callAdapterFactories.size(); i < count; i++) {
/ / get CallAdapterCallAdapter<? ,? > adapter = callAdapterFactories.get(i).get(returnType, annotations, this);
if(adapter ! =null) {
returnadapter; }}... }Copy the code
Let’s go to the retrofit.Builder.build () method and look at it as follows:
public Retrofit build() {
.......
Executor callbackExecutor = this.callbackExecutor;
if (callbackExecutor == null) {
// Get the thread pool. // Get the thread pool
callbackExecutor = platform.defaultCallbackExecutor();
}
List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories); callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor)); . }Copy the code
We entered the defaultCallAdapterFactories Platform
// Obtain the platform based on the JVM name
private static Platform findPlatform() {
return "Dalvik".equals(System.getProperty("java.vm.name"))? new Android()//
: new Platform(true);
}
List<? extends CallAdapter.Factory> defaultCallAdapterFactories(
@Nullable Executor callbackExecutor) {
DefaultCallAdapterFactory executorFactory = new DefaultCallAdapterFactory(callbackExecutor);
return hasJava8Types
? asList(CompletableFutureCallAdapterFactory.INSTANCE, executorFactory)
: singletonList(executorFactory);
}
DefaultCallbackExecutor's thread pool executes in the main thread
static final class Android extends Platform {
Android() {
super(Build.VERSION.SDK_INT >= 24);
}
@Override
public Executor defaultCallbackExecutor() {
return new MainThreadExecutor();
}
@Nullable
@OverrideObject invokeDefaultMethod( Method method, Class<? > declaringClass, Objectobject, Object... args) throws Throwable {
if (Build.VERSION.SDK_INT < 26) {
throw new UnsupportedOperationException(
"Calling default methods on API 24 and 25 is not supported");
}
return super.invokeDefaultMethod(method, declaringClass, object, args);
}
static final class MainThreadExecutor implements Executor {
// Handler in the main thread
private final Handler handler = new Handler(Looper.getMainLooper());
@Override
publicvoid execute(Runnable r) { handler.post(r); }}}Copy the code
Look again at, DefaultCallAdapterFactory, how to implement CallAdapter.
final class DefaultCallAdapterFactory extends CallAdapter.Factory {
private final @Nullable Executor callbackExecutor;
DefaultCallAdapterFactory(@Nullable Executor callbackExecutor) {
this.callbackExecutor = callbackExecutor;
}
@Override
public @NullableCallAdapter<? ,? >get(
Type returnType, Annotation[] annotations, Retrofit retrofit) {
// Check that the service interface method returns a Call. If not, return null and continue the loop
if(getRawType(returnType) ! = Call.class) {
return null; }...// Return the CallAdapter instance
returnnew CallAdapter<Object, Call<? > > () {@Override
public Type responseType() {
return responseType;
}
@Override
public Call<Object> adapt(Call<Object> call) {
return executor == null? call : new ExecutorCallbackCall<>(executor, call); }}; }// Wrap the Call
static final class ExecutorCallbackCall<T> implements Call<T> {
final Executor callbackExecutor;
final Call<T> delegate;
// Pass a thread pool to delegateCall
ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) {
this.callbackExecutor = callbackExecutor;
this.delegate = delegate;
}
@Override
public void enqueue(final Callback<T> callback) {
.....
delegate.enqueue(
new Callback<T>() {
@Override
public void onResponse(Call<T> call, final Response<T> response) {
// Make a thread switch to execute the callback in the main thread
callbackExecutor.execute(
() -> {
if (delegate.isCanceled()) {
// Emulate OkHttp's behavior of throwing/delivering an IOException on
// cancellation.
callback.onFailure(ExecutorCallbackCall.this, new IOException("Canceled"));
} else {
callback.onResponse(ExecutorCallbackCall.this, response); }}); }... }); }}Copy the code
The default CallAdapter actually does one thing, which is to switch threads and make a callback in the main thread. So how do you customize the CallAdapter? We know that the default Retrofit interface method returns a value of Call, so if we want to use RxJava, how to adapt?
@GET("users/{user}/repos")
fun listReposRx(@Path("user") user:String?).:Single<List<Repo>>
val retrofit = Retrofit.Builder()
.baseUrl("https://api.github.com/")
.addConverterFactory(GsonConverterFactory.create()) // GSON's conversion plant
.addCallAdapterFactory(RxJava3CallAdapterFactory.create())// Add RxJava support
.build()
retrofit.create(GitHubService::class.java).listReposRx("octocat")
.subscribe()
Copy the code
So how does the RxJavaCallAdapter match the RxJava return type?
@Override
public @NullableCallAdapter<? ,? >get( Type returnType, Annotation[] annotations, Retrofit retrofit) { Class<? > rawType = getRawType(returnType);// It matches the return types it has
if (rawType == Completable.class) {
// Completable is not parameterized (which is what the rest of this method deals with) so it
// can only be created with a single configuration.
return new RxJava3CallAdapter(
Void.class, scheduler, isAsync, false.true.false.false.false.true);
}
boolean isFlowable = rawType == Flowable.class;
boolean isSingle = rawType == Single.class;// The example above uses Single
boolean isMaybe = rawType == Maybe.class;
if(rawType ! = Observable.class&&! isFlowable && ! isSingle && ! isMaybe) {return null; }... }Copy the code
OK, so the complete process of Retrofit is as follows, through the wiring way to clear the source code.