Using Retrofit as an Android web request has become quite common, and the source code for Retrofit is small and sophisticated enough to be a good read and learn object. Although I used it a long time ago, BUT I really haven’t seen the source code, so I’ll try it out while I’m free. This Retrofit version is 2.6.0. After reading it, I think it will be easier before 2.4, unfortunately I can’t find the old jar package

Basic usage

To understand the source code of a framework, you must know its basic usage. Then select an entry method from the base usage so that you can read along with the usage.

Define the request interface
public interface GitHubService {
    @GET("users/{user}/repos")
    Call<List<UserBean>> listRepos(@Path("user") String user);
}
Copy the code
2. Initialize Retrofit
    Retrofit retrofit = new Retrofit.Builder()
            .baseUrl("https://api.github.com/")
            .client(new OkHttpClient())
            //.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
            .addConverterFactory(GsonConverterFactory.create())
            .build();
    GitHubService service = retrofit.create(GitHubService.class);
Copy the code
3. Network request
    Call<List<UserBean>> repos = service.listRepos("octocat");
    {
        repos.enqueue(new Callback<List<UserBean>>() {
            @Override
            public void onResponse(Call<List<UserBean>> call, Response<List<UserBean>> response) {
                List<UserBean> list = response.body();
            }

            @Override
            public void onFailure(Call<List<UserBean>> call, Throwable throwable) {}}); }Copy the code

The source code to read

It’s clear from the three steps above that retrofit.create() is an entry method. Let’s first look at what retrofit () initializes.

Retrofit initialization

Retrofit is initialized through the Builder design pattern. Take a look at what’s done in the build() method.

public Retrofit build(a) {
	// baseUrl is required for initialization
    if(this.baseUrl == null) {
        throw new IllegalStateException("Base URL required.");
    } else {
        Object callFactory = this.callFactory;
        // If client is not set during initialization, callFactory is null
        // Create an OkHttpClient assignment to the callFactory by default
        if(callFactory == null) {
            callFactory = new OkHttpClient();
        }
        Executor callbackExecutor = this.callbackExecutor;
        if(callbackExecutor == null) {
            callbackExecutor = this.platform.defaultCallbackExecutor();
        }
  		// when addCallAdapterFactory is called
  		/ / if there is no initialization, use DefaultCallAdapterFactory
        ArrayList callAdapterFactories = new ArrayList(this.callAdapterFactories);
        callAdapterFactories.addAll(this.platform.defaultCallAdapterFactories(callbackExecutor));
        // call addConverterFactory to initialize,
        // Use default BuiltInConverters if not initialized
        ArrayList converterFactories = new ArrayList(1 + this.converterFactories.size() + this.platform.defaultConverterFactoriesSize());
        converterFactories.add(new BuiltInConverters());
        converterFactories.addAll(this.converterFactories);
        converterFactories.addAll(this.platform.defaultConverterFactories());
        return new Retrofit((Factory)callFactory, this.baseUrl, Collections.unmodifiableList(converterFactories), Collections.unmodifiableList(callAdapterFactories), callbackExecutor, this.validateEagerly); }}Copy the code

Three very important parameters are initialized when constructing a Retrofit object:

  • callFactory
  • CallAdapter
  • ConverterFactory

1. CallFactory, OkHttpClient, inherits call.Factory. OKhttp factory class 2. CallAdapter The adapter pattern is responsible for converting the Call object to the type we need. An example is the Observable object in Rxjava.

The following default login to return to the Call object, when using the initialization calls when addCallAdapterFactory (RxJava2CallAdapterFactory. The create ()) to add a RxJava Retrofit of the adapter, Returns an Observable

    // When using the default adapter
    Call<String> login(a);  

    // Return when adding the Rxjava adapter
    Observable<String> login(a);  
Copy the code

3, ConverterFactory Convert the response returned from HTTP into the object we need. Such as the UserBean.

Build the Service object
 GitHubService service = retrofit.create(GitHubService.class);
Copy the code

Create method:

    public <T> T create(final Class<T> service) {
        Utils.validateServiceInterface(service);
        if(this.validateEagerly) {
            this.eagerlyValidateMethods(service);
        }

        return Proxy.newProxyInstance(service.getClassLoader(), new Class[]{service}, new InvocationHandler() {
            private final Platform platform = Platform.get();
            private final Object[] emptyArgs = new Object[0];

            @Nullable
            public Object invoke(Object proxy, Method method, @Nullable Object[] args) throws Throwable {
                returnmethod.getDeclaringClass() == Object.class? method.invoke(this, args):(this.platform.isDefaultMethod(method)?
				this.platform.invokeDefaultMethod(method, service, proxy, args):
				Retrofit.this.loadServiceMethod(method).invoke(args ! =null? args:this.emptyArgs)); }}); }Copy the code

