Retrofit has made Android web requests incredibly easy and works well with the REST interface behind the scenes. It’s worth exploring how it works.

The use of the Retrofit

Usually we use Retrofit in conjunction with Rxjava. We won’t do too much research on usage here, but on principle, so the code below is Retrofit’s own API, not Rxjava.

Here is a normal GET request

1. Create an interface

Create API. Java file:

public interface API {
    @GET("Request address, but not server address")
    Call<Response> get(
                            @Query("param1") String param1,// The first argument
                            @Query("param2") int param2);// The second argument
}
Copy the code

In the @get annotation, add the request address to remove the server link. In the @query annotation, add the request parameter name.

2. Create the Retrofit service and request client

Create a new singleton class, RetrofitService.java(optionally named), and define a static OkHttpClient in it

private RetrofitService(a) {// Privatize the constructor
    }

public static RetrofitService getInstance(a) {// Double check
        if (instance == null) {
            synchronized (RetrofitService.class) {
                if (instance == null) {
                    instance = newRetrofitService(); }}}return instance;
    }

private static OkHttpClient mOkHttpClient;

private static void initOkHttpClient(a) {
    HttpLoggingInterceptor logInterceptor = new HttpLoggingInterceptor();// Log interceptor
    mOkHttpClient = new OkHttpClient.Builder().cache(cache)
                            .addInterceptor(logInterceptor)// Log interceptor, as required
                            .connectTimeout(10, TimeUnit.SECONDS).build();// Connection timeout time
}
Copy the code

Provide a method to retrieve the API interface

