Dig into Retrofit:2.8.0 source code. Describes how Retrofit achieves encapsulation of OkHttp and how Retrofit supports coroutines.

1. Builder

Retrofit is created through Retrofit.Builder and is mainly used to configure various Factory factories.

val retrofit = Retrofit.Builder()
    .baseUrl("this is baseUrl")
    .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
    .addConverterFactory(GsonConverterFactory.create())
    .build()
Copy the code

Builder.builder() has five main steps:

  1. To obtainPlatformObject,PlatformIs a platform adapter, mainly for cross-platform use, such as Android platform implementation isAndroid.
  2. Initialize theCallFactroy.CallFactroyThe role of is productionrealCallThe network request is made by him. The default value isOkHttptheOKHttpClient.
  3. Initialize theExecutor, pass by defaultPlatformTo obtain.
  4. Initialize theCallAdapter.Factroy.CallAdapter.FactroyproductionCallAdapter.CallAdapterisrealCallThe adapter through the pairrealCallThe packaging of the realizationExecutor.java8theFutrue.rxjavaAnd other scheduling methods. There are multiple configurations. The order is custom configuration > Default configuration. Default configuration passedPlatformTo obtain.
  5. Initialize theConverter.Factory.Converter.FactoryproductionConverter.ConverterIt’s a data converter, which converts the returned data into the desired data, usually into the object we want to use. There are multiple configurations, in the order of built-in configuration > Custom configuration > Default configuration. Default configuration passedPlatformTo obtain.
class Builder{

    Builder(Platform platform) {
        this.platform = platform;
    }

    public Builder(a) {
        //step 0
        this(Platform.get());
    }

    public Retrofit build(a) {
        if (baseUrl == null) {
            throw new IllegalStateException("Base URL required.");
        }

        // step1
        okhttp3.Call.Factory callFactory = this.callFactory;
        if (callFactory == null) {
            callFactory = new OkHttpClient();
        }

        //step2
        Executor callbackExecutor = this.callbackExecutor;
        if (callbackExecutor == null) {
            callbackExecutor = platform.defaultCallbackExecutor();
        }

        //step3
        List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
        callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor));

        //step4
        List<Converter.Factory> converterFactories = new ArrayList<>(
                1 + this.converterFactories.size() + platform.defaultConverterFactoriesSize());
        converterFactories.add(new BuiltInConverters());
        converterFactories.addAll(this.converterFactories);
        converterFactories.addAll(platform.defaultConverterFactories());

        return newRetrofit(callFactory, baseUrl, unmodifiableList(converterFactories), unmodifiableList(callAdapterFactories), callbackExecutor, validateEagerly); }}Copy the code

At this point, the Retrofit object has been created, and a rough structure looks like this:

2. Create an API object

Retrofit sets the request parameters and return types by defining the interface for the network request, creates the object for this interface by calling retrofit.create(), and calls the methods of this object to generate the final network request.

interface MyService {
    @GET("/user")
    fun getUser(a): Observable<Response<User>>
}

val myServiceClass: MyService = retrofit.create(MyService::class.java)
Copy the code

Entering the create method, you can see that the object is created by calling proxy.newProxyInstance () directly. This is the dynamic proxy mechanism provided by the standard library that creates instance objects of the interface at run time.

The proxy.newProxyInstance () method takes three arguments:

  • ClassLoader: a classLoader

  • Interfaces: The interfaces to be implemented

  • InvocationHandler: Proxy method

public <T> T create(final Class<T> service) {
    return(T) Proxy.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 (method.getDeclaringClass() == Object.class) {
                        return method.invoke(this, args);
                    }
                    if (platform.isDefaultMethod(method)) {
                        return platform.invokeDefaultMethod(method, service, proxy, args);
                    }
                    returnloadServiceMethod(method).invoke(args ! =null? args : emptyArgs); }}); }Copy the code

To better understand dynamic proxies, we can use dynamic proxies to automatically generate an interface implementation class. Pass all methods through the handler.invoke() proxy, as shown in the code below. But the dynamic proxy is the run-time generation of this class and directly generates the bytecode.

interface MyService {
    @GET("/user")
    fun getUser(a): Observable<Response<User>>

    @GET("/name")
    fun getName(userId: String): Observable<Response<String>>
}

// Examples of automatically generated code
class SuspendServiceProxy implements SuspendService {
    InvocationHandler handler;

