Article directory
I. information
Author: yoyiyi(soleil)
Proofreader: yoyiyi(soleil)
Date of establishment: January 22, 2020
Revised on: January 28, 2020
Warehouse: Soleil-Notes
Two, introduction
This is the second part of Android advanced notes, retrofit source code analysis. Retrofit is a RESTful design style network request framework. As for what is the RESTful design style interface, let's not expand it here. Students who are interested in it can consult relevant materials. In the past, we designed all interfaces according to the RESTful design It's very nice to get up. Far away, back to the topic, for retrofit, the bottom Okhttp is actually responsible for the network request work. For the analysis of Okhttp, please refer to the previous article Android advanced notes - Okhttp4.X source code analysis.
Three. Introduction
Here is a small example. The interface is Fun and fun Interface, request item classification.
//1. Define API to describe the requested interface public interface WanAndroidService { @GET("project/tree/json") Call<CategoryEntity> getCategory(); } //2. Create Retrofit val retrofit = Retrofit.Builder() .baseUrl("https://www.wanandroid.com/") .addConverterFactory(GsonConverterFactory.create()) .build() //3. Create a network request instance val service = retrofit.create(WanAndroidService::class.java) //4. Call the network request API, generate a call, and execute the request val call= service.getCategory() call.enqueue(object : retrofit2.Callback<CategoryEntity> { override fun onFailure(call: retrofit2.Call<CategoryEntity>, t: Throwable) { } override fun onResponse( call: retrofit2.Call<CategoryEntity>, response: retrofit2.Response<CategoryEntity> ) { val result = response.body() Log.d("result", result.toString()) } })
From the above code, we can see that the use process of Retrofit is very simple, but this article is not about the use of Retrofit. In our learning, we should not only look at the appearance, but also look at the essence, so as to make continuous progress.
4, Source code analysis
4.1. Create retrofit
In the above code, there is a very important key point, which is the creation of Retrofit. Let's see how Retrofit is built.
4.1.1. Building
//Building Retrofit with builder mode val retrofit = Retrofit.Builder() .baseUrl("https://www.wanandroid.com/") .addConverterFactory(GsonConverterFactory.create()) .build()
Next I'll take a look at Builder, which is an internal class of Retrofi
public static final class Builder { //Platform type private final Platform platform; //Request factory, Okhttp by default private @Nullable okhttp3.Call.Factory callFactory; //Address of the requested url private @Nullable HttpUrl baseUrl; //Factory set for data conversion private final List<Converter.Factory> converterFactories = new ArrayList<>(); //Collection of adapter factories, default ExecutorCallAdapterFactory private final List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(); //Call back the executor and switch the sub thread to the main thread. On Android, it encapsulates the main thread executor of the handler private @Nullable Executor callbackExecutor; //Cache, create ServiceMethod for true private boolean validateEagerly; }
Let's see the default initialization of Builder
public static final class Builder { Builder(Platform platform) { this.platform = platform; } public Builder() { this(Platform.get()); } ..... } //Platform class is involved class Platform { private static final Platform PLATFORM = findPlatform(); static Platform get() { return PLATFORM; } private static Platform findPlatform() { try { //Determine whether it is Android platform Class.forName("android.os.Build"); if (Build.VERSION.SDK_INT != 0) { //Create an Android class return new Android(); } } catch (ClassNotFoundException ignored) { } return new Platform(true); } //Create default network request adapter factory List<? extends CallAdapter.Factory> defaultCallAdapterFactories( @Nullable Executor callbackExecutor) { //Default network adapter DefaultCallAdapterFactory executorFactory = new DefaultCallAdapterFactory(callbackExecutor); return hasJava8Types ? asList(CompletableFutureCallAdapterFactory.INSTANCE, executorFactory) : singletonList(executorFactory); } int defaultCallAdapterFactoriesSize() { return hasJava8Types ? 2 : 1; } List<? extends Converter.Factory> defaultConverterFactories() { return hasJava8Types ? singletonList(OptionalConverterFactory.INSTANCE) : emptyList(); } //Inherit Platform static final class Android extends Platform { Android() { super(Build.VERSION.SDK_INT >= 24); } @Override public Executor defaultCallbackExecutor() { //Switch thread, sub thread to main thread return new MainThreadExecutor(); } // Handler mechanism, sub thread switches to main thread static class MainThreadExecutor implements Executor { private final Handler handler = new Handler(Looper.getMainLooper()); @Override public void execute(Runnable r) { handler.post(r); } } }
4.1.2. Add baseUrl
//Retrofit.java public Builder baseUrl(String baseUrl) { Objects.requireNonNull(baseUrl, "baseUrl == null"); //Convert string to HttpUrl return baseUrl(HttpUrl.get(baseUrl)); } public Builder baseUrl(HttpUrl baseUrl) { Objects.requireNonNull(baseUrl, "baseUrl == null"); List<String> pathSegments = baseUrl.pathSegments(); if (!"".equals(pathSegments.get(pathSegments.size() - 1))) { throw new IllegalArgumentException("baseUrl must end in /: " + baseUrl); } this.baseUrl = baseUrl; return this; }
4.1.3. Add GsonConverterFactory
//1. create of gsonconverterfactory public static GsonConverterFactory create() { return create(new Gson()); } //2. Call create public static GsonConverterFactory create(Gson gson) { if (gson == null) throw new NullPointerException("gson == null"); return new GsonConverterFactory(gson); } private final Gson gson; //3. Create a GsonConverterFactory with a Gson object private GsonConverterFactory(Gson gson) { this.gson = gson; } //4. Add addGsonConverFactory. To put it bluntly, add the GsonConverterFactory containing the Gson object to the data conversion factory converterFactories public Builder addConverterFactory(Converter.Factory factory) { converterFactories.add(Objects.requireNonNull(factory, "factory == null")); return this; }
4.1.4.build()
Next let's see what's done in the build() method.
public Retrofit build() { if (baseUrl == null) { throw new IllegalStateException("Base URL required."); } okhttp3.Call.Factory callFactory = this.callFactory; if (callFactory == null) { //Default request factory uses OkHttpClient callFactory = new OkHttpClient(); } Executor callbackExecutor = this.callbackExecutor; if (callbackExecutor == null) { //Callback callbackExecutor = platform.defaultCallbackExecutor(); } // Make a defensive copy of the adapters and add the default Call adapter. List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories); callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor));//Add default adapter // Make a defensive copy of the converters. List<Converter.Factory> converterFactories = new ArrayList<>( 1 + this.converterFactories.size() + platform.defaultConverterFactoriesSize()); // Add the built-in converter factory first. This prevents overriding its behavior but also // ensures correct behavior when using converters that consume all types. converterFactories.add(new BuiltInConverters()); converterFactories.addAll(this.converterFactories); converterFactories.addAll(platform.defaultConverterFactories()); return new Retrofit(callFactory, baseUrl, unmodifiableList(converterFactories), unmodifiableList(callAdapterFactories), callbackExecutor, validateEagerly); } }
4.2. Create network request
Next, analyze the process of retrofit.create(), which adopts appearance mode and agent mode.
public <T> T create(final Class<T> service) { //Verify interface validateServiceInterface(service); //Use dynamic proxy to get all interface annotation configuration of request interface and create network request interface instance return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { 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); } if (platform.isDefaultMethod(method)) { return platform.invokeDefaultMethod(method, service, proxy, args); } return loadServiceMethod(method).invoke(args != null ? args : emptyArgs); } }); } private void validateServiceInterface(Class<?> service) { if (!service.isInterface()) { throw new IllegalArgumentException("API declarations must be interfaces."); } Deque<Class<?>> check = new ArrayDeque<>(1); check.add(service); while (!check.isEmpty()) { Class<?> candidate = check.removeFirst(); 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()); } Collections.addAll(check, candidate.getInterfaces()); } if (validateEagerly) { Platform platform = Platform.get(); for (Method method : service.getDeclaredMethods()) { if (!platform.isDefaultMethod(method) && !Modifier.isStatic(method.getModifiers())) { loadServiceMethod(method); } } } }
Next let's look at the loadServiceMethod
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; } abstract class ServiceMethod<T> { static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) { //Parsing comments for request configuration 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."); } //Request method built by HttpServiceMethod return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory); } abstract @Nullable T invoke(Object[] args); }
Let's take a look at httpservicemethod and parseannotations
static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations( Retrofit retrofit, Method method, RequestFactory requestFactory) { //1. Get the corresponding network request adapter from the Retrofit object CallAdapter<ResponseT, ReturnT> callAdapter = createCallAdapter(retrofit, method, adapterType, annotations); //2. Obtain the corresponding data converter from the Retrofit object according to the return value and annotation type of the network request interface method Converter<ResponseBody, ResponseT> responseConverter = createResponseConverter(retrofit, method, responseType);
Let's look at createCallAdapter
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); } } public CallAdapter<?, ?> nextCallAdapter(@Nullable CallAdapter.Factory skipPast, Type returnType, Annotation[] annotations) { Objects.requireNonNull(returnType, "returnType == null"); Objects.requireNonNull(annotations, "annotations == null"); int start = callAdapterFactories.indexOf(skipPast) + 1; //Cycle to get the right request factory for (int i = start, count = callAdapterFactories.size(); i < count; i++) { CallAdapter<?, ?> adapter = callAdapterFactories.get(i).get(returnType, annotations, this); if (adapter != null) { return adapter; } }
Let's look at createResponseConverter
//Finally, they all came into this method public <T> Converter<ResponseBody, T> nextResponseBodyConverter( @Nullable Converter.Factory skipPast, Type type, Annotation[] annotations) { Objects.requireNonNull(type, "type == null"); Objects.requireNonNull(annotations, "annotations == null"); int start = converterFactories.indexOf(skipPast) + 1; //Cycle to get the right conversion plant 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; } } }
Finally, execute httpservicemethod "invoke"
@Override final @Nullable ReturnT invoke(Object[] args) { //OkHttpCall responsible for network requests Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter); return adapt(call, args); }
4.3. Call the network request API, generate a call, and execute the request
val call= service.getCategory()
From the above analysis, we can see that the service object is actually the Call object obtained from the dynamic proxy object Proxy.newProxyInstance().
4.3.1. Asynchronous request
enqueue is called by asynchronous request
//DefaultCallAdapterFactory.java @Override public void enqueue(final Callback<T> callback) { Objects.requireNonNull(callback, "callback == null"); //Using static proxy delegate for seven cattle delegate.enqueue(new Callback<T>() { @Override public void onResponse(Call<T> call, final Response<T> response) { //Thread switch, sub thread switch to 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); } }); } @Override public void onFailure(Call<T> call, final Throwable t) { callbackExecutor.execute(() -> callback.onFailure(ExecutorCallbackCall.this, t)); } }); }
Let's look at the enqueue in delegate
//OkHttpCall.java @Override public void enqueue(final Callback<T> callback) { Objects.requireNonNull(callback, "callback == null"); okhttp3.Call call; Throwable failure; synchronized (this) { if (executed) throw new IllegalStateException("Already executed."); executed = true; call = rawCall; failure = creationFailure; if (call == null && failure == null) { try { //In fact, it is to create the Request object of Okhttp, which calls OkHttp.call. call = rawCall = createRawCall(); } catch (Throwable t) { throwIfFatal(t); failure = creationFailure = t; } } }
4.3.2. Synchronization request
val response = category.execute()
OkhttpCall or called
@Override public Response<T> execute() throws IOException { okhttp3.Call call; synchronized (this) { if (executed) throw new IllegalStateException("Already executed."); executed = true; if (creationFailure != null) { if (creationFailure instanceof IOException) { throw (IOException) creationFailure; } else if (creationFailure instanceof RuntimeException) { throw (RuntimeException) creationFailure; } else { throw (Error) creationFailure; } } call = rawCall; if (call == null) { try { call = rawCall = createRawCall(); } catch (IOException | RuntimeException | Error e) { throwIfFatal(e); // Do not assign a fatal error to creationFailure. creationFailure = e; throw e; } } } if (canceled) { call.cancel(); } //Call execute() of OkHttpCall to send network request return parseResponse(call.execute()); } 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); } ExceptionCatchingResponseBody catchingBody = new ExceptionCatchingResponseBody(rawBody); try { //Turn the response body into a Java object 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(); throw e; } }
At this point, the Retrofit process has been very clear. In a word, it can be summarized as follows: using dynamic agent, the encapsulated request is finally handed over to the underlying OkHttp for processing.
Five, reference
Source code analysis of Android mainstream tripartite Library (2. In depth understanding of Retrofit source code)
Android: hand in hand to help you understand the source code of Retrofit 2.0