private volatile static API aPI = null;
    public static API createAPI(a) {
        if (aPI == null) {
            synchronized (RetrofitService.class) {
                if (aPI == null) {
                    initOkHttpClient();
                    aPI = new Retrofit.Builder()
                            .client(mOkHttpClient)
                            .baseUrl("Server address")
                            .addConverterFactory(GsonConverterFactory.create())// Specify the JSON processing library
                            .build().create(API.class);// Pass in the API created in the first step}}}return aPI;
    }
Copy the code

3. The request is sent

Call<Response> = call = RetrofitService.getInstance()
                .createShowAPI()
                .get(Parameters of "1"."Parameter 2");
                
call.enqueue(new Callback<Response>() {
            @Override
            public void onResponse(Call<Response> call, Response<Response> response) {
                // Request successful processing
            }

            @Override
            public void onFailure(Call<ShowApiResponse<ShowApiNews>> call, Throwable t) {
                // Failed processing of the request}});Copy the code

The core of Retrofit – dynamic agents

How does Retrofit take the interface methods that we defined and turn them into requests and send them out, so let’s go to the source code here

Creator mode

Let’s start with the creation of Retrofit, which uses the creator pattern

new Retrofit.Builder()
    .client(mOkHttpClient)
    .baseUrl("Server address")
    .addConverterFactory(GsonConverterFactory.create())// Specify the JSON processing library
    .build().create(API.class);// Pass in the API created in the first step
Copy the code

Start by looking at the constructor of the retrofit.Builder () class

public static final class Builder {
    public Builder(a) {
      this(Platform.get()); }}Copy the code

Here we call a constructor with arguments. What is platform.get ()

  private static final Platform PLATFORM = findPlatform();

  static Platform get(a) {
    return PLATFORM;
  }
  
Copy the code

Platform.findPlatform

  private static Platform findPlatform(a) {
    try {
      Class.forName("android.os.Build");
      if(Build.VERSION.SDK_INT ! =0) {
        return newAndroid(); }}catch (ClassNotFoundException ignored) {
    }
    try {
      Class.forName("java.util.Optional");
      return new Java8();
    } catch (ClassNotFoundException ignored) {
    }
    return new Platform();
  }
Copy the code

As you can see, the Platform class, as the name implies, is actually a Platform. Two platforms are built into Retrofit, Android and Java8. Different platforms handle it differently. Keep looking at the code

  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

We won’t delve into that here, but we’ll come back, but we’ve already seen that the Executor class on the Android platform returns the MainThreadExecutor, which provides a handler, and that handler is the Looper of the main thread that was passed in. So in the execute method, handler.post is actually executed on the main thread (UI thread).

Go back to retrofit.builder () and look at the constructor with parameters:

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

    Builder(Retrofit retrofit) {
      platform = Platform.get();
      callFactory = retrofit.callFactory;
      baseUrl = retrofit.baseUrl;
      converterFactories.addAll(retrofit.converterFactories);
      adapterFactories.addAll(retrofit.adapterFactories);
      // Remove the default, platform-aware call adapter added by build().
      adapterFactories.remove(adapterFactories.size() - 1);
      callbackExecutor = retrofit.callbackExecutor;
      validateEagerly = retrofit.validateEagerly;
    }
Copy the code

In this constructor, various properties are initialized. Let’s look at the definitions of these properties

    private final Platform platform;// The platform you just saw is Android
    private @Nullable okhttp3.Call.Factory callFactory;//
    private HttpUrl baseUrl;// Server address
    private final List<Converter.Factory> converterFactories = new ArrayList<>();//json parses the factory list
    private final List<CallAdapter.Factory> adapterFactories = new ArrayList<>();//
    private @Nullable Executor callbackExecutor;// This is an Android Executor that executes a callback on the main thread
    private boolean validateEagerly;// Whether the flag was created in advance
Copy the code

Take a look at each of these attributes, some of which we’ll find out later, but most of which have some quirks in their names: The main ones are the callFactory and adapterFactories, which we don’t know what they do for now. After the constructor initializes, we call the Builder.build() method

Builder.build()

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

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

      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));

      // Make a defensive copy of the converters.
      List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories);

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

In this case, we pass the Builder property to the Retrofit constructor to see how the two properties are assigned:

callFactory

okhttp3.Call.Factory callFactory = this.callFactory;
      if (callFactory == null) {
        callFactory = new OkHttpClient();
      }
Copy the code

The callFactory is, by default, an OkHttpClient, which means Retrofit’s internal request principle is actually OkHttp. Remember when we first created it we also passed in a static class OkHttpClient. What’s the connection?

new Retrofit.Builder().client(mOkHttpClient)
Copy the code

Retrofit.Builder().client

    public Builder client(OkHttpClient client) {
      return callFactory(checkNotNull(client, "client == null"));
    }
    
    public Builder callFactory(okhttp3.Call.Factory factory) {
      this.callFactory = checkNotNull(factory, "factory == null");
      return this;
    }
    
Copy the code

The OkHttpClient is also assigned to the callFactory, so the callFactory is the OkHttp network request client

adapterFactories

// 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

Create a new list and add the default CallAdapterFactory. We know that Platform is Android, so look at the previous code:

Platform.Android

  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

ExecutorCallAdapterFactory defaultCallAdapterFactory returns

final class ExecutorCallAdapterFactory extends CallAdapter.Factory {
  final Executor callbackExecutor;

  ExecutorCallAdapterFactory(Executor callbackExecutor) {
    this.callbackExecutor = callbackExecutor; }}Copy the code

You can see that the constructor does nothing but pass in the callback handler class. We’ll look at the other methods in there later when we call them. So we still don’t know what the adapterFactories are, but we see that it has an enqueue method and a bunch of methods for handling responses, so we know that it handles requests and responses. See the source code for how it works.

Retrofit.creat

After the creator initializes all the attributes, the retrofit.creat method comes up

aPI = new Retrofit.Builder()
    .client(mOkHttpClient)
    .baseUrl("Server address")
    .addConverterFactory(GsonConverterFactory.create())// Specify the JSON processing library
    .build().create(API.class);
Copy the code

Retrofit.creat

  public <T> T create(final Class<T> service) {
    Utils.validateServiceInterface(service);// Verify that the incoming class is an interface class
    if (validateEagerly) {// Create it ahead of time, default is false, skip here
      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 {
            // The class in which the method is defined, where we define it in the interface, returns false
            if (method.getDeclaringClass() == Object.class) {
              return method.invoke(this, args);
            }
            / / platform. IsDefaultMethod didn't do any processing, direct return false
            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

Here you see the heart of Retrofit, which uses dynamic proxies to handle the methods we define in the interface. When we call our defined interface method, we come to the invoke method of the dynamic proxy class and execute the last three lines, where the defined interface method is parsed and processed accordingly.

conclusion

There were a couple of things we needed to be aware of when Retrofit was created

platform

This is a Retrofit supported platform with Android and Java8 in it, Android in this case

callFactory

The client that performs the request, in this case OkHttpClient, is passed in at creation time. Client

converterFactories

Json parse processing factory array, in this case GsonConverterFactory. Request and response parsing, converting JSON strings into concrete entity classes

callAdapterFactories

The details of request and response handling adapter factory array, there is no default value is ExecutorCallAdapterFactory, if you need to use rxjava, RxJava2CallAdapterFactory

callbackExecutor

This is the default MainThreadExecutor for the Android platform. It uses a Handler to handle callbacks in the main thread.