    @NonNull
    @Override
    public Observable<Response<User>> getUser() {
        return handler.invoke(
                this,
                SuspendService.class.getMethod("getUser", String.class),
                new Object[]{}
        );
    }

    @NonNull
    @Override
    public Observable<Response<String>> getName(@NonNull String userId) {
        return handler.invoke(
                this,
                SuspendService.class.getMethod("getName", String.class),
                newObject[]{userId} ); }}Copy the code

3. Create the request object

Once the API object is created, its methods can be called to create the request object.

val observable = myServiceClass.getUser()
Copy the code

As you can see, this method is delegated to the InvocationHandler, which first checks whether the method object is an entity object, and then calls it directly.

If it is not an object, the call is proxyed to the ServiceMethod. The ServiceMethod object is created by calling loadServiceMethod(), and the invoke() method returns the value.

public @Nullable Object invoke(Object proxy, Method method, @Nullable Object[] args) throws Throwable {
    // Determine if this method object is an entity object, and if so, call it directly
    if (method.getDeclaringClass() == Object.class) {
        return method.invoke(this, args);
    }
    // Determine if this method object is an entity object, and if so, call it directly
    if (platform.isDefaultMethod(method)) {
        return platform.invokeDefaultMethod(method, service, proxy, args);
    }
    returnloadServiceMethod(method).invoke(args ! =null ? args : emptyArgs);
}
Copy the code

3.1. Create a ServiceMethod object

ServiceMethod is a custom proxy method within Retrofit that handles the actual logic.

abstract class ServiceMethod<T> {
    abstract @Nullable T invoke(Object[] args);
}
Copy the code

The loadServiceMethod() method gets the ServiceMethod object.

The Method object serves as the Key cache for the ServiceMethod object. In Retrofit, creating a ServiceMethod is a time-consuming process, so the singleton pattern is used.

If not cache, call ServiceMethod. ParseAnnotations () create a ServiceMethod object.

private finalMap<Method, ServiceMethod<? >> serviceMethodCache =newConcurrentHashMap<>(); 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;
}
Copy the code

ServiceMethod. ParseAnnotations () only do one thing, create RequestFactory will create ServiceMethod work then to subclass HttpServiceMethod processing.

RequestFactory is the parameter configuration of the interface, which is obtained by parsing the interface’s annotations, return values, input parameters, and annotations.

The parsed data is then passed to HttpServiceMethed, which continues to create the ServiceMethod.

abstract class ServiceMethod<T> {
    static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) { RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method); .return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
    }

    abstract @Nullable T invoke(Object[] args);
}
Copy the code

3.1.1. Create RequstFactory

There is also a Builder pattern inside the RequestFactory. Two main things were done:

  1. Iterate through the interface annotations and initialize the configuration parameters, which are read herePOST,GETSuch comments.
  2. Iterates through the input parameter and its annotations to convert the input parameter toParameterHandlerObject that gives it the processing of the logical proxy configured for each parameter setting.
final class RequestFactory {
    static RequestFactory parseAnnotations(Retrofit retrofit, Method method) {
        return new RequestFactory.Builder(retrofit, method).build();
    }

    static final class Builder {
        Builder(Retrofit retrofit, Method method) {
            this.retrofit = retrofit;
            this.method = method;
            this.methodAnnotations = method.getAnnotations();
            this.parameterTypes = method.getGenericParameterTypes();
            this.parameterAnnotationsArray = method.getParameterAnnotations();
        }

        RequestFactory build(a) {
            // Comment parsing
            for(Annotation annotation : methodAnnotations) { parseMethodAnnotation(annotation); }...// Parameter parsing
            int parameterCount = parameterAnnotationsArray.length;
            parameterHandlers = newParameterHandler<? >[parameterCount];for (int p = 0, lastParameter = parameterCount - 1; p < parameterCount; p++) {
                parameterHandlers[p] =
                        parseParameter(p, parameterTypes[p], parameterAnnotationsArray[p], p == lastParameter);
            }

            return new RequestFactory(this); }}}Copy the code

3.1.2. HttpServiceMethod

Mentioned ServiceMethod. ParseAnnotations () by RequestFactory parsing the annotation of the interface, the return value, the parameter and its annotations to obtain these parameters, such as data is passed to the parsing is complete HttpServiceMethed, It continues to create the ServiceMethod.