The Create method does two things:

Use proxy mode to dynamically create a Service object and return it

Create a ServiceMethod object and call the invoke() method.

About dynamic proxy and reflection can be found in my other two articles: Proxy pattern Java reflection mechanism

Create a ServiceMethod object

Get a ServiceMethod using the loadServiceMethod() method.

ServiceMethod<? > loadServiceMethod(Method method) { ServiceMethod result = (ServiceMethod)this.serviceMethodCache.get(method);
        if(result ! =null) {
            return result;
        } else {
            Map var3 = this.serviceMethodCache;
            synchronized(this.serviceMethodCache) {
                result = (ServiceMethod)this.serviceMethodCache.get(method);
                if(result == null) {
                    result = ServiceMethod.parseAnnotations(this, method);
                    this.serviceMethodCache.put(method, result);
                }
                returnresult; }}}Copy the code

You can see that one of the loadServiceMethod() methods caches the ServiceMethod object. When the ServiceMethod object is null, Invokes the ServiceMethod. ParseAnnotations (this, method) to obtain a ServiceMethod object.

abstract class ServiceMethod<T> {
    ServiceMethod() {
    }

    static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
        RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);
        Type returnType = method.getGenericReturnType();
        if(Utils.hasUnresolvableType(returnType)) {
            throw Utils.methodError(method, "Method return type must not include a type variable or wildcard: %s".new Object[]{returnType});
        } else if(returnType == Void.TYPE) {
            throw Utils.methodError(method, "Service methods cannot return void.".new Object[0]);
        } else {
            returnHttpServiceMethod.parseAnnotations(retrofit, method, requestFactory); }}@Nullable
    abstract T invoke(Object[] var1);
}
Copy the code

ServiceMethod is an abstract class. Obtain a ServiceMethod object using the parseAnnotations() method.

Get RequestFactory

See ServiceMethod parseAnnotations method, first call RequestFactory. ParseAnnotations method, get a RequestFactory object

  RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);
Copy the code

So what is a RequestFactory?

Check the RequestFactory parseAnnotations source code is as follows

You can see that RequestFactory is again a Builder pattern. Take a look at the parameters and methods in Builder


    @GET("users/{user}/repos")
    Call<List<UserBean>> listRepos(@Path("user") String user);
Copy the code

For example, parse the above request method GET, path “users/{user}/repos”, parameters, etc.

Let’s take a look at the parsing methods:

// Methods to parse Http requests, get, POST, etc
private void parseMethodAnnotation(Annotation annotation);
// Parse Http request methods and paths
private void parseHttpMethodAndPath(String httpMethod, String value, boolean hasBody);
// Parse the request header
private Headers parseHeaders(String[] headers)
// Parse the request parameters
privateParameterHandler<? >parseParameter(int p, Type parameterType, @Nullable Annotation[] annotations, boolean allowContinuation)
// Parse annotations in arguments such as @path. Method to invoke ParameterHandler parsing
 privateParameterHandler<? >parseParameterAnnotation(int p, Type type, Annotation[] annotations, Annotation annotation)
 
static Set<String> parsePathParameters(String path);
Copy the code

ParameterHandler has an important class when parsing methods. It is ParameterHandler that does the parsing.

Now we know what RequestFactory does, parsing HTTP request methods, paths, requests, etc., and HTTP request parameters.

Let’s go back to our ServiceMethod class.

    static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
        RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);
        Type returnType = method.getGenericReturnType();
        if(Utils.hasUnresolvableType(returnType)) {
            throw Utils.methodError(method, "Method return type must not include a type variable or wildcard: %s".new Object[]{returnType});
        } else if(returnType == Void.TYPE) {
            throw Utils.methodError(method, "Service methods cannot return void.".new Object[0]);
        } else {
            returnHttpServiceMethod.parseAnnotations(retrofit, method, requestFactory); }}Copy the code

Through reflection method return Type: Type returnType = method. The getGenericReturnType ();

Then call HttpServiceMethod. ParseAnnotations (retrofit method, requestFactory); Get an HttpServiceMethod object.

HttpServiceMethod

HttpServiceMethod inherits from ServiceMethod.

The HttpServiceMethod constructor is as follows:

    HttpServiceMethod(RequestFactory requestFactory, Factory callFactory, Converter<ResponseBody, ResponseT> responseConverter) {
        this.requestFactory = requestFactory;
        this.callFactory = callFactory;
        this.responseConverter = responseConverter;
    }
Copy the code

Three very important parameters need to be initialized:

  • RequestFactory: A request parameter in a service
  • CallFactory: An adapter for a Call
  • ResponseConverter: Response converter

