retrofit 2.6.1 Source Parsing

Posted by moallam on Mon, 12 Aug 2019 12:59:16 +0200

Articles Catalogue


Retrofit2 github address

Explanation of examples

Retrofit is introduced, routine operation.

	implementation 'com.squareup.retrofit2:retrofit:2.6.1'
    implementation 'com.squareup.retrofit2:converter-gson:2.6.1'
    implementation 'com.squareup.retrofit2:adapter-rxjava:2.6.1'

Interface class

public interface GitHubService {

    @GET("users/{user}/repos")
    Call<List<Repo>> listRepos(@Path("user") String user);

    @HTTP(method = "DELETE", path = "remove/", hasBody = true)
    Call<ResponseBody> deleteObject(@Body RequestBody object);
}

Make network requests

 Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("https://api.github.com/")
                .client(new OkHttpClient())
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                .addConverterFactory(GsonConverterFactory.create(new Gson()))
                .build();
        GitHubService service = retrofit.create(GitHubService.class);
        Call<List<Repo>> repos = service.listRepos("octocat");
        repos.enqueue(new Callback<List<Repo>>() {
            @Override
            public void onResponse(Call<List<Repo>> call, Response<List<Repo>> response) {
              
            }

            @Override
            public void onFailure(Call<List<Repo>> call, Throwable t) {

            }
        });

The process consists of three steps.

  • Create a Retrofit instance, interface
  • Examples of interfaces obtained by dynamic proxy
  • Call interface, request network

Source code parsing

Create Retrofit instances (constructor pattern)

  • .url("baseUrl"): Converts baseUrl of String type to HttpUrl type

  • .client(OkHttpClient): Supports custom OkHttpClient. OkHttpClient implements Call.Factory interface and constructs Call request object through new Call (request)

  • CallbackExecutor (): Provided by the Android platform, MainThreadExecutor, sends messages to the handler of the main thread

  • AddCallAdapterFactory (): When adapterFactories add a custom RxJava2CallAdapterFactory, a default ExecutorCallAdapterFactory() will be added to the system.

  • addConverterFactory(): converterFactories will add a custom GsonConverterFactory, which has previously been added to Builder() as the default BuiltInConverters()

  • platform, supporting Android and Java 8

public final class Retrofit {
  private final Map<Method, ServiceMethod<?>> serviceMethodCache = new ConcurrentHashMap<>();

  final okhttp3.Call.Factory callFactory; //The corresponding okhttpClient is used to construct the request object call and make network requests.
  final HttpUrl baseUrl;
  final List<Converter.Factory> converterFactories; //Collection of data converter factories
  final List<CallAdapter.Factory> callAdapterFactories; //Collection of network request adapter factories
  final @Nullable Executor callbackExecutor; //The callback executor of the main thread is used in conjunction with Executor Call Adapter Factory
  final boolean validateEagerly;

public static final class Builder {
    private final Platform platform; // Support for android and Java 8
 	//The remaining member variables are consistent with retrofit
    public Builder() {
      this(Platform.get());
    }

public Retrofit build() {
      //Initialization of Eliminating Other Membership Variables
      //CallAdapter Factories adds user-defined and platform-related additions
      List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
      callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor));

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

Acquisition of PlatForm

Support for Android Platform and Java 8, using Policy Mode and Singleton Mode

Android platform, providing default network request adapter, data converter, handler for main thread

  • Provide defaultCallAdapter Factories
  • Provide defaultConverter Factories
  • Provide defaultCallbackExecutor
class Platform {
//Singular Patterns of Bad Men
  private static final Platform PLATFORM = findPlatform();

  static Platform get() {
    return PLATFORM;
  }

  private static Platform findPlatform() {
    try {
      Class.forName("android.os.Build");
      if (Build.VERSION.SDK_INT != 0) {
        return new Android();//Class loading method to determine whether there are platform-related classes
      }
    } catch (ClassNotFoundException ignored) {
    }
    try {
      Class.forName("java.util.Optional");
      return new Java8();
    } catch (ClassNotFoundException ignored) {
    }
    return new Platform();
  }

