Retrofit has been widely touted in the industry and is basically a framework for interview questions. What are its advantages and what design patterns are used? There are many on the Internet, so I won’t say much about it. Here is the simplest way to look at the implementation process of RetorFit from the perspective of the process of using Retrofit.

PS: I am a steel straight man, do not like to use some gorgeous words, more do not like to say some nonsense, seize the core points, to prevent the cloud in the fog, do not know what to do.

A, the use of

Let’s first look at its use:

1. Create an interface
public interface ApiService {
    @GET("users/{user}/repos")
    Call<List<Repo>> listRepos(@Path("user") String user);
}
Copy the code
2, the use of
// 1, create a retrofit
val retrofit = Retrofit.Builder().baseUrl("https://api.github.com/")
    .addConverterFactory(GsonConverterFactory.create())
    .build()
    
// 2, create the proxy object
val service = retrofit.create(ApiService::class.java)

// 3, call the request method
val repos = service.listRepos("octocat")

// 4, initiates the request and retrieves the callback
repos.enqueue(object : Callback<List<Repo>> {
    override fun onResponse(call: Call<List<Repo>>, response: Response<List<Repo> >) {
        LogUtil.e("result: " + response.body())
    }

    override fun onFailure(call: Call<List<Repo>>, t: Throwable) {
        LogUtil.e("onFailure: " + t.toString())
    }
})
Copy the code

Second, analysis

Based on the process used above, analyze the execution of Retrofit in four steps

Create a RetorFit

val retrofit = Retrofit.Builder().baseUrl("https://api.github.com/")
        .addConverterFactory(GsonConverterFactory.create())
// .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
        .build()
Copy the code

To create a RetorFit in Builder mode, just look at its build() to see what it has done:

// The following code is simplified, so you know what it is
public Retrofit build(a) {
	// 1, baseUrl
	if (baseUrl == null) {
		throw new IllegalStateException("Base URL required.");
	}
	// 2, callFactory(i.e. OkHttpClient)
	okhttp3.Call.Factory callFactory = new OkHttpClient();
    
	// 3, Executor(for thread switching)
	Executor callbackExecutor = platform.defaultCallbackExecutor();

	// 4, callAdapterFactories(factories that adapt the requested intermediate process)
	// Make a defensive copy of the adapters and add the default Call adapter.
	List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);

	// 5, result converter factory
	// Make a defensive copy of the converters.
	List<Converter.Factory> converterFactories = newArrayList<>(); .return new Retrofit(callFactory, baseUrl, unmodifiableList(converterFactories),
	  unmodifiableList(callAdapterFactories), callbackExecutor, validateEagerly);
}
Copy the code

Retofit mainly contains these five objects, all of which will be used later.

2. Create a proxy object

val service = retrofit.create(ApiService::class.java)
Copy the code

The core idea of Retofit is dynamic proxies. A proxy object is created by calling Retofit’s create() with the ApiService interface as a parameter. So what does create() do? It looks like this:

// Brief code
public <T> T create(final Class<T> service) {
    // Return the proxy object
    return (T) Proxy.newProxyInstance(service.getClassLoader(), newClass<? >[] { service },new InvocationHandler() {
            ...
        });
}
Copy the code

You can see that Retrofit returns an object that is a proxy to the ApiService.

Note: We have only created an object, invoke() in the InvocationHandler has not yet started executing.

3, call the request method

val repos = service.listRepos("octocat")
Copy the code

By calling the method on the proxy object created in the second step, we obtain an object that can initiate a request. What object is it and how is it obtained?

The invoke() method of the second proxy object’s InvocationHandler is executed only when listRepos(“octocat”) is called:

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 {...returnloadServiceMethod(method).invoke(args ! =null? args : emptyArgs); }});Copy the code

The code in the method at Invoke () is simple, but the process is really the most complex and critical of the whole process. This can be divided into two steps:

3.1, loadServiceMethod (method)

This step obtains a ServiceMethod object. The process is as follows:

3.1.1 Checking the Cache

First check whether there is a cache, if not, create it directly:

ServiceMethod<? > loadServiceMethod(Method method) {// Get the cacheServiceMethod<? > result = serviceMethodCache.get(method);if(result ! =null) return result;

	synchronized (serviceMethodCache) {
		result = serviceMethodCache.get(method);
		if (result == null) {
            // Create it directly
			result = ServiceMethod.parseAnnotations(this, method); serviceMethodCache.put(method, result); }}return result;
}
Copy the code
3.1.2 Direct Creation
// Here the source code is simplified with appropriate adjustments
static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations( Retrofit retrofit, Method method) {
	// 1, gets the RequestFactory object, which encapsulates the network request parameters and can be converted into OkHttp request parameters
	RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);
	
	Annotation[] annotations = method.getAnnotations();
	Type adapterType = method.getGenericReturnType();
	// 2, callAdapter, ADAPTS the request intermediate processCallAdapter<ResponseT, ReturnT> callAdapter = createCallAdapter(retrofit, method, adapterType, annotations); Type responseType = callAdapter.responseType(); .// 3, the request returns the result to the format conversion
	Converter<ResponseBody, ResponseT> responseConverter =
		createResponseConverter(retrofit, method, responseType);
	// 4, callFactory is the same as OkHttpClient
	okhttp3.Call.Factory callFactory = retrofit.callFactory;
	// 5, create CallAdapted
	return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);
}
Copy the code