HttpServiceMethod. ParseAnnotations () source (omitted) as follows:

    static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(Retrofit retrofit, Method method, RequestFactory requestFactory) {... Annotation[] annotations = method.getAnnotations(); Object adapterType; Type responseType; . adapterType = method.getGenericReturnType();// Create a Call adapter
        CallAdapter callAdapter1 = createCallAdapter(retrofit, method, (Type)adapterType, annotations);
        // Get the return typeresponseType = callAdapter1.responseType(); .// Create a Converter
        Converter responseConverter = createResponseConverter(retrofit, method, responseType);
        Factory callFactory = retrofit.callFactory;
        return (HttpServiceMethod)(new HttpServiceMethod.CallAdapted(requestFactory, callFactory, responseConverter, callAdapter1);
	}
Copy the code

Create a CallAdapter object

Tracing the createCallAdapter() method will eventually call the nextCallAdapter() method in the Retrofit class. The source code is as follows

    publicCallAdapter<? ,? > nextCallAdapter(@Nullable retrofit2.CallAdapter.Factory skipPast, Type returnType, Annotation[] annotations) {
        Utils.checkNotNull(returnType, "returnType == null");
        Utils.checkNotNull(annotations, "annotations == null");
        int start = this.callAdapterFactories.indexOf(skipPast) + 1;
        int builder = start;

        int i;
        for(i = this.callAdapterFactories.size(); builder < i; ++builder) {
            CallAdapter count = ((retrofit2.CallAdapter.Factory)this.callAdapterFactories.get(builder)).get(returnType, annotations, this);
            // Get a non-null CallAdapter and return
            if(count ! =null) {
                returncount; }}...throw new IllegalArgumentException(var8.toString());
    }
Copy the code

The nextCallAdapter() method iterates through the CallAdapters in the callAdapterFactories collection, CallAdapterFactories, on the other hand, assign values to Retrofit via addCallAdapterFactory() or default values.

Returns when a non-null CallAdapter is obtained.

2. Create the Converter object

Through the responseType = callAdapter1. ResponseType (); I get response.

ResponseConverter = responseType (retrofit, method, responseType); Create a responseConverter

Tracking createResponseConverter () method, and ultimately in the Retrofit call nextResponseBodyConverter () method, the source code is as follows:

	public <T> Converter<ResponseBody, T> nextResponseBodyConverter(@Nullable retrofit2.Converter.Factory skipPast, Type type, Annotation[] annotations) {
        Utils.checkNotNull(type, "type == null");
        Utils.checkNotNull(annotations, "annotations == null");
        int start = this.converterFactories.indexOf(skipPast) + 1;
        int builder = start;

        int i;
        for(i = this.converterFactories.size(); builder < i; ++builder) {
            Converter count = ((retrofit2.Converter.Factory)this.converterFactories.get(builder)).responseBodyConverter(type, annotations, this);
            if(count ! =null) {
                returncount; }}throw new IllegalArgumentException(var8.toString());
    }
Copy the code

Also get the Converter object when Retrofit is initialized and return it. Initialize or use the default Converter with the addConverterFactory() method.

3. Get the callFactory

CallFactory = retrofit.callFactory; Get a Factory object.

Again, the assignment is created when RetroFIT is initialized.

Return the HttpServiceMethod object

New a CallPicked object inherited from HttpServiceMethod

    static final class CallAdapted<ResponseT.ReturnT> extends HttpServiceMethod<ResponseT.ReturnT> {
        private final CallAdapter<ResponseT, ReturnT> callAdapter;

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

        protected ReturnT adapt(Call<ResponseT> call, Object[] args) {
            return this.callAdapter.adapt(call); }}Copy the code

This is the main function of the HttpServiceMethod class. To Create this ServiceMethod, we need to go back to the Create() method again.

Create the OkHttpCall object

The Create method does two things:

Use proxy mode to dynamically create a Service object and return it

Create a ServiceMethod object and call the invoke() method.

With the ServiceMethod object created above, we have obtained an instance of ServiceMethod, HttpServiceMethod, and now invoke() from the HttpServiceMethod object.

The source code is as follows:

    final ReturnT invoke(Object[] args) {
        OkHttpCall call = new OkHttpCall(this.requestFactory, args, this.callFactory, this.responseConverter);
        return this.adapt(call, args);
    }
Copy the code

An OkHttpCall object is created and initialized in the Invoke method, inheriting the Call object.

Three, request network
    Call<List<UserBean>> call = service.listRepos("octocat");
    {
        call.enqueue(new Callback<List<UserBean>>() {
            @Override
            public void onResponse(Call<List<UserBean>> call, Response<List<UserBean>> response) {}@Override
            public void onFailure(Call<List<UserBean>> call, Throwable throwable) {}}); }Copy the code
Call.queue () source code

Call the request method in ApiService, get a Call return value, and execute the network request through call.enQueue ().

The Call object is an interface and its implementation class is OkHttpCall, so the real network request logic is in OkHttpCall

Queue () ¶

    public void enqueue(final Callback<T> callback) {

        okhttp3.Call call;// Declare an okHTTP request callThrowable failure; .// omit some null judgments, null processing
        	
        	// Perform okHTTP requests. Retrofit is a web request framework encapsulated based on OKHttp3.
        	// The part that actually requests the network is OKhttp
            call.enqueue(new okhttp3.Callback() {
            	//okhttp callback succeeded
                public void onResponse(okhttp3.Call call, Response rawResponse) {
                    retrofit2.Response response;
                    try {
                    	// Parse the successful data, return the type we need
                        response = OkHttpCall.this.parseResponse(rawResponse);
                    } catch (Throwable var6) {
                        Utils.throwIfFatal(var6);
                        this.callFailure(var6);
                        return;
                    }

                    try {
                    	// External callback callback
                        callback.onResponse(OkHttpCall.this, response);
                    } catch(Throwable var5) { Utils.throwIfFatal(var5); var5.printStackTrace(); }}//OKhttp failed callback
                public void onFailure(okhttp3.Call call, IOException e) {
                    this.callFailure(e);
                }				
                private void callFailure(Throwable e) {
                    try {
                    	// External Callback Callback
                        callback.onFailure(OkHttpCall.this, e);
                    } catch(Throwable var3) { Utils.throwIfFatal(var3); var3.printStackTrace(); }}}); }Copy the code

As you can see from the source code, it is OKhttp that actually executes the network request, and when the request succeeds, there is an important line in the OKhttp success callback:

retrofit2.Response response; / / parsing OKhttp return rawResponse, convert the response we need response = OkHttpCall. Enclosing parseResponse (rawResponse);Copy the code
The result of successfully parsing OKhttp is returned

ParseResponse ()

    retrofit2.Response<T> parseResponse(Response rawResponse) throws IOException {
        ResponseBody rawBody = rawResponse.body();
        rawResponse = rawResponse.newBuilder().body(new OkHttpCall.NoContentResponseBody(rawBody.contentType(), rawBody.contentLength())).build();
        int code = rawResponse.code();
        if(code >= 200 && code < 300) {
            if(code ! =204&& code ! =205) {
                OkHttpCall.ExceptionCatchingResponseBody catchingBody1 = new OkHttpCall.ExceptionCatchingResponseBody(rawBody);

                try {
                	// Convert the Body data to the type we need
                    Object e1 = this.responseConverter.convert(catchingBody1);
                    return retrofit2.Response.success(e1, rawResponse);
                } catch (RuntimeException var9) {
                    catchingBody1.throwIfCaught();
                    throwvar9; }}else {
                rawBody.close();
                return retrofit2.Response.success((Object)null, rawResponse); }}else{.../ / to omit
            returne; }}Copy the code

There is also a very core piece of code:

  Object e1 = this.responseConverter.convert(catchingBody1);
Copy the code

The convert method is called via responseConverter.

So what is responseConverter?

Where is responseConverter assigned?

OKhttpCall is created in the invoke() method that calls HttpServiceMethod, And responseConverter is actually in HttpServiceMethod by converting responseConverter = createResponseConverter(retrofit, method, responseType); To create.

At this point Retrofit initialization is complete until the end of the network. Because when Retrofit is initialized, a defaultCallbackExecutor is initialized.

                Executor callbackExecutor = this.callbackExecutor;
                if(callbackExecutor == null) {
                    callbackExecutor = this.platform.defaultCallbackExecutor();
                }
Copy the code

DefaultCallbackExecutor is available on Android as follows

        public Executor defaultCallbackExecutor(a) {
            return new Platform.Android.MainThreadExecutor();
        }
        
        static class MainThreadExecutor implements Executor {
            private final Handler handler = new Handler(Looper.getMainLooper());

            MainThreadExecutor() {
            }

            public void execute(Runnable r) {
                this.handler.post(r); }}Copy the code

As you can see, this is a callback in the main thread, so we can perform UI operations directly in Retrofit’s callback.

Retrofit flow chart:

conclusion

Now that I’ve read Retrofit’s source code, there are a lot of questions about why it’s done that haven’t been digested, but it’s just that it’s done that way. I also did some preparation before looking at the proxy mode, reflection mode, adapter mode, decoration mode and so on. Fortunately, I am not ready to eat a fat man, slowly accumulated, over a period of time to see whether it will be better.