  @Nullable Executor defaultCallbackExecutor() {
    return null;
  }

  @IgnoreJRERequirement // Only classloaded and used on Java 8.
  static class Java8 extends Platform { //Content ellipsis
  }

  static class Android extends Platform {
    @IgnoreJRERequirement // Guarded by API check.
    @Override boolean isDefaultMethod(Method method) {
      if (Build.VERSION.SDK_INT < 24) {
        return false;
      }
      return method.isDefault();
    }

    @Override public Executor defaultCallbackExecutor() {
      return new MainThreadExecutor();
    }

    @Override List<? extends CallAdapter.Factory> defaultCallAdapterFactories(
        @Nullable Executor callbackExecutor) {
      if (callbackExecutor == null) throw new AssertionError();
      DefaultCallAdapterFactory executorFactory = new DefaultCallAdapterFactory(callbackExecutor);
      return Build.VERSION.SDK_INT >= 24
        ? asList(CompletableFutureCallAdapterFactory.INSTANCE, executorFactory)
        : singletonList(executorFactory);
    }

    @Override int defaultCallAdapterFactoriesSize() {
      return Build.VERSION.SDK_INT >= 24 ? 2 : 1;
    }

    @Override List<? extends Converter.Factory> defaultConverterFactories() {
      return Build.VERSION.SDK_INT >= 24
          ? singletonList(OptionalConverterFactory.INSTANCE)
          : Collections.<Converter.Factory>emptyList();
    }

    @Override int defaultConverterFactoriesSize() {
      return Build.VERSION.SDK_INT >= 24 ? 1 : 0;
    }

//Switch to the main thread to execute the runnable object
    static class MainThreadExecutor implements Executor {
      private final Handler handler = new Handler(Looper.getMainLooper());

      @Override public void execute(Runnable r) {
        handler.post(r);
      }
    }
  }
}

An example of dynamic proxy getting interface

  • Dynamic proxy is not explained for the time being
  • LoadService Method () obtains a Service Method object and calls back the invoke() abstract method of service Method
  • Service Method Cache preserves the mapping relationship between method and Service Method
public <T> T create(final Class<T> service) {
    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 method is a method in Object.class, it executes directly and returns
            if (method.getDeclaringClass() == Object.class) {
              return method.invoke(this, args);
            }
            if (platform.isDefaultMethod(method)) { 
              return platform.invokeDefaultMethod(method, service, proxy, args);
            }
            //Normal network requests go down here
            return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
          }
        });
  }
Acquisition of service method
private final Map<Method, ServiceMethod<?>> serviceMethodCache = new ConcurrentHashMap<>();

ServiceMethod<?> loadServiceMethod(Method method) {
    ServiceMethod<?> result = serviceMethodCache.get(method);
    if (result != null) return result; //Trying to fetch from the cache

    synchronized (serviceMethodCache) {
      result = serviceMethodCache.get(method);
      if (result == null) {
        result = ServiceMethod.parseAnnotations(this, method);//Static method
        serviceMethodCache.put(method, result); //Build the serviceMethod object and put it in the cache
      }
    }
    return result;
  }
abstract class ServiceMethod<T> {
  static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
  	//Resolve the annotations (httpMethod/URL/header/contentType) and parameters in the interface to construct the Request object
    RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);

    return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
  }

  abstract @Nullable T invoke(Object[] args); //invoke method
}

getServiceMethod(method)

  • HttpService Method < ResponseT, Return T > is inherited from Service Method < Return T >
  • CallAdapted < ResponseT, Return T > inherits from HttpService Method < ResponseT, Return T >
  • That is to say, getService Method (method), which actually returns the CallAdapter object.