HttpServiceMethod. ParseAnnotations () in the three main steps:

  • Create the appropriate CallAdapter

  • Create the appropriate Converter

  • Returns from creating an httpServiceMethod subclass called CallStuck

The main logic of the code here is partially omitted, leaving only what is currently concerned and will be covered later.

static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations( Retrofit retrofit, Method method, RequestFactory requestFactory) {.../** * Create CallAdapter */
    Annotation[] annotations = method.getAnnotations();
    Type adapterType = method.getGenericReturnType();
    CallAdapter<ResponseT, ReturnT> callAdapter =
            createCallAdapter(retrofit, method, adapterType, annotations);

    /** * create Converter */
    Type responseType = callAdapter.responseType();
    Converter<ResponseBody, ResponseT> responseConverter =
            createResponseConverter(retrofit, method, responseType);

    /** * To create a subclass of HttpServiceMethod, call Best-seller returns */
    okhttp3.Call.Factory callFactory = retrofit.callFactory;
    return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);
}
Copy the code

3.1.2.1. invoke

Now, focus on how invoke() is called to get the return value after ServieceMethod is created.

The invoke() method is implemented in HttpServieceMethod where an instance of Call, OkHttpCall, is created. After creation, the adapt() method implemented by subclass CallCaller is called, which is handled by the CallAdapter to convert the Call to the desired return type.

As you can see, the CallAdapter is a wrapper around the actual Call-OK HttpCall. Different CallAdapters can be implemented to implement different functions, such as RxJava extensions, and RxJava logic does not intrude on OkHttpCall code.

abstract class HttpServiceMethod<ResponseT.ReturnT> extends ServiceMethod<ReturnT> {

    HttpServiceMethod(RequestFactory requestFactory, okhttp3.Call.Factory callFactory,
                      Converter<ResponseBody, ResponseT> responseConverter) {
        this.requestFactory = requestFactory;
        this.callFactory = callFactory;
        this.responseConverter = responseConverter;
    }
    
    @Override final @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 retrofit2.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

3.1.2.2. OkHttpCall

The final web request object created is the OkHttpCall.

OkHttpCall implements the Retrofit.Call interface, which is basically the same as okhttp. Call. Here are just three of its methods:

  1. execute()Synchronously initiates the request and returns the request bodyResponse.
  2. enqueue()Asynchronously initiates a request and passesCallbackCommunication, it is important to note that the callback handled is also called in asynchrony.
  3. cancel()Cancel the request.

Looking at the implementation of OkHttpCall, you can see that all concrete implementations of the Call interface methods are delegated to rawCall.

Cancel () proxies rawCall.cancel() directly.

The execute() agent gives rawCall.execute(), passing the return value to parseResponse() for a conversion.

The enqueue() agent adds an extra layer of Callback to rawCall.enqueue(), which in a successful Callback is also handed over to parseResponse() after the conversion to the original Callback.

That is, OkHttpCall gives rawCall all the logical static proxies. The advantage of this is that you can do some extra processing where appropriate, which is to get the return value and convert the data through parseResponse().

final class OkHttpCall<T> implements Call<T> {...@Override
    public void enqueue(final Callback<T> callback) {
        okhttp3.Call call;

        synchronized (this) {
            call = rawCall = createRawCall();
        }

        call.enqueue(new okhttp3.Callback() {
            @Override
            public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) { Response<T> response; response = parseResponse(rawResponse); . callback.onResponse(retrofit2.OkHttpCall.this, response);
            }

            @Override
            public void onFailure(okhttp3.Call call, IOException e) {
                callFailure(e);
            }

            private void callFailure(Throwable e) {... callback.onFailure(retrofit2.OkHttpCall.this, e); }}); }@Override
    public Response<T> execute(a) throws IOException {
        okhttp3.Call call;

        synchronized (this) {
            call = getRawCall();
        }

        return parseResponse(call.execute());
    }
  
	  @Override	
  	public void cancel(a) {
        okhttp3.Call call;
        synchronized (this) {
            call = rawCall;
        }
        if(call ! =null) { call.cancel(); }}}Copy the code

RawCall comes from the creation of the CallFactory. As mentioned earlier, if not set, the CallFactory defaults to OkHttpClient, which is the factory in OkHttp that creates the RealCall. I won’t go into the code in OkHttp here.

