What is a Retrofit
- Retrofit is Square’s framework for repackaging its okHTTP low-level web requests to make web requests easier. Of course retrofit is not designed solely for the OKHTTP service, but the default implementation uses OKHTTP.
The realization of the Retrofit
Let’s start with a few of Retrofit’s important classes
Retrofit
Retrofit adapts a Java interface to HTTP calls by using annotations on the declared methods to define how requests are made. Create instances using {@linkplain Builder the builder} and pass your interface to {@link #create} to generate an implementation.
- A dynamic proxy is used to generate HTTP requests by parsing annotations on method declarations.
- The Retrofit instance primarily acts as an adapter.
Call
An invocation of a Retrofit method that sends a request to a webserver and returns a response. Each call yields its own HTTP request and response pair. Use {@link #clone} to make multiple calls with the same parameters to the same webserver; this may be used to implement polling or to retry a failed call.
- An implementation of the Retrofit method that sends a request to the server and returns a response
- Saves a pair of HTTP requests and responses
Converter<F, T>
Convert objects to and from their representation in HTTP. Instances are created by {@linkplain Factory a factory} which is {@linkplain Retrofit.Builder#addConverterFactory(Factory) installed} into the {@link Retrofit} instance.
- Converts the HTTP response to the return type required by the request interface
CallAdapter<R, T>
Adapts a {@link Call} with response type {@code R} into the type of {@code T}. Instances are created by {@linkplain Factory a factory} which is {@linkplain Retrofit.Builder#addCallAdapterFactory(Factory) installed} into the {@link Retrofit} instance.
- ADAPTS requests to meet interface requirements
ServiceMethod<R, T>
Adapts an invocation of an interface method into an HTTP call.
- ADAPTS the defined network request method into an HTTP Call
ParameterHandler
- ServiceMethod Specifies the location where values are saved during parameter parsing
RequestBuilder
- Build an initial Request
Retrofit is the process of initiating a request
-
Take a look at the invoked code.
Retrofit was created using the dynamic proxy approach, and the intermediate Platform is compatible for different platforms. You can clearly see that by proxying the Service and building a serviceMethod from its method, Then, a new okHttpCall is generated, and a network request is made to okHttpCall using the internal callAdapter held by serviceMethod and adapted to the expected return type of the user to complete a network request.
public <T> T create(final Class<T> service) { Utils.validateServiceInterface(service); if (validateEagerly) { eagerlyValidateMethods(service); } return (T) Proxy.newProxyInstance(service.getClassLoader(), newClass<? >[] { service },new InvocationHandler() { private final Platform platform = Platform.get(); @Override public 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); } if (platform.isDefaultMethod(method)) { return platform.invokeDefaultMethod(method, service, proxy, args); } ServiceMethod<Object, Object> serviceMethod = (ServiceMethod<Object, Object>) loadServiceMethod(method); OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args); returnserviceMethod.callAdapter.adapt(okHttpCall); }}); }Copy the code
-
The construction of ServiceMethod
The ServiceMethodg construction process is mainly to annotate network requests defined in the Service. The completed parameters are stored in the ParameterHandler for future use. As for obtaining the annotation parameters, it will not be described here.
-
CallAdapter access
private CallAdapter<T, R> createCallAdapter(a) { Type returnType = method.getGenericReturnType(); if (Utils.hasUnresolvableType(returnType)) { throw methodError( "Method return type must not include a type variable or wildcard: %s", returnType); } if (returnType == void.class) { throw methodError("Service methods cannot return void."); } Annotation[] annotations = method.getAnnotations(); try { //noinspection unchecked return (CallAdapter<T, R>) retrofit.callAdapter(returnType, annotations); } catch (RuntimeException e) { // Wide exception range because factories are user code. throw methodError(e, "Unable to create call adapter for %s", returnType); }}Copy the code
Here we start with Factory mode, using the callAdapter. Factory method to get the CallAdapter. We know that we can build Retrofit with addCallAdapterFactory, which means we can implement callAdapters ourselves and provide them to Retrofit to make our code more extensible.
The qualified Factory is retrieved by iterating through the callAdapter.Factory list saved in Retrofit for the transformation to be used for the request.
As you can see from the above code, when you actually get it, it’s the default CallAdapter initialized in RetroFIT
-
The default CallAdapter
You can see the following code in the Retrofit build process
Executor callbackExecutor = this.callbackExecutor; if (callbackExecutor == null) { callbackExecutor = platform.defaultCallbackExecutor(); } // Make a defensive copy of the adapters and add the default Call adapter. List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories); adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor)); Copy the code
The platform we obtained through platform adaptation is actually the following Android platform
static class Android extends Platform { @Override public Executor defaultCallbackExecutor(a) { return new MainThreadExecutor(); } @Override CallAdapter.Factory defaultCallAdapterFactory(@Nullable Executor callbackExecutor) { if (callbackExecutor == null) throw new AssertionError(); return new ExecutorCallAdapterFactory(callbackExecutor); } 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
Through the android platform can initialize a default ExecutorCallAdapterFactory, used to obtain the default CallAdapter.
Through ExecutorCallAdapterFactory can get to a CallAdapter implementation, and will Call the adapter into a ExecutorCallbackCall, internal processing and thread switching network request.
-
Requested combing
From the above analysis, the default request process is as follows:
- Dynamically proxy the Service
- Build ServiceMethod
- Build OkHttpCall with ServiceMethod
- According to the real through the CallAdapter ServiceMethod ExecutorCallAdapterFactory, OkHttpCall get adapted to the Call object we need
- After obtaining the Call required by the user, execute or enqueue can be performed.
This is the end of the basic network request.
·~~~~~~~~~~~~~~~~~~·
·~~~~~~~~~~~~~~~~~~·
·~~~~~~ I am the dividing line ~~~~~~~·
·~~~~~~~~~~~~~~~~~~·
·~~~~~~~~~~~~~~~~~~·
Wait a minute! If you feel like you missed something, the Converter mentioned earlier seems to be used.
We said that the Converter converts the response, which is definitely used in Call, so let’s go back to the OkHttpCall build.
-
OkHttpCall
So let’s focus on the parsing of response
Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException { ResponseBody rawBody = rawResponse.body(); // Remove the body's source (the only stateful object) so we can pass the response along. rawResponse = rawResponse.newBuilder() .body(new NoContentResponseBody(rawBody.contentType(), rawBody.contentLength())) .build(); int code = rawResponse.code(); if (code < 200 || code >= 300) { try { // Buffer the entire body to avoid future I/O. ResponseBody bufferedBody = Utils.buffer(rawBody); return Response.error(bufferedBody, rawResponse); } finally{ rawBody.close(); }}if (code == 204 || code == 205) { rawBody.close(); return Response.success(null, rawResponse); } ExceptionCatchingRequestBody catchingBody = new ExceptionCatchingRequestBody(rawBody); try { T body = serviceMethod.toResponse(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; }}Copy the code
Let’s look at this one
T body = serviceMethod.toResponse(catchingBody); Copy the code
If you go back to the end, the actual code looks like this
public <T> Converter<ResponseBody, T> nextResponseBodyConverter( @Nullable Converter.Factory skipPast, Type type, Annotation[] annotations) { checkNotNull(type, "type == null"); checkNotNull(annotations, "annotations == null"); int start = converterFactories.indexOf(skipPast) + 1; for (inti = 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
So the GsonConverterFactory that we always set up is going to be called here, and it’s going to parse the actual response.
At this point, a complete network request and parameter parsing are complete
conclusion
- Retrofit is itself an adapter for coordinating resources
- Dynamic code mode, where each request is generated dynamically
- The static proxy mode, such as ExecutorCallbackCall returned to us, is actually a proxy object for internal calls
- Builder mode, simplify the construction of objects
- The factory pattern is used while increasing the customization of the code
- Adapter pattern, which ADAPTS the original response to the type actually needed