getServiceMethod(method).invoke(args)

  • A Call < ResponseT > object, OkHttpCall, is constructed, which is a Call in retrofit and a Call in non-okHttp.
  • Call the CallAdapted method adapt(call, args)
  • In fact, callAdapter.adapt(call) is called to convert the Call < ResponseT > object into a Return T object.
abstract class HttpServiceMethod<ResponseT, ReturnT> extends ServiceMethod<ReturnT> {
  /**
   * Inspects the annotations on an interface method to construct a reusable service method that
   * speaks HTTP. This requires potentially-expensive reflection so it is best to build each service
   * method only once and reuse it.
   */
  static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
      Retrofit retrofit, Method method, RequestFactory requestFactory) {
    //Get the callAdapter object, using custom, or built-in
  	Type adapterType = method.getGenericReturnType();
    CallAdapter<ResponseT, ReturnT> callAdapter =
        createCallAdapter(retrofit, method, adapterType, annotations);
    //Get the responseConver object, using custom, or built-in
    Type responseType = callAdapter.responseType();
    Converter<ResponseBody, ResponseT> responseConverter =
        createResponseConverter(retrofit, method, responseType);
	//Obtain okHttpClient object, okHttpClient implements okhttp3.Call.Factory
    okhttp3.Call.Factory callFactory = retrofit.callFactory;
    return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);
  }

 @Override final @Nullable ReturnT invoke(Object[] args) {
    Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
    return adapt(call, args); //Constructing OkHttpCall object and adapting it through callAdapter
  }

  protected abstract @Nullable ReturnT adapt(Call<ResponseT> call, Object[] args);

//Real Service Method
  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) {
      return callAdapter.adapt(call); //callAdapter for adaptation
    }
  }
}

Draw the key points again:
1. Either Executor Call Adapter or RxJava Call Adapter eventually calls call.execute(), call.enqueue(callback)

2. call is actually OkHttpCall (Call wrapper class) created in Invocation Handler based on service method and args.

3. So OkHttpCall is?

Constructing OkHttpCall Object
  • OkHttpCall is a wrapping of Call objects in okHttp

  • execute() and enqueue() are constructed by createRawCall(), okHttpClient.newCall(request).

  • After the response is obtained, parseResponse() is used to parse okhttp3.Response, which is Response < T >.

(That is to say, Retrofit is only the encapsulation of the network interface, and the final implementation is achieved by okHttpClient)

final class OkHttpCall<T> implements Call<T> {


  OkHttpCall(RequestFactory requestFactory, Object[] args,
      okhttp3.Call.Factory callFactory, Converter<ResponseBody, T> responseConverter) {
    this.requestFactory = requestFactory;
    this.args = args;
    this.callFactory = callFactory;
    this.responseConverter = responseConverter;
  }
  
	//Executing Asynchronous Tasks
  @Override public void enqueue(final Callback<T> callback) {
    okhttp3.Call call;
  	call = rawCall = createRawCall();
    call.enqueue(new okhttp3.Callback() {
      @Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
        Response<T> response;
     	response = parseResponse(rawResponse);
   		callback.onResponse(OkHttpCall.this, response);
     }

      @Override public void onFailure(okhttp3.Call call, IOException e) {
          callback.onFailure(OkHttpCall.this, e);
    });
  }

//Perform synchronization tasks
  @Override public Response<T> execute() throws IOException {
    okhttp3.Call call;
	call = rawCall = createRawCall();
    return parseResponse(call.execute());
  }

//Build an okhttp3.Call, request object, constructed by okHttpClient.newCall(request)
  private okhttp3.Call createRawCall() throws IOException {
    okhttp3.Call call = callFactory.newCall(requestFactory.create(args));
    return call;
  }

//Resolve okhttp3.Response for Response < T >
  Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
    ResponseBody rawBody = rawResponse.body();
   	T body = responseConverter.convert(rawBody );
  }

}

Topics: Retrofit Android network Java