Retrofit source learning

Keywords: Retrofit OkHttp network Android

Preface

The source code of Retrofit has been learning for some time, and has recently been doubled, and then summarized. It uses many design patterns, including factory mode, agent mode, adapter mode and so on.

Create method

mRetorfit.create(Service.clss)

The return statement of the create method is as follows:

    return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
        new InvocationHandler() {
          private final Platform platform = Platform.get();

          @Override public Object invoke(Object proxy, Method method, Object[] args)
              throws Throwable {
            ......
            ServiceMethod<Object, Object> serviceMethod =
                (ServiceMethod<Object, Object>) loadServiceMethod(method);
            OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
            return serviceMethod.callAdapter.adapt(okHttpCall);
          }
        });

Using dynamic proxy, intercept the Service method to be executed each time, and then give it to three statements to execute.

loadServiceMethod(method)

  ServiceMethod<?, ?> loadServiceMethod(Method method) {
  //Getting Service Method Objects from Cache
    ServiceMethod<?, ?> result = serviceMethodCache.get(method);
    if (result != null) return result;

    synchronized (serviceMethodCache) {
      result = serviceMethodCache.get(method);
      if (result == null) {
      //Build ServiceMethod objects and cache them
        result = new ServiceMethod.Builder<>(this, method).build();
        serviceMethodCache.put(method, result);
      }
    }
    return result;
  }

This method is used to get the Service Method object, which is first retrieved from the cache, and then constructed and cached if it is not found. Ensure that only one ServiceMethod object is created for each method defined in the Service.

ServiceMethod.Builder

Builder code is more, it initializes several important objects: callAdapter, responseConverter, parameterHandlers, and parses annotations, then saves these objects for initialization when creating ServiceMethod objects.

ServiceMethod

The constructor is as follows:

  ServiceMethod(Builder<R, T> builder) {
    this.callFactory = builder.retrofit.callFactory();
    this.callAdapter = builder.callAdapter;
    this.baseUrl = builder.retrofit.baseUrl();
    this.responseConverter = builder.responseConverter;
    this.httpMethod = builder.httpMethod;
    this.relativeUrl = builder.relativeUrl;
    this.headers = builder.headers;
    this.contentType = builder.contentType;
    this.hasBody = builder.hasBody;
    this.isFormEncoded = builder.isFormEncoded;
    this.isMultipart = builder.isMultipart;
    this.parameterHandlers = builder.parameterHandlers;
  }

Several of the more important members are:

  • Call Factory: OkHttp.Call.Factory object, which defaults to OkHttpClient, is used to build Http requests and create Call objects.
  • callAdapter: A policy that converts Call < R > into T can be returned directly to Call < R > created by Factory passing in the addCallAdapter Factory method when Retrofit is created.
    • RxJava
    • Java8
    • Android
    • Guava
  • ResponsseConverter: A data converter that converts the data returned by the server into the data type we want. Created by Factory passing in the addConverterFactory method when creating Retrofit to convert the acquired data into the format we need.

OkHttpCall

OkHttpCall is a class that wraps OkHttp and initializes service method and args when created. There are also enqueue and execute methods.

CallAdapter

It is created in the createCallAdapter method in Service Method and then calls the following statement:

retrofit.callAdapter(returnType, annotations)

That is to say, according to the return type and annotations, it is created in Retrofit:

public CallAdapter<?, ?> nextCallAdapter(CallAdapter.Factory skipPast, Type returnType,
      Annotation[] annotations) {
    checkNotNull(returnType, "returnType == null");
    checkNotNull(annotations, "annotations == null");

    int start = adapterFactories.indexOf(skipPast) + 1;
    for (int i = start, count = adapterFactories.size(); i < count; i++) {
      CallAdapter<?, ?> adapter = adapterFactories.get(i).get(returnType, annotations, this);
      if (adapter != null) {
        return adapter;
      }
    }
    ......
  }

First check it, then get it through the factory class and return it. Combined with RxJava, so RxJava2CallAdapterFactory is used, so what you get here is a RxJava2CallAdapter object.

RxJava2CallAdapter#adapt()

@Override public Object adapt(Call<R> call) {
//Create the corresponding Observable object based on synchronization
    Observable<Response<R>> responseObservable = isAsync
        ? new CallEnqueueObservable<>(call)
        : new CallExecuteObservable<>(call);

    Observable<?> observable;
    if (isResult) {
      observable = new ResultObservable<>(responseObservable);
    } else if (isBody) {
      observable = new BodyObservable<>(responseObservable);
    } else {
      observable = responseObservable;
    }

    if (scheduler != null) {
      observable = observable.subscribeOn(scheduler);
    }

    if (isFlowable) {
      return observable.toFlowable(BackpressureStrategy.LATEST);
    }
    if (isSingle) {
      return observable.singleOrError();
    }
    if (isMaybe) {
      return observable.singleElement();
    }
    if (isCompletable) {
      return observable.ignoreElements();
    }
    return observable;
  }

The corresponding Observable object is created according to whether it is synchronized or not, and then returned.
When we use RxJava, we locate Observable in the method return type, then call subscribe to pass in an Observer object, that is, start subscribing; in subscribe method, we call subscribeActual method and pass in Observer object, while CallEnqueueObservable and CallExecuteObservable both implement subscribeActual method.

CallEnqueueObservable#subscribeActual

@Override protected void subscribeActual(Observer<? super Response<T>> observer) {
    // Because eachCallOnly one request can be made, so a new one needs to be cloned.Callobject
    Call<T> call = originalCall.clone();
    CallCallback<T> callback = new CallCallback<>(call, observer);
    observer.onSubscribe(callback);
    //Execute asynchronous requests
    call.enqueue(callback);
  }

The originalCall, OkHttpCall, has two important sentences in the synchronization statement in its enqueue method:

call = rawCall = createRawCall();//1
call.enqueue(...)//2
response = parseResponse(rawResponse);//3
  1. Create okhttp3.Call object:
 private okhttp3.Call createRawCall() throws IOException {
    Request request = serviceMethod.toRequest(args);
    okhttp3.Call call = serviceMethod.callFactory.newCall(request);
    if (call == null) {
      throw new NullPointerException("Call.Factory returned null.");
    }
    return call;
  }

Service Method builds a Request object by requesting parameters and annotations, and then creates the Call through the initial callFactory and returns it.

2. Call initiates a network request and parses the result
3. The parsing process is further judged by the return code. If the response is normal, the service method. toResponse (catchingBody) will be called:

R toResponse(ResponseBody body) throws IOException {
    return responseConverter.convert(body);
  }

The toResponse method calls the conversion of the Converter object and converts the returned data into the desired data format, such as GsonConverterFactory.create(), which converts the returned data into Json format and returns it, in addition to the XML parser. Converter was also created in Retrofit based on the Converter factory we set up.

So far, it has basically gone through the process of creating, acquiring and transforming data.

summary

  1. Retrofit#create()
  2. Dynamic proxy to intercept the service method to be executed
  3. Create a Service Method, which is cached during creation, and if not, created and cached. Encapsulate the incoming parameters, and parsing annotations (method annotations, parameter types, parameter annotations) into a Request, initialize responseConverter, callAdapter, callFactory
  4. Create an OkHttpCall
  5. Call the initially incoming CallAdapter adapt method
  6. The adapt method starts executing the network request, which is to create the Call object in OkHttpCall and then initiate the request (asynchronous/synchronous)
  7. Get the response result, parse it through the toResponse method of the serviceMethod object, and return it. The parsing is done through the initial responseConverter# conversion method, which converts it into the type we want.

Posted by fluxem on Thu, 16 May 2019 00:25:16 -0700