final class OkHttpCall<T> implements Call<T> {...private okhttp3.Call getRawCall(a) throws IOException {
        okhttp3.Call call = rawCall;
        if(call ! =null) return call;

        return rawCall = createRawCall();
    }

    private okhttp3.Call createRawCall(a) throws IOException {
        okhttp3.Call call = callFactory.newCall(requestFactory.create(args));
        returncall; }}Copy the code

ParseResponse () calls Converter.convert () to convert the data returned by the interface. Instead of expanding the converter creation criteria, I’m interested in taking a look at the source code for myself.

Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {... ResponseBody rawBody = rawResponse.body(); rawResponse = rawResponse.newBuilder() .body(new NoContentResponseBody(rawBody.contentType(), rawBody.contentLength()))
            .build();

    ExceptionCatchingResponseBody catchingBody = new ExceptionCatchingResponseBody(rawBody);
    T body = responseConverter.convert(catchingBody);
    return Response.success(body, rawResponse);
}
Copy the code

3.1.2.4 permission levels. CallAdapter

As mentioned earlier, invoke() creates an instance of Call, OkHttpCall. After creation, the adapt() method implemented by the subclass CallSeller is called, and the CallAdapter proxy OkHttpCall converts the original return value Call to the true return value.

abstract class HttpServiceMethod<ResponseT.ReturnT> extends ServiceMethod<ReturnT> {

    HttpServiceMethod(RequestFactory requestFactory, okhttp3.Call.Factory callFactory,
                      Converter<ResponseBody, ResponseT> responseConverter) {
        this.requestFactory = requestFactory;
        this.callFactory = callFactory;
        this.responseConverter = responseConverter;
    }
    
    @Override final @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 retrofit2.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

Back to HttpServiceMethod. ParseAnnotations CallAdapter () to see how to create.

  • HttpServiceMethod. CreateCallAdapter () will return to type annotations and directly to Retrofit. CallAdapter (), chosen by it to create the appropriate callAdapter.

  • Retrofit simply iterates through the list of CallAdapterFactories in order, passing annotations and return types to the specific callAdapterFactory, which determines whether it has been created until it succeeds.

//HttpServiceMethod
static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations( Retrofit retrofit, Method method, RequestFactory requestFactory) {.../** * Create CallAdapter */
    Annotation[] annotations = method.getAnnotations();
    Type adapterType = method.getGenericReturnType();
    CallAdapter<ResponseT, ReturnT> callAdapter =
            createCallAdapter(retrofit, method, adapterType, annotations);
}

//HttpServiceMethod
private static <ResponseT, ReturnT> CallAdapter<ResponseT, ReturnT> createCallAdapter( Retrofit retrofit, Method method, Type returnType, Annotation[] annotations) {...return (CallAdapter<ResponseT, ReturnT>) retrofit.callAdapter(returnType, annotations);
}

//Retrofit
publicCallAdapter<? ,? > callAdapter(Type returnType, Annotation[] annotations) {return nextCallAdapter(null, returnType, annotations);
}

//Retrofit
publicCallAdapter<? ,? > nextCallAdapter(@Nullable CallAdapter.Factory skipPast, Type returnType,
                                         Annotation[] annotations) {
    int start = callAdapterFactories.indexOf(skipPast) + 1; //skipPast==null , start = 0
    for (inti = start, count = callAdapterFactories.size(); i < count; i++) { CallAdapter<? ,? > adapter = callAdapterFactories.get(i).get(returnType, annotations,this);
        if(adapter ! =null) {
          	returnadapter; }}}Copy the code

Back when Retrofit was created, there were multiple CallAdapterFactories in the list of CallAdapterFactories, in the order of custom configurations — the default configurations.

Custom configurations such as RxJava2CallAdapterFactory initialization time.

The default configuration obtains the defalutFactory from the Platform.

Based on the traversal order from front to back, try to create custom configurations in the initialization order first, with the default configuration being the last, as the bottom of the pocket.

So the order is RxJava2CallAdapterFactory – defaultFactory.

Take a look at how defaultFactory is handled by default, and then look at RxJava2.

3.1.2.3.1. DefaultCallAdapterFactory

DefaultCallAdapterFactory. The get () to obtain CallAdapter, only when the return type is the Call to create CallAdapter.

The callAdapter.adapt creation gives the OkHttpCall agent to ExecutorCallbackCall.

final class DefaultCallAdapterFactory extends CallAdapter.Factory {
    Executor callbackExecutor;

    DefaultCallAdapterFactory(@Nullable Executor callbackExecutor) {
        this.callbackExecutor = callbackExecutor;
    }

    @OverrideCallAdapter<? ,? > get(Type returnType, Annotation[] annotations, Retrofit retrofit) {if(getRawType(returnType) ! = Call.class) {return null; }...return newCallAdapter<Object, Call<? > > () {...public Call<Object> adapt(Call<Object> call) {
                return newExecutorCallbackCall<>(callbackExecutor, call); }}; }}Copy the code

ExecutorCallbackCall provides an additional layer of proxies for OkHttpCall, which only deals with callbacks to enqueue() and then dumps them to the callbackExecutor. All other methods call OkHttpCall directly.

As mentioned earlier, enqueue() initiates the request asynchronously, communicates through the Callback, and handles the Callback that is also called asynchronously, doing something that throws the Callback back to the main thread call.

static final class ExecutorCallbackCall<T> implements Call<T> {
    final Executor callbackExecutor;
    final Call<T> delegate;

    ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) {
        this.callbackExecutor = callbackExecutor;
        this.delegate = delegate; }...public void enqueue(final Callback<T> callback) {
        Objects.requireNonNull(callback, "callback == null");

        delegate.enqueue(new Callback<T>() {
            ...
            @Override public void onResponse(Call<T> call, final Response<T> response) {
                callbackExecutor.execute(() -> {
                    callback.onResponse(this, response); }); }}); }public Response<T> execute(a) throws IOException {
        returndelegate.execute(); }}Copy the code

The Android Platform’s default CallBackExecutor in the Platform implementation class Android, Runnable is thrown into the MainHandler to implement the callback to the main thread.

static final class Android extends Platform {

    @Override public Executor defaultCallbackExecutor(a) {
        return MainThreadExecutor();
    }

    static class MainThreadExecutor implements Executor {
        private final Handler handler = new Handler(Looper.getMainLooper());

        @Override public void execute(Runnable r) { handler.post(r); }}}Copy the code
3.1.2.3.2. RxJava2CallAdapterFactory

RxJava2CallAdapterFactory only in return type is a time to create CallAdapter observables.

public final class RxJava2CallAdapterFactory extends CallAdapter.Factory {

    @Override
    public @NullableCallAdapter<? ,? > get( Type returnType, Annotation[] annotations, Retrofit retrofit) { Class<? > rawType = getRawType(returnType);if(rawType ! = Observable.class) {// Other types are omitted
            return null;
        }

        boolean isResult = false;
        boolean isBody = false;
        Type responseType;
        

        Type observableType = getParameterUpperBound(0, (ParameterizedType) returnType); Class<? > rawObservableType = getRawType(observableType);if (rawObservableType == Response.class) {
            responseType = getParameterUpperBound(0, (ParameterizedType) observableType); }...return new RxJava2CallAdapter(...);
    }
}
Copy the code

Adapt () in RxJava2CallAdapter returns Call wrapped as an Observable.

final class RxJava2CallAdapter<R> implements CallAdapter<R.Object> {
    
    @Override public Object adapt(Call<R> call) {
        Observable<Response<R>> observable = newCallExecuteObservable<>(call); .returnRxJavaPlugins.onAssembly(observable); }}Copy the code

Finally, it goes to CallExecuteObservable, which calls call.execute() on startup and throws the result to the observer.

final class CallExecuteObservable<T> extends Observable<Response<T>> {
    private final Call<T> originalCall;

    CallExecuteObservable(Call<T> originalCall) {
        this.originalCall = originalCall;
    }

    @Override protected void subscribeActual(Observer<? super Response<T>> observer) {
        Call<T> call = originalCall.clone();
        try{... Response<T> response = call.execute(); observer.onNext(response); }}}Copy the code

Review 3.2.

  1. ApiInterface instanceA dynamic proxytoHttpServiceMethod.
  2. The real enforcer of the network request isOkHttpClientTo create theRealCall.
  3. ApiInterface call toRealCallThere are two levels directlyStatic agent OkHttpCallandCallAdapter.
  4. OkHttpCallThe agentRealCall, extra callConverterDo serialization.
  5. CallAdapterThe agentOkHttpCall, can be extended here, willCallConvert to the actual return type.

4. Coroutine implementation

Before looking at how Retrofit implements coroutines, let’s review the basic concepts of coroutines.

When there is a delayed task, the subsequent logic needs to wait for the task to complete and return data, so that it can continue to execute. In order not to block the thread, it usually needs to communicate through thread scheduling and transfer callback. Once coroutines are used, they can be done by simply writing them as synchronous code.

But it’s not dark magic. It’s not that we don’t need thread scheduling and delivery callbacks with coroutines. It’s just that these tedious things are encapsulated and generated automatically for us. Encapsulate callbacks as continuations, and the scheduling of threads as schedulers.

Continuation, which calls its resume or resumeWithException to return a result or throw an exception, is exactly what we call a callback.

The essence of a scheduler is a coroutine interceptor that intercepts continuations and schedules callbacks in them. Dispatchers usually use off-the-shelf ones such as dispatchers.main. If you dig into the source code, you’ll find that at the end of the day, you still use handler.post(), which is exactly what we call thread scheduling.

As mentioned earlier, Retrofit implementations often do different things depending on the return type, so it’s important to understand how the coroutine automatically generates the callback code and how the callback is passed. Write a simple coroutine interface, look at the transformed Java code, and try calling the coroutine interface in Java code.

You can see that the return value String is wrapped as a Continuation

that is passed as an input parameter. Consider that this is not how callbacks are implemented.

The actual return value is Object(for state machine state switching).

/ / Kotlin code
interface SuspendService {
    suspend fun C(c1: Long): String
}

// Java code for bytecode conversion
public interface SuspendService {
   @Nullable
   Object C(long var1, @NotNull Continuation var3);
}

// Try calling the suspend method in Java
class MMM {
    SuspendService service;

    public static void main(String[] args) {
        MMM mmm = new MMM();
        mmm.service.C(1L, new Continuation<String>() {
            @NonNull
            @Override
            public CoroutineContext getContext() {
                return null;
            }

            @Override
            public void resumeWith(@NonNullObject o) { } }); }}Copy the code

Back to the dynamic proxy, because the code suspend generates adds a callback argument Continuation, which is passed to the agent’s handler when you dynamically broker.

Continuations are so tedious to create and use that the best way to handle them is to throw them back into kotlin’s suspend method and let the compiler handle them, which is how Retrofit implements coroutines.

interface SuspendService {
    @GET("/user")
    suspend fun getUser(a): Response<User>

    @GET("/name")
    suspend fun getName(userId: String): Response<String>
}

// Dynamic proxy generation bytecode example
class SuspendServiceProxy implements SuspendService {
    InvocationHandler handler;

    @Nullable
    @Override
    public Object getUser(@NonNull Continuation<? super Response<User>> $completion) {
        return handler.invoke(
                this,
                SuspendService.class.getMethod("getUser", Continuation.class),
                new Object[]{$completion}
        );
    }

    @Nullable
    @Override
    public Object getName(@NonNull String userId, @NonNull Continuation<? super Response<String>> $completion) {
        return handler.invoke(
                this,
                SuspendService.class.getMethod("getName", String.class, Continuation.class),
                newObject[]{userId, $completion} ); }}Copy the code

Then go back to HttpServiceMethod and look at the code that was left out.

In RequestFactory, the method to determine whether a parameter is a Continuation object or not is suspend.

Now, if you use the suspend method, you directly customize a type adapterType, whose actual type is Call, and the generic type is the actual return type (T in Response

). Will he as a return type to create CallAdapter, and here is actually creating DefaultCallAdapterFactory ExecutorCallbackCall. SuspendForResponse object is created.

abstract class HttpServiceMethod<ResponseT.ReturnT> extends ServiceMethod<ReturnT> {

    static <ResponseT, ReturnT> retrofit2.HttpServiceMethod<ResponseT, ReturnT> parseAnnotations( Retrofit retrofit, Method method, RequestFactory requestFactory) {

        /** * indicates whether the function suspend * is processed inside the requestFactory by determining whether the argument is a Continuation
        booleanisKotlinSuspendFunction = requestFactory.isKotlinSuspendFunction; . Annotation[] annotations = method.getAnnotations(); Type adapterType;if (isKotlinSuspendFunction) {
            Type[] parameterTypes = method.getGenericParameterTypes();
            Type responseType = Utils.getParameterLowerBound(0,
                    (ParameterizedType) parameterTypes[parameterTypes.length - 1]);
          
          if (getRawType(responseType) == Response.class && responseType instanceof ParameterizedType) {
            // Unwrap the actual body type from Response<T>.
            responseType = Utils.getParameterUpperBound(0, (ParameterizedType) responseType);
            continuationWantsResponse = true;
     		  }
          	/** * create a new return type of your own and wrap the actual return routine around Call */
            adapterType = new Utils.ParameterizedTypeImpl(null, Call.class, responseType);
        } else {
            adapterType = method.getGenericReturnType();
        }

        CallAdapter<ResponseT, ReturnT> callAdapter =
                createCallAdapter(retrofit, method, adapterType, annotations);
        Type responseType = callAdapter.responseType();

        Converter<ResponseBody, ResponseT> responseConverter =
                createResponseConverter(retrofit, method, responseType);

        okhttp3.Call.Factory callFactory = retrofit.callFactory;
        if(! isKotlinSuspendFunction) {return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);
        } else {
            return (HttpServiceMethod<ResponseT, ReturnT>) newSuspendForResponse<>(requestFactory, callFactory, responseConverter, (CallAdapter<ResponseT, Call<ResponseT>>) callAdapter); }}}Copy the code

In SuspendForResponse, the return type of adapt() corresponds to the return type Object of suspend. And the logic is to resolve the Call and Continuation object, and then have Call KotlinExtensions. AwaitResponse (), as said before, it is a method that a suspend in the agent does not handle a Continuation, It’s left to the compiler to handle.

static final class SuspendForResponse<ResponseT> extends HttpServiceMethod<ResponseT.Object> {
    private final CallAdapter<ResponseT, Call<ResponseT>> callAdapter;

    SuspendForResponse(RequestFactory requestFactory, okhttp3.Call.Factory callFactory,
                       Converter<ResponseBody, ResponseT> responseConverter,
                       CallAdapter<ResponseT, Call<ResponseT>> callAdapter) {
        super(requestFactory, callFactory, responseConverter);
        this.callAdapter = callAdapter;
    }

    @Override
    protected Object adapt(Call<ResponseT> call, Object[] args) {
        call = callAdapter.adapt(call);

        Continuation<Response<ResponseT>> continuation =
                (Continuation<Response<ResponseT>>) args[args.length - 1]; .returnKotlinExtensions.awaitResponse(call, continuation); }}Copy the code

KotlinExtensions. AwaitResponse () is the extension function Call, the realization of the extension function is accomplished by means of static method was introduced into this, so in front of the incoming awaitResponse () has two parameters, These are the Call object and the Continuation object.

KotlinExtensions. AwaitResponse () method of subject is suspendCancellableCoroutine SuspendCancellableCoroutine run in and help us to get to the current of coroutines coroutines CancellableContinuation instance, CancellableContinuation is a Continuation of a cancelable. The invokeOnCancellation method can be used to set up a callback to cancel the event. Once the callback is called, it means that the coroutine is cancelled, and the corresponding cancellation response should be made, that is, to cancel the request sent by OkHttp. Read this paragraph several times.

Call.enqueue() is then called to send the network request, and resume of CancellableContinuation or resumeWithException is called in Callback to return the result or throw an exception.

The Callback is processed by the callAdapter and the OkHttpCall.

suspend fun <T> Call<T>.awaitResponse(a): Response<T> {
  return suspendCancellableCoroutine { continuation ->
    continuation.invokeOnCancellation {
      cancel()
    }
    enqueue(object : Callback<T> {
      override fun onResponse(call: Call<T>, response: Response<T>) {
        continuation.resume(response)
      }

      override fun onFailure(call: Call<T>, t: Throwable) {
        continuation.resumeWithException(t)
      }
    })
  }
}

// Extend the function example
fun <T> Call<T>.awaitResponse(a){
    toString()
}

// Extend the function sample to Java code
public static final void awaitResponse(@NotNull Call $this$awaitResponse) {
    $this$awaitResponse.toString();
}
Copy the code

Refer to the article

Android: This is a comprehensive & detailed source analysis guide for Retrofit 2.0

A dynamic proxy

In-depth understanding of how Kotlin coroutines work

One or two things behind the scenes at Kotlin Coroutines

Cracking Kotlin coroutines (3) – Coroutine scheduling

Cracking Kotlin coroutines (2) – Coroutines starter

Cracking Kotlin coroutines (5) – Coroutine Cancellations