The requestFactory, callAdapter, responseConverter, and callFactory are all important objects. We then create a CallAdapted object from these four objects:

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

This CallAdapted object is the result of loadServiceMethod(method), since it inherits from HttpServiceMethod and indirectly from ServiceMethod.

3.2, invoke (args! = null ? args : emptyArgs)

Let’s take a look at the ServiceMethod invoke() :

@Override 
final @Nullable ReturnT invoke(Object[] args) {
    Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
    return adapt(call, args);
}
Copy the code

Adapt () ¶ Adapt (); adapt(); adapt(); adapt(); adapt();

static final class CallAdapted<ResponseT.ReturnT> extends HttpServiceMethod<ResponseT.ReturnT> {
	private final CallAdapter<ResponseT, ReturnT> callAdapter;
	// Execute this method
	@Override protected ReturnT adapt(Call<ResponseT> call, Object[] args) {
		returncallAdapter.adapt(call); }}Copy the code

Adapt () returns the result of calladapter.adapt (call), so what is this callAdapter? It was created in section 3.1.2 above, and without further circumstance. it is a CallAdapter object that is directly new.

/ / DefaultCallAdapterFactory. In Java
newCallAdapter<Object, Call<? > > () {public Type responseType(a) {
        return responseType;
    }
	// Execute here
    public Call<Object> adapt(Call<Object> call) {
        return (Call)(executor == null ? call : newDefaultCallAdapterFactory.ExecutorCallbackCall(executor, call)); }};Copy the code

So call adapt () method returns a DefaultCallAdapterFactory. Eventually ExecutorCallbackCall () object. Its brief code is as follows:

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) {... }... }Copy the code
3.3 summary
loadServiceMethod(method).invoke(args ! =null ? args : emptyArgs)
Copy the code

This step performs the process of dynamic proxy, which gives methods in the interface the ability to initiate requests by way of facets. Through loadServiceMethod (method) to obtain a ServiceMethod object, by calling the invoke () adapter, obtained a DefaultCallAdapterFactory ExecutorCallbackCall object.

4, make the request and get the callback

repos.enqueue(object : Callback<List<Repo>> {
    override fun onResponse(call: Call<List<Repo>>, response: Response<List<Repo>>) {
        LogUtil.e("result: " + response.body())
    }

    override fun onFailure(call: Call<List<Repo>>, t: Throwable) {
        LogUtil.e("onFailure: " + t.toString())
    }
})
Copy the code

In the third step to obtain a DefaultCallAdapterFactory ExecutorCallbackCall object, So on call the enqueue () call is actually DefaultCallAdapterFactory. ExecutorCallbackCall in the enqueue (), then look at the end of after a request by OkHttp request and returns the result.

4.1 ExecutorCallbackCall

DefaultCallAdapterFactory. ExecutorCallbackCall in the enqueue () as follows:

// Brief code
@Override 
public void enqueue(final Callback<T> callback) {
	delegate.enqueue(new Callback<T>() {
		@Override 
		public void onResponse(Call<T> call, final Response<T> response) {
			callbackExecutor.execute(new Runnable() {
                @Override public void run(a) {... callback.onResponse(ExecutorCallbackCall.this, response); }}); }@Override public void onFailure(Call<T> call, final Throwable t) {
			callbackExecutor.execute(new Runnable() {
				@Override public void run(a) {
					callback.onFailure(ExecutorCallbackCall.this, t); }}); }}); }Copy the code

Internally, it calls the delegate’s enqueue() method, which returns the result to the callbackExecutor for thread switching and callback to the business layer.

ExecutorCallbackCall is a wrapper around a delegate, adding the ability to switch threads.

So what’s the delegate here? It is the OkHttpCall object created in step 3.2:

Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
Copy the code

Next, look at the enqueue method in OkHttpCall

4.2 OkHttpCall
// Brief code
@Override 
public void enqueue(final Callback<T> callback) {... okhttp3.Call call = createRawCall(); . call.enqueue(new okhttp3.Callback() {
      @Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
        Response<T> response;
        try {
            // Return the result format conversion
			response = parseResponse(rawResponse);
        } catch (Throwable e) {
			return;
        }

        try {
			callback.onResponse(OkHttpCall.this, response);
        } catch (Throwable t) {
			throwIfFatal(t);
			t.printStackTrace(); // TODO this is not great}}@Override public void onFailure(okhttp3.Call call, IOException e) { callFailure(e); }... }); }Copy the code

It first gets an okHttp3.call, then makes a real network request through this OkHttp Call, transforms the result of the request through parseResponse(), and finally calls back to an ExecutorCallbackCall in 4.1.

You can see that OkHttpCall is a wrapper around OkHttp, adding the ability to transform the returned results.

4.3 summary

ExecutorCallbackCall is a wrapper around OkHttpCall, adding the ability to switch threads; OkHttpCall is a wrapper around OkHttp, adding the ability to transform the returned results.

Three,

Retrofit logic is very complex, this article from the user of the process of using retrofit analysis of the general implementation process, which neglected a lot of details, hope to help you.