Retrofit source code parsing

Keywords: Retrofit network Android JSON

retrofit source code parsing

In recent projects, retrofit is used mostly in network framework. In a word, the use of retrofit is relatively skilled, from encapsulation to bindData of network request, it is more convenient; the annotation of retrofit and retrofit+rajava are the mainstream of android network framework.
Look at a picture below:

The following code is a simple use of retrofit: <Includes a dialog box with retrofit requests added>

 private ApiClient(Context context) {
        this.mContext = context;
        initHttpClient();
        mRetrofit = new Retrofit.Builder()
                .baseUrl(Constant.BASE_URL)//Setting baseurl parameters
                .client(mOkHttpClient)//Setting up OkHttpClient instance
                .addConverterFactory(GsonConverterFactory.create())//gson Conversion Plant
                .build();
    }

    /**
     * Creating ApiClients Objects in a Public Method
     * @return
     */
    public static ApiClient getInstance(Context context) {
        if (mApiClient == null) {
            synchronized (ApiClient.class) {
                mApiClient = new ApiClient(context);
            }
        }
        return mApiClient;
    }

    /**
     * Association Interface Definition Class
     */
    public <T> T create(Class<T> service) {
        return mRetrofit.create(service);
    }

    /**
     * Secondary Packaging Callback Interface
     * @param call
     * @param mCallBack
     * @param <T>
     */
    public <T> void show(Call<T> call, final boolean isLoading,final RetrofitListener<T> mCallBack) {

        //Display dialog box
        mCallBack.onBefor(isLoading);

        call.enqueue(new Callback<T>() {
            @Override
            public void onResponse(Call<T> call, retrofit2.Response<T> response) {
                mCallBack.onAfter(isLoading);
                mCallBack.onResponse(call, response);
            }

            @Override
            public void onFailure(Call<T> call, Throwable t) {
                mCallBack.onAfter(isLoading);
                mCallBack.onFailure(call, t);
            }
        });
    }
    The general process of retrofit: First, create an interface. The second step is to create a Retrofit object that provides information such as BASE_URL. The third step is to create a proxy object that implements the interface. The fourth step is to call the interface method and return a Call object. The fifth step is to call execute to execute the synchronization request. The sixth step is to get the data from the response. Bid the data.

    ok~Start looking at the source code of retrofit:

1

    // new Retrofit.Builder() First look at the Builder class
public static final class Builder {
    ...
    Builder(Platform platform) {
      this.platform = platform;
      converterFactories.add(new BuiltInConverters());
    }

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

    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;
    }
    public Builder baseUrl(String baseUrl) {
      checkNotNull(baseUrl, "baseUrl == null");
      HttpUrl httpUrl = HttpUrl.parse(baseUrl);
      if (httpUrl == null) {
        throw new IllegalArgumentException("Illegal URL: " + baseUrl);
      }
      return baseUrl(httpUrl);
    }

    public Builder baseUrl(HttpUrl baseUrl) {
      checkNotNull(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;
    }

    public Builder addConverterFactory(Converter.Factory factory) {
      converterFactories.add(checkNotNull(factory, "factory == null"));
      return this;
    }

    public Builder addCallAdapterFactory(CallAdapter.Factory factory) {
      adapterFactories.add(checkNotNull(factory, "factory == null"));
      return this;
    }

    public Builder callbackExecutor(Executor executor) {
      this.callbackExecutor = checkNotNull(executor, "executor == null");
      return this;
    }

    public Builder validateEagerly(boolean validateEagerly) {
      this.validateEagerly = validateEagerly;
      return this;
    }

    public Retrofit build() {
      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();
      }

      List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories);
      adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));

      List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories);

      return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
          callbackExecutor, validateEagerly);
    }
  }
    1.new Retrofit.Builder() is to obtain platform information and add built-in conversion factories to the collection of factories.
    2: From our basic example, we can see that there is a method called to. baseUrl(BASE_URL). In fact, when Retrofit is not used, this method must be passed in, and it can not be empty. From the source code, we can see that when the parameters passed in by the baseUrl method are empty, NullPointerException null pointer exception will be thrown.

    3: The addConverter Factory method is imported into a converter factory. It is mainly used for data conversion. The data requested by the network will be converted into the data type we need here, such as converting json data into object type through Gson.

    4: From the source code, we can see that there is another client method, which is optional. If there is no input, the default is OkHttpClient. Here we can do some operations on OkHttpClient, such as adding interceptors to print log s, etc.

    5: Callback Executor This method can be seen from the name that it should be the callback executor, that is, the Call object is converted to the UI main thread after obtaining data from the network service.

    6: The addCallAdapterFactory method is mainly for Call transformation, such as support for Rxjava, from the returned call object to the Observable object.

    7: Finally, call the build() method and pass the required object to the Retrofit object through the new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,callbackExecutor, validateEagerly).

2.

// mRetrofit. create (xxxxApi. class// annotation class)

public <T> T create(final Class<T> service) {
    Utils.validateServiceInterface(service);
    if (validateEagerly) {
      eagerlyValidateMethods(service);
    }
    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, @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);
            return serviceMethod.callAdapter.adapt(okHttpCall);
          }
        });
  }
First, xxxxApi api = mRetrofit. create (xxxxApi. class// annotation class) returns a dynamic proxy object (which is explained separately after the dynamic proxy), and then invokes the method in the annotation class with the dynamic proxy object to obtain the request:
Call<AppointInfo> appointInfo = responseInfoApi.getAppointInfo("APPAPI","123456", code, data);
    Here, responseInfoApi. getAppointInfo ("APPAPI", "123456", "code, data"); goes the invoke method in Proxy. new Proxy Instance, which is equivalent to dynamic interception, and then calls;
    Of course, the core code is:
 ServiceMethod serviceMethod = loadServiceMethod(method);
 OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
 return serviceMethod.callAdapter.adapt(okHttpCall);
    Obtain service method object through method - > Obtain okHttpCall object again, and continue encapsulating okHttpCall to return call object through service method. callAdapter. adapt, that is, request object.
    Let's look at the load Service Method:
ServiceMethod loadServiceMethod(Method method) {
    ServiceMethod result;
    synchronized (serviceMethodCache) {
      result = serviceMethodCache.get(method);
      if (result == null) {
        result = new ServiceMethod.Builder(this, method).build();
        serviceMethodCache.put(method, result);
      }
    }
    return result;
  }
    In fact, there is a service method Cache in the load Service Method, which is a caching queue of service method. First, the corresponding service method is obtained from the caching queue through the method. If it is empty, the service method is obtained through the new Service Method. Builder (this, method). build (); and the service method is also obtained, and the service method Cache. put (method, result);
    Then look at the new Service Method. Builder (this, method). build () source code
public Builder(Retrofit retrofit, Method method) {
      this.retrofit = retrofit;
      this.method = method;
      this.methodAnnotations = method.getAnnotations();
      this.parameterTypes = method.getGenericParameterTypes();
      this.parameterAnnotationsArray = method.getParameterAnnotations();
    }

public ServiceMethod build() {
      callAdapter = createCallAdapter();
      responseType = callAdapter.responseType();
      if (responseType == Response.class || responseType == okhttp3.Response.class) {
        throw methodError("'"
            + Utils.getRawType(responseType).getName()
            + "' is not a valid response body type. Did you mean ResponseBody?");
      }
      responseConverter = createResponseConverter();
      ...
      return new ServiceMethod<>(this);
    }
    As you can see, some parameters are initialized in builder and service method is returned in builder.
    Then look at the build method, //createCallAdapter();
private CallAdapter<?> createCallAdapter() {
      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 {
        return 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);
      }
    }
    build is to get the type of method and the annotations:
Type returnType = method.getGenericReturnType();
    Then call retrofit. callAdapter (return Type, annotations);
public CallAdapter<?> callAdapter(Type returnType, Annotation[] annotations) {
    return nextCallAdapter(null, returnType, annotations);
  }

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;
      }
    }
    ...
}
Finally, call nextCallAdapter,for traversing adapter Fractory to get the callAdapter object, then get the callAdapter object from the builder constructor:
public static final class Builder {
    ...
    public Builder addCallAdapterFactory(CallAdapter.Factory factory) {
      adapterFactories.add(checkNotNull(factory, "factory == null"));
      return this;
    }
    ...
    public Retrofit build() {
      List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories);
      adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));
    ...
    }  
  }

CallAdapter.Factory defaultCallAdapterFactory(Executor callbackExecutor) {
    if (callbackExecutor != null) {
      return new ExecutorCallAdapterFactory(callbackExecutor);
    }
    return DefaultCallAdapterFactory.INSTANCE;
  }
    // Through service method, the second step of getting okHttpCall object from args is relatively simple, that is, object transfer:
OkHttpCall(ServiceMethod<T> serviceMethod, Object[] args) {
    this.serviceMethod = serviceMethod;
    this.args = args;
  }
    // Service Method. CallAdapter. Adapt (okHttpCall), after obtaining the okHttpCall object, encapsulates it as a call object, requests as request, and checks the adapt source code.
 T adapt(Call<R> call);
    Simply put, there's no clue. Then look at service method. callAdapter:
@Override
  public CallAdapter<Call<?>> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
    if (getRawType(returnType) != Call.class) {
      return null;
    }
    final Type responseType = Utils.getCallResponseType(returnType);
    return new CallAdapter<Call<?>>() {
      @Override public Type responseType() {
        return responseType;
      }

      @Override public <R> Call<R> adapt(Call<R> call) {
        return new ExecutorCallbackCall<>(callbackExecutor, call);
      }
    };
  }
Return new Executor Callback Call <> (call back Executor, call); that's the strange code above; so you get the call object and start the next request;

3.

    Asynchronization is divided into synchronization and asynchronization. Here asynchronization is taken as an example <asynchronization is commonly used>, //call.enqueue(new Callback<T>(), which is implemented as follows:
service.enqueue(new Callback<List<User>>() {
    @Override
    public void onResponse(Call<List<User>> call, Response<List<User>> response) {
        Log.d("response body",response.body());
    }

    @Override
    public void onFailure(Call<BoardInfo> call, Throwable t) {
        Log.i("response Throwable",t.getMessage().toString());
    }
});
    Obtaining the intercept from the above agent shows that the Call object is Executor CallbackCall:
static final class ExecutorCallbackCall<T> implements Call<T> {
    final Executor callbackExecutor;
    final Call<T> delegate;

    ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) {
      this.callbackExecutor = callbackExecutor;
      this.delegate = delegate;
    }

    @Override public void enqueue(final Callback<T> callback) {
      if (callback == null) throw new NullPointerException("callback == null");

      delegate.enqueue(new Callback<T>() {
        @Override public void onResponse(Call<T> call, final Response<T> response) {
          callbackExecutor.execute(new Runnable() {
            @Override public void run() {
              if (delegate.isCanceled()) {
                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(new Runnable() {
            @Override public void run() {
              callback.onFailure(ExecutorCallbackCall.this, t);
            }
          });
        }
      });
    }
    As can be seen from the above code, two interfaces are defined in Executor CallbackCall
  ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) {
      this.callbackExecutor = callbackExecutor;
      this.delegate = delegate;
    }
    Whereas callbackExecutor completes thread switching, delegate is the main body to execute asynchronous requests.
 delegate.enqueue(new Callback<T>() {
        @Override public void onResponse(Call<T> call, final Response<T> response) {
          callbackExecutor.execute(new Runnable() {
            @Override public void run() {
              if (delegate.isCanceled()) {
                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(new Runnable() {
            @Override public void run() {
              callback.onFailure(ExecutorCallbackCall.this, t);
            }
          });
        }
      });
    }
    Next, see how delegate executes asynchronous requests:
@Override 
public void enqueue(final Callback<T> callback) {
    if (callback == null) throw new NullPointerException("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 {
          call = rawCall = createRawCall();
        } catch (Throwable t) {
          failure = creationFailure = t;
        }
      }
    }

    if (failure != null) {
      callback.onFailure(this, failure);
      return;
    }

    if (canceled) {
      call.cancel();
    }

    call.enqueue(new okhttp3.Callback() {
      @Override 
      public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse)
          throws IOException {
        Response<T> response;
        try {
          response = parseResponse(rawResponse);
        } catch (Throwable e) {
          callFailure(e);
          return;
        }
        callSuccess(response);
      }

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

      private void callFailure(Throwable e) {
        try {
          callback.onFailure(OkHttpCall.this, e);
        } catch (Throwable t) {
          t.printStackTrace();
        }
      }

      private void callSuccess(Response<T> response) {
        try {
          callback.onResponse(OkHttpCall.this, response);
        } catch (Throwable t) {
          t.printStackTrace();
        }
      }
    });
  }
    In fact, it's not hard to see that okhttp is encapsulated. What's unclear at the end of the encapsulation is that we can continue to study the source code.      

Posted by greatme on Tue, 18 Jun 2019 16:51:53 -0700