preface

Since the previous project was built with MVP architecture, which is a combination of RxJava + Glide + OKHttp + Retrofit and other open source frameworks, it was just on the use level without in-depth research. Recently, we plan to attack all of them. Students who have not paid attention to them can pay attention to a wave first. After reading this series of articles, you’ll be much more comfortable handling questions (whether in an interview or on the job) if you know how they work.

Android picture loading framework Glide 4.9.0 (a) From the perspective of source code analysis Glide execution process

Android Image loading framework Glide 4.9.0 (2) Analysis of Glide cache strategy from the perspective of source code

From the perspective of source code analysis of Rxjava2 basic execution process, thread switching principle

OKHttp3 (a) synchronous and asynchronous execution process is analyzed from the perspective of source code

Analyze the charm of OKHttp3 (ii) interceptor from the source point of view

Analyze OKHttp3 cache strategy from source code perspective

Analyze Retrofit network request from source code point of view, including RxJava + Retrofit + OKhttp network request execution process

introduce

Retrofit and OKHttp are both open source projects from Square, and both Retrofit and OKHttp are part of the HTTP Web request framework, so why should we analyze it if both are part of the web framework? For those of you who have used the Retrofit framework, the underlying version ofit is actually OKHttp based for web access. Strictly speaking, I personally think Retrofit is based on OKHttp repackaging, which is intended to be simpler to use.

Simple to use

This chapter focuses on analyzing the Retrofit source code, so if you’re unsure about how to use the Retrofit API, Here are to hong ocean great god wanandroid open API: wanandroid.com/wxarticle/c… To make network requests:

Define the Retrofit network request interface

public interface APIService {
  
  	// Define the interface Retrofit + OKHttp network access according to Retrofit requirements
    @GET("/wxarticle/chapters/json")
    Call<JsonObject> getWXarticle(a);
  
  	// Define interface RxJava + Retrofit + OKHttp network access according to Retrofit requirements
    @GET("/wxarticle/chapters/json")
    Observable<JsonObject> getWXarticle(a);
}

Copy the code

Initialize the Retrofit instance

  public static final String API_URL = "https://wanandroid.com";

	//1. Configure OKHttpClient to add view Request/Response data interceptor
	OkHttpClient okHttpClient = new OkHttpClient.Builder().addInterceptor(new Interceptor() {
            @Override
            public Response intercept(Chain chain) throws IOException {
              	// Prints the request data before the request
                Request request = chain.request();
                Log.d(TAG, request.toString());
              
              	// Prints the server response data
                Response response = chain.proceed(request);
                MediaType mediaType = response.body().contentType();
                String content = response.body().string();
                Log.d("JSON:", content);
                Log.d("MediaType:", mediaType.toString());
                return response.newBuilder().
                        body(ResponseBody.create(mediaType, content)).
                        build();
            }
        }).build();

		//2. Build Retrofit objects
		Retrofit retrofit = new Retrofit.Builder()
            .baseUrl(API_URL) //http API
            .addConverterFactory(GsonConverterFactory.create()) // Configure GSON transformation
            .addCallAdapterFactory(RxJava2CallAdapterFactory.createAsync()) // Configure RxJava to support asynchronous access to create synchronization
            .client(okHttpClient)// Proactively configure OKhttpClient
            .build();

	//3. Get the Retrofit request object
	APIService api = retrofit.create(APIService.class);
Copy the code

Retrofit + OKHttp

  Call<JsonObject> call = api.getWXarticles();
	//OKHttp asynchronously accesses the network
  call.enqueue(new Callback<JsonObject>() {
  @Override
  public void onResponse(Call<JsonObject> call, retrofit2.Response<JsonObject> response) {
  	Log.d(TAG, "Retrofit + OKHttp "+response.body().toString());
  }

  @Override
  public void onFailure(Call<JsonObject> call, Throwable t) {
  	Log.d(TAG, "Retrofit + OKHttp "+t.getMessage()); }});Copy the code

RxJava + Retrofit + OKHttp

  Observable<JsonObject> observable = api.getWXarticle();
  observable.subscribeOn(Schedulers.io())
                .subscribeOn(AndroidSchedulers.mainThread())
                .subscribe(new Observer<JsonObject>() {
                    @Override
                    public void onSubscribe(Disposable d) {
                        Log.d(TAG, "RxJava + Retrofit + OKHttp subscription succeeded");
                    }

                    @Override
                    public void onNext(JsonObject s) {
                        Log.d(TAG, "RxJava + Retrofit + OKHttp "+s.toString());
                    }

                    @Override
                    public void onError(Throwable e) {
                        Log.d(TAG, "RxJava + Retrofit + OKHttp "+e.getMessage());
                    }

                    @Override
                    public void onComplete(a) {
                        Log.d(TAG, "RxJava + Retrofit + OKHttp onComplete"); }});Copy the code

Output result:

/ / request address com. Dn_alan. Myapplication. MainActivity: https://wanandroid.com/wxarticle/chapters/json/Request/Request information com. Dn_alan. Myapplication. MainActivity: Request{method=GET, url=https://wanandroid.com/wxarticle/chapters/json, Tags = {class retrofit2. Invocation = com. Dn_alan. Myapplication. APIService. GetWXarticles () []}}; / / Rxjava subscribing com. Dn_alan. Myapplication. MainActivity: Retrofit RxJava + + OKHttp subscribing / / Retrofit RxJava + + OKHttp server returned data D/com. Dn_alan. Myapplication. MainActivity: RxJava + Retrofit + OKHttp {"data":[{"children":[],"courseId":13," ID ":408,"name":"....} //RxJava + Retrofit + OKHttp Network request is completed D/com. Dn_alan. Myapplication. MainActivity: Retrofit RxJava + + OKHttp onComplete / / Retrofit + OKHttp network request is completed D/com. Dn_alan. Myapplication. MainActivity: Retrofit + OKHttp {"data":[{"children":.....}Copy the code

We can see that RxJava + Retrofit + OKHttp, Retrofit + OKHttp were successfully requested by printing, and we can see that Retrofit will be splicing together by requesting the address. This use is not very cool. If you are not familiar with the source code for RxJava and OKHttp, I suggest you take a look at my previous articles and review them, because I will cover them in this chapter, but I won’t go through them in detail, because the focus is on Retrofit.

Here’s where we really start to look at how Retrofit works with OKHttp and RxJava for web access.

Source code analysis

Retrofit class introduction

packageretrofit2; .public final class Retrofit {...// omit the code

  Retrofit(okhttp3.Call.Factory callFactory, HttpUrl baseUrl,
      List<Converter.Factory> converterFactories, List<CallAdapter.Factory> callAdapterFactories,
      @Nullable Executor callbackExecutor, boolean validateEagerly) {
    this.callFactory = callFactory; // Configure OKhttp
    this.baseUrl = baseUrl;// The configured Http address
    this.converterFactories = converterFactories; // Configure transformation functions such as GSON,
    this.callAdapterFactories = callAdapterFactories; // Configure RxJava
    this.callbackExecutor = callbackExecutor;
    this.validateEagerly = validateEagerly;
  }
  
  @SuppressWarnings("unchecked") // Single-interface proxy creation guarded by parameter safety.
  public <T> T create(final Class<T> service) {...// The main code for dynamic proxy is explained later}...// Omit some code
  
  public static final class Builder {
    private final Platform platform;
    private @Nullable okhttp3.Call.Factory callFactory;
    private @Nullable HttpUrl baseUrl;
    private final List<Converter.Factory> converterFactories = new ArrayList<>();
    private final List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>();
    private @Nullable Executor callbackExecutor;
    private boolean validateEagerly;

    Builder(Platform platform) {
      this.platform = platform;
    }

    public Builder(a) {
      this(Platform.get()); }...// Omit some code

    /** ** * build OKHttpClient */
    public Builder client(OkHttpClient client) {
      return callFactory(checkNotNull(client, "client == null"));
    }

    /** ** build OKHttpClient call */
    public Builder callFactory(okhttp3.Call.Factory factory) {
      this.callFactory = checkNotNull(factory, "factory == null");
      return this;
    }



    /**
     * Set the API base URL.
     *
     * @seeBuild URL */
    public Builder baseUrl(String baseUrl) {
      checkNotNull(baseUrl, "baseUrl == null");
      return baseUrl(HttpUrl.get(baseUrl));
    }

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

    /** * build RXJava {@link Call}.
     */
    public Builder addCallAdapterFactory(CallAdapter.Factory factory) {
      callAdapterFactories.add(checkNotNull(factory, "factory == null"));
      return this;
    }

   

		
    public Retrofit build(a) {
      // url 
      if (baseUrl == null) {
        throw new IllegalStateException("Base URL required.");
      }
			/ / get the OKHttpClient
      okhttp3.Call.Factory callFactory = this.callFactory;
      if (callFactory == null) {
        callFactory = new OkHttpClient();
      }

      Executor callbackExecutor = this.callbackExecutor;
      if (callbackExecutor == null) {
        callbackExecutor = platform.defaultCallbackExecutor();
      }

      // Initialize a container
      List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
      / / add a default DefaultCallAdapterFactory for assisting OKHttp network requests
      callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor));

      // Initialize the default data converter
      List<Converter.Factory> converterFactories = new ArrayList<>(
          1 + this.converterFactories.size() + platform.defaultConverterFactoriesSize());

      converterFactories.add(new BuiltInConverters());
      converterFactories.addAll(this.converterFactories);
      converterFactories.addAll(platform.defaultConverterFactories());
			
      // Build Retrofit objects
      return newRetrofit(callFactory, baseUrl, unmodifiableList(converterFactories), unmodifiableList(callAdapterFactories), callbackExecutor, validateEagerly); }}}Copy the code

Based on Retrofit’s internal code implementation, we can see that two main things are done

  1. The CREATE dynamic proxy executes the defined annotation interface functions (the internal implementation is covered in the next section).
  2. Build something like RxJava adaptation, transform GSON, network request OKHTTP configuration, etc.

So retrofit Create is kind of the core approach.

Retrofit Create dynamic proxy implementation

  public <T> T create(final Class<T> service) {
    / / 1. Check the service
    Utils.validateServiceInterface(service);
    if (validateEagerly) {
      eagerlyValidateMethods(service);
    }
    // Execute dynamic proxy
    return (T) Proxy.newProxyInstance(service.getClassLoader(), newClass<? >[] { 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 {
            // Is the Object type
            if (method.getDeclaringClass() == Object.class) {
              return method.invoke(this, args);
            }
            if (platform.isDefaultMethod(method)) {// This is usually not implemented
              return platform.invokeDefaultMethod(method, service, proxy, args);
            }
            //2. Actually execute the specific functions in the defined interface
            // invoke the internal ServiceMethod function
            returnloadServiceMethod(method).invoke(args ! =null? args : emptyArgs); }}); }Copy the code

If you are familiar with JAVA design patterns, you should know that dynamic proxy patterns are used here. If you are not familiar with dynamic proxy patterns or other design patterns, you can read my previous design pattern series articles, starting with note 2 loadServiceMethod

 private finalMap<Method, ServiceMethod<? >> serviceMethodCache =newConcurrentHashMap<>(); ServiceMethod<? > loadServiceMethod(Method method) {//1. Get the ServiceMethod in the cacheServiceMethod<? > result = serviceMethodCache.get(method);if(result ! =null) return result;//1.1 Returns if it exists
		// add a synchronization lock
    synchronized (serviceMethodCache) {
      // The lock is locked again
      result = serviceMethodCache.get(method);
      if (result == null) {// If it is empty
        result = ServiceMethod.parseAnnotations(this, method); // Start parsing annotations on the interface
        // The parsed data is cached to avoid parsing the same data againserviceMethodCache.put(method, result); }}return result;
  }
Copy the code

We know from the code above that we take the ServiceMethod object from the Map, and if it’s not in the cache we parse it and define the Retrofit network request interface to build the ServiceMethod, ConcurrentHashMap parsing is recommended for those unfamiliar with ConcurrentHashMap parsing.

Below we see ServiceMethod parseAnnotations

  static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
    //1. Get a RetroFIT request object from a function method in the Retrofit instance and interface
    RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);
		//2. Get the return object in the annotation: http://loveshisong.cn/%E7%BC%96%E7%A8%8B%E6%8A%80%E6%9C%AF/2016-02-16-Type%E8%AF%A6%E8%A7%A3.html
    Type returnType = method.getGenericReturnType();
    // Internal check type
    if (Utils.hasUnresolvableType(returnType)) {
      ...// Throw an exception
    }
    if (returnType == void.class) {
      throw methodError(method, "Service methods cannot return void.");
    }
		//3. Perform HttpServiceMethod parsing
    return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
  }
Copy the code

We know that the code above does three main things

  1. Get the request object
  2. Determines the return value type of the function in the calling interface
  3. Perform parsing action

We’ll look at RequestFactory. ParseAnnotations (retrofit, method) internal implementation

// Defined as final cannot be inherited
final class RequestFactory {
  static RequestFactory parseAnnotations(Retrofit retrofit, Method method) {
    return new Builder(retrofit, method).build();
  }
  
static final class Builder {...// Omit some code
  Builder(Retrofit retrofit, Method method) {
      this.retrofit = retrofit; // Get retrofit objects
      this.method = method; // Get the transfer method
      this.methodAnnotations = method.getAnnotations();// Get all the annotations above the method
      this.parameterTypes = method.getGenericParameterTypes();// Get all parameter types
      this.parameterAnnotationsArray = method.getParameterAnnotations();// Get the annotations defined in the parameters
  }
  
  
    RequestFactory build(a) {
      for (Annotation annotation : methodAnnotations) {
        //1. Parse method annotationsparseMethodAnnotation(annotation); }...// omit attribute judgment

      // Get the number of parameter annotations and iterate over them to do some processing
      int parameterCount = parameterAnnotationsArray.length;
      parameterHandlers = newParameterHandler<? >[parameterCount];for (int p = 0, lastParameter = parameterCount - 1; p < parameterCount; p++) { parameterHandlers[p] = parseParameter(p, parameterTypes[p], parameterAnnotationsArray[p], p == lastParameter); }...// omit attribute judgment
			Instantiate a RequestFactory object
      return new RequestFactory(this); }}Copy the code

Let’s look at notation first

    private void parseMethodAnnotation(Annotation annotation) {
      if (annotation instanceof DELETE) {
        parseHttpMethodAndPath("DELETE", ((DELETE) annotation).value(), false);
      } else if (annotation instanceof GET) {
        parseHttpMethodAndPath("GET", ((GET) annotation).value(), false);
      } else if (annotation instanceof HEAD) {
        parseHttpMethodAndPath("HEAD", ((HEAD) annotation).value(), false);
      } else if (annotation instanceof PATCH) {
        parseHttpMethodAndPath("PATCH", ((PATCH) annotation).value(), true);
      } else if (annotation instanceof POST) {
        parseHttpMethodAndPath("POST", ((POST) annotation).value(), true);
      } else if (annotation instanceof PUT) {
        parseHttpMethodAndPath("PUT", ((PUT) annotation).value(), true);
      } else if (annotation instanceof OPTIONS) {
        parseHttpMethodAndPath("OPTIONS", ((OPTIONS) annotation).value(), false);
      } else if (annotation instanceof HTTP) {
        HTTP http = (HTTP) annotation;
        parseHttpMethodAndPath(http.method(), http.path(), http.hasBody());
      } else if (annotation instanceof retrofit2.http.Headers) {
        String[] headersToParse = ((retrofit2.http.Headers) annotation).value();
        if (headersToParse.length == 0) {
          throw methodError(method, "@Headers annotation is empty.");
        }
        headers = parseHeaders(headersToParse);
      } else if (annotation instanceof Multipart) {
        if (isFormEncoded) {
          throw methodError(method, "Only one encoding annotation is allowed.");
        }
        isMultipart = true;
      } else if (annotation instanceof FormUrlEncoded) {
        if (isMultipart) {
          throw methodError(method, "Only one encoding annotation is allowed.");
        }
        isFormEncoded = true; }}Copy the code

If the current function has a request annotation such as GET,POST,…., etc Let’s look at the internal implementation

    private void parseHttpMethodAndPath(String httpMethod, String value, boolean hasBody) {
      if (this.httpMethod ! =null) {
        throw methodError(method, "Only one HTTP method is allowed. Found: %s and %s.".this.httpMethod, httpMethod);
      }
      this.httpMethod = httpMethod;
      this.hasBody = hasBody;

      if (value.isEmpty()) {
        return;
      }

      // Get the relative URL path and existing query string, if present.
      int question = value.indexOf('? ');
      if(question ! = -1 && question < value.length() - 1) {
        // Ensure the query string does not have any named parameters.
        String queryParams = value.substring(question + 1);
        Matcher queryParamMatcher = PARAM_URL_REGEX.matcher(queryParams);
        if (queryParamMatcher.find()) {
          throw methodError(method, "URL query string \"%s\" must not have replace block. "
              + "For dynamic query parameters use @Query.", queryParams); }}this.relativeUrl = value;
      this.relativeUrlParamNames = parsePathParameters(value);
    }
Copy the code

The inside is to do some pre-processing of the request link, and the last line of code is to do some processing on the data in the annotation and return a set.

Now, back to ServiceMethod class HttpServiceMethod parseAnnotations (retrofit, method, requestFactory);

  static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations( Retrofit retrofit, Method method, RequestFactory requestFactory) {
    boolean isKotlinSuspendFunction = requestFactory.isKotlinSuspendFunction; // Whether Kotlin is supported
    boolean continuationWantsResponse = false;
    boolean continuationBodyNullable = false;
		// Get all the annotations in the current network request function
    Annotation[] annotations = method.getAnnotations();
    Type adapterType;
    if (isKotlinSuspendFunction) { // There is no support for all internal implementations yet.//
    } else {
      // Returns the return type of the annotation in the interface
      adapterType = method.getGenericReturnType();
    }
		/ / 1. Create createCallAdapter is returned RxJava observed object or is the default DefaultCallAdapterFactory OKHttp object, judging by the comments return values
    CallAdapter<ResponseT, ReturnT> callAdapter =
        createCallAdapter(retrofit, method, adapterType, annotations);
    Type responseType = callAdapter.responseType();
    if (responseType == okhttp3.Response.class) {
      throw methodError(method, "'"
          + getRawType(responseType).getName()
          + "' is not a valid response body type. Did you mean ResponseBody?"); }...// omit judgment
		//2. Create a data response conversion to return GSON
    Converter<ResponseBody, ResponseT> responseConverter =
        createResponseConverter(retrofit, method, responseType);
		//3. Get the OKHttp Call Factory
    okhttp3.Call.Factory callFactory = retrofit.callFactory;
    if(! isKotlinSuspendFunction) {//4. Create a CallStuck
      return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);
    } else if (continuationWantsResponse) {
      //noinspection unchecked Kotlin compiler guarantees ReturnT to be Object.
      return (HttpServiceMethod<ResponseT, ReturnT>) new SuspendForResponse<>(requestFactory,
          callFactory, responseConverter, (CallAdapter<ResponseT, Call<ResponseT>>) callAdapter);
    } else {
      //noinspection unchecked Kotlin compiler guarantees ReturnT to be Object.
      return (HttpServiceMethod<ResponseT, ReturnT>) newSuspendForBody<>(requestFactory, callFactory, responseConverter, (CallAdapter<ResponseT, Call<ResponseT>>) callAdapter, continuationBodyNullable); }}Copy the code

It does some preprocessing of the annotations and returns a ServiceMethod object.

Note 1 from above we can know here according to the analytical returns an annotation RxJava2CallAdapter object or is the Builder of the default DefaultCallAdapterFactory Retrofit

When a ServiceMethod object is returned, an invoke function is continued, and we look directly at the HttpServiceMethod function, since it inherits from ServiceMethod

  @Override final @Nullable ReturnT invoke(Object[] args) {
    Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
    return adapt(call, args);
  }

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

Take the OKHttpCall above and execute an Adapt abstract function. Since we parse the annotation directly we know that the CallAdapter object is instantiated inside it, so look for its internal ADAPT implementation

  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) {
      // Call the CallAdapter adapt
      returncallAdapter.adapt(call); }}Copy the code

Here we know that the CallAdapter.adapt implements two classes, RxJava2CallAdapter and DefaultCallAdapterFactory respectively under the analysis of their implementation adapt function (Call Call)

  • RxJava2CallAdapter adapt
final class RxJava2CallAdapter<R> implements CallAdapter<R.Object> {

  @Override public Object adapt(Call<R> call) {
    . / / the isAsync is configured here RxJava2CallAdapterFactory createAsync inside has a record of a Boolean ()
    The CallEnqueueObservable object is returned because it is asynchronous
    Observable<Response<R>> responseObservable = isAsync
        ? new CallEnqueueObservable<>(call)
        : newCallExecuteObservable<>(call); Observable<? > observable;//2. As long as the generic parameter in the request interface is not Result, isBody is normally executed to return BodyObservable holding a CallEnqueueObservable object inside
    if (isResult) {// If the generic argument is Result
      observable = new ResultObservable<>(responseObservable);
    } else if (isBody) { 
      observable = new BodyObservable<>(responseObservable);
    } else {
      observable = responseObservable;
    }

    / / there is no specified so empty, is also specified in RxJava2CallAdapterFactory configuration
    if(scheduler ! =null) {
      observable = observable.subscribeOn(scheduler);
    }
		// Define whether Flowable is observed
    if (isFlowable) {
      return observable.toFlowable(BackpressureStrategy.LATEST);
    }
    // Is the Single observed defined
    if (isSingle) {
      return observable.singleOrError();
    }
     // Define whether Maybe be observed
    if (isMaybe) {
      return observable.singleElement();
    }
    // Defines whether the Completable is being observed
    if (isCompletable) {
      return observable.ignoreElements();
    }
    // Return BodyObservable if neither of them exists
    returnRxJavaPlugins.onAssembly(observable); }}Copy the code

Go here to api.getwxarticle (); If the observed object is defined, it returns the observed object that holds a CallEnqueueObservable inside the BodyObservable.

  • DefaultCallAdapterFactory adapt
final class DefaultCallAdapterFactory extends CallAdapter.Factory {
  private final @Nullable Executor callbackExecutor;

  DefaultCallAdapterFactory(@Nullable Executor callbackExecutor) {
    this.callbackExecutor = callbackExecutor;
  }

  @Override public @NullableCallAdapter<? ,? > get( Type returnType, Annotation[] annotations, Retrofit retrofit) { ...// Omit some code

    return newCallAdapter<Object, Call<? > > () {@Override public Type responseType(a) {
        return responseType;
      }

      @Override public Call<Object> adapt(Call<Object> call) {
        return executor == null
            ? call
            : newExecutorCallbackCall<>(executor, call); }}; }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) {
      checkNotNull(callback, "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(a) {
              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(new Runnable() {
            @Override public void run(a) {
              callback.onFailure(ExecutorCallbackCall.this, t); }}); }}); }...// Omit some code}}Copy the code

As you can see, if the network request interface function returns a Call, then what is returned is the ExecutorCallbackCall object

The summary of this chapter

As you can see, the CREATE function uses dynamic proxies to call functions in the interface, first parsing the annotation data, then parsing the function return value, and finally creating the corresponding implementation class based on the function return value.

The next section covers the actual invocation

Retrofit + OKHttp

Call Call = api.getwxarticles (); Call is the ExecutorCallbackCall object, so depending on how you call it

        Call<JsonObject> call = api.getWXarticles();
        call.enqueue(new Callback<JsonObject>() {
            @Override
            public void onResponse(Call<JsonObject> call, retrofit2.Response<JsonObject> response) {
                Log.d(TAG, "Retrofit + OKHttp " + response.body().toString());
            }

            @Override
            public void onFailure(Call<JsonObject> call, Throwable t) {
                Log.d(TAG, "Retrofit + OKHttp "+ t.getMessage()); }});Copy the code

We’ll look directly at the Enqueue function of the ExecutorCallbackCall

  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) {
      //1. Check whether callBack is empty
      checkNotNull(callback, "callback == null");
			// delegate is a Call from OKHttpClient
      delegate.enqueue(new Callback<T>() {
        @Override public void onResponse(Call<T> call, final Response<T> response) {
          callbackExecutor.execute(new Runnable() {
            @Override public void run(a) {
              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(new Runnable() {
            @Override public void run(a) {
              callback.onFailure(ExecutorCallbackCall.this, t); }}); }}); }...// Omit some code
  }
Copy the code

As noted in comment 2 above, the delegate is passed in via the upper-layer HttpServiceMethod invoke function

  @Override final @Nullable ReturnT invoke(Object[] args) {
    Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
    return adapt(call, args);
  }
Copy the code

Okhttpcall.enqueue (new Callback()))

// Inherits Call from OKHttp
final class OkHttpCall<T> implements Call<T> {
 
  @Override public void enqueue(final Callback<T> callback) {
    checkNotNull(callback, "callback == null");
		// Get the Call from OKHttp
    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 {
          1. Create a RealCall object
          call = rawCall = createRawCall();
        } catch(Throwable t) { throwIfFatal(t); failure = creationFailure = t; }}}if(failure ! =null) {
      callback.onFailure(this, failure);
      return;
    }

    if (canceled) {
      call.cancel();
    }
		//2. Execute the enqueue asynchronous network request through the RealCall object
    call.enqueue(new okhttp3.Callback() {
      @Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
        Response<T> response;
        try {
          response = parseResponse(rawResponse);
        } catch (Throwable e) {
          throwIfFatal(e);
          callFailure(e);
          return;
        }

        try {
          callback.onResponse(OkHttpCall.this, response);
        } catch (Throwable t) {
          throwIfFatal(t);
          t.printStackTrace(); // TODO this is not great}}@Override public void onFailure(okhttp3.Call call, IOException e) {
        callFailure(e);
      }

      private void callFailure(Throwable e) {
        try {
          callback.onFailure(OkHttpCall.this, e);
        } catch (Throwable t) {
          throwIfFatal(t);
          t.printStackTrace(); // TODO this is not great}}}); }}Copy the code

There are two main things going on here

  1. Create an OKHttp call, also known as a RealCall
  2. Execute the RealCall enQueue asynchronous function

Let’s look at comment 1 because the OKHttp Request also requires a Request object, which we haven’t seen yet so keep looking

  private okhttp3.Call createRawCall(a) throws IOException {
    / / 1.
    okhttp3.Call call = callFactory.newCall(requestFactory.create(args));
    if (call == null) {
      throw new NullPointerException("Call.Factory returned null.");
    }
    return call;
  }
Copy the code

Callfactory.newcall (requestFactory.create(args)) returns an okHttp. call object. But those familiar with OKHTTP know that newCall requires a Request object to be passed in

//OKHttpClient.java
@Override public Call newCall(Request request) {
    return RealCall.newRealCall(this, request, false /* for web socket */);
}
Copy the code

So let’s just look at requestFactory.create(args)

1. Return a Request object
okhttp3.Request create(Object[] args) throws IOException {
    @SuppressWarnings("unchecked") // It is an error to invoke a method with the wrong arg types.
    ParameterHandler<Object>[] handlers = (ParameterHandler<Object>[]) parameterHandlers;

    int argumentCount = args.length;
    if(argumentCount ! = handlers.length) { ...// omit exception code
    }
		// Build a Retrofit Request object
    RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl,
        headers, contentType, hasBody, isFormEncoded, isMultipart);

    if (isKotlinSuspendFunction) {

      argumentCount--;
    }

    List<Object> argumentList = new ArrayList<>(argumentCount);
    for (int p = 0; p < argumentCount; p++) {
      argumentList.add(args[p]);
      handlers[p].apply(requestBuilder, args[p]);
    }
		/ / 2.
    return requestBuilder.get()
        .tag(Invocation.class, new Invocation(method, argumentList))
        .build();
  }
Copy the code

From the above code we know that we built a Retrofit Request object internally. Finally, we call the comment 2 code and continue to follow

	// requestBuilder.get()
  Request.Builder get(a) {
    HttpUrl url;
    HttpUrl.Builder urlBuilder = this.urlBuilder;
    if(urlBuilder ! =null) { // Create a new one if the URL is empty
      url = urlBuilder.build();
    } else {
      / / here is connect a complete spliced together "https://wanandroid.com/wxarticle/chapters/json"
      url = baseUrl.resolve(relativeUrl);
      if (url == null) {
        throw new IllegalArgumentException(
            "Malformed URL. Base: " + baseUrl + ", Relative: "+ relativeUrl); }}// Build the request body
    RequestBody body = this.body;
    if (body == null) {
      // Try to pull from one of the builders.
      if(formBuilder ! =null) {
        body = formBuilder.build();
      } else if(multipartBuilder ! =null) {
        body = multipartBuilder.build();
      } else if (hasBody) {
        // Body is absent, make an empty body.
        body = RequestBody.create(null.new byte[0]); }}// Build the request data type
    MediaType contentType = this.contentType;
    if(contentType ! =null) {
      if(body ! =null) {
        body = new ContentTypeOverridingRequestBody(body, contentType);
      } else {
        headersBuilder.add("Content-Type", contentType.toString()); }}// Construct a Request object containing the URL,headers, and body
    return requestBuilder
        .url(url)
        .headers(headersBuilder.build())
        .method(method, body);
  }
Copy the code

Here the get function is a procedure for Retrofit Request – > OKHttp Request.

Now that the OKHttp Request and RealCall objects are available, the corresponding asynchronous functions can be executed and the data can be called back to the calling layer. If you are not familiar with OKHttp, read the introduction to this article.

RxJava + Retrofit + OKHttp

Observable

Observable = api.getwxarticle (); An Observable is actually a BodyObservable and upstream of it is the CallEnqueueObservable asynchronous observer object, so according to call

        
				Observable<JsonObject> observable = api.getWXarticle();
				// An Observable is a BodyObservable that holds an upstream CallEnqueueObservable observable
        observable.subscribeOn(AndroidSchedulers.mainThread())
                .subscribe(new Observer<JsonObject>() {
                    @Override
                    public void onSubscribe(Disposable d) {
                        Log.d(TAG, "RxJava + Retrofit + OKHttp subscription succeeded");
                    }

                    @Override
                    public void onNext(JsonObject s) {
                        Log.d(TAG, "RxJava + Retrofit + OKHttp " + s.toString());
                    }

                    @Override
                    public void onError(Throwable e) {
                        Log.d(TAG, "RxJava + Retrofit + OKHttp " + e.getMessage());
                    }

                    @Override
                    public void onComplete(a) {
                        Log.d(TAG, "RxJava + Retrofit + OKHttp onComplete"); }});Copy the code

Due to return here is BodyObservable observed, then according to the execution process in ObservableSubscribeOn. The s.o subscribeActual nSubscribe (parent); The subscription succeeded in the function. According to RxJava subscription successful execution flow upstream and downstream can know eventually callback to BodyObservable. SubscribeActual function, specific implementation is as follows

  @Override protected void subscribeActual(Observer<? super T> observer) {
    upstream.subscribe(new BodyObserver<T>(observer));
  }
Copy the code

According to the call execution process in THE RxJava source code, upstream is the asynchronous observed object of the upstream CallEnqueueObservable, and the specific implementation of its subscribeActual function is directly looked at

	//CallEnqueueObservable.java
  @Override protected void subscribeActual(Observer<? super Response<T>> observer) {
    / / 1.
    Call<T> call = originalCall.clone();
    / / 2.
    CallCallback<T> callback = new CallCallback<>(call, observer);
    / / 3.
    observer.onSubscribe(callback);
    if(! callback.isDisposed()) {/ / 4.call.enqueue(callback); }}Copy the code

Summarize the meaning of the above notes

  1. Get the OKhttpCall Call object
  2. Wrap the call and Observer again
  3. Invoke the underlying observer object, the BodyObserver
  4. Finally, the OKHttp asynchronous network request is executed, passing the encapsulated CallCallback to the OKHttpCall object

Let’s look directly at the implementation of the OKHttpCall enQueue function

  @Override public void enqueue(final Callback<T> callback) {
    checkNotNull(callback, "callback == null"); .// Omit some code

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

        try {
          callback.onResponse(OkHttpCall.this, response);
        } catch (Throwable t) {
          throwIfFatal(t);
          t.printStackTrace(); // TODO this is not great}}@Override public void onFailure(okhttp3.Call call, IOException e) {
        callFailure(e);
      }

      private void callFailure(Throwable e) {
        try {
          callback.onFailure(OkHttpCall.this, e);
        } catch (Throwable t) {
          throwIfFatal(t);
          t.printStackTrace(); // TODO this is not great}}}); }Copy the code

Here to see if the request is successful correction directly to OKhttp. The callback. OnResponse and callback to CallCallback onResponse function, the following is the specific implementation

    @Override public void onResponse(Call<T> call, Response<T> response) {
      if (disposed) return;

      try {
        observer.onNext(response);

        if(! disposed) { terminated =true; observer.onComplete(); }}catch (Throwable t) {
        Exceptions.throwIfFatal(t);
        if (terminated) {
          RxJavaPlugins.onError(t);
        } else if(! disposed) {try {
            observer.onError(t);
          } catch (Throwable inner) {
            Exceptions.throwIfFatal(inner);
            RxJavaPlugins.onError(newCompositeException(t, inner)); }}}}Copy the code

By the observer. OnNext (response); A layer of transfer to spread a layer of end ObservableSubscribeOn# SubscribeOnObserver. OnNext () function here the entire request is completed.

conclusion

RxJava + Retrofit + OKHttp, RxJava + Retrofit + OKHttp, RxJava + Retrofit + OKHttp, RxJava + Retrofit + OKHttp, RxJava + Retrofit + OKHttp It is recommended that you understand the fundamentals of OKHttp and RxJava before learning Retrofit network requests, otherwise Retrofit will not be able to watch.

reference

Look at Retrofit from a dynamic proxy perspective, that’s the essence of Retrofit!