Framework Interpretation | Retrofit Design Analysis

Author: Calculus_Xiao Wang

Retrofit is a type-safe HTTP client that converts HTTP APIs to Java interfaces through annotations, and uses dynamic proxies, CallAdapter and Converter to initiate requests and parse responses.

This article focuses on the architectural design of Retrofit, and will not elaborate注解解析能力 on it

This article is based onretrofit:2.6.2

example

This example only takes the most basic request initiated by retrofit as an example. Regarding packages such as kotlin suspend or rxjava, they will be analyzed together in the source code stage

// 定义一个接口,用注解描述HTTP请求
public interface GitHubService {
    @GET("users/{user}/repos")
    Call<List<Repo>> listRepos(@Path("user") String user);
}

// 创建一个Retrofit实例,并指定基础URL和转换器
Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com/")
    .addConverterFactory(GsonConverterFactory.create())
    .build();

// 通过Retrofit创建接口的实现类
GitHubService service = retrofit.create(GitHubService.class);

// 调用接口方法,获取Call对象
Call<List<Repo>> repos = service.listRepos("octocat");

// 同步或异步执行Call对象,获取响应数据
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) {
    // 处理失败情况
    }
});

Source code analysis

Usually in the use of retrofit, it will be used together with rxjava or kotlin suspend keywords, but these two steps are the same as the sample code:

  1. Get the Service object
  2. Call the Service method
// 通过Retrofit创建接口的实现类
GitHubService service = retrofit.create(GitHubService.class);

// 调用接口方法,获取Call对象
Call<List<Repo>> repos = service.listRepos("octocat");

As for retrofit, what we are most curious about is how it is linked to OkHttp to make requests with it. The request initiated in OkHttp is passed Call.enqueue(), so the target direction of this article is the call of this method

Get the Service object

retrofit.createWhat you get is newProxyInstancethe Service object generated by the dynamic proxy. The focus is on the methods in it invoke. In general, the final call is loadServiceMethod(method)the executed invoke() . Then see what object it returns, and what its invoke() does. what

// Retrofit.java
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();
        private final Object[] emptyArgs = new Object[0];

        @Override public @Nullable 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);
          }
          return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
        }
      });
}

Each method of Service eventually corresponds to one HttpServiceMethod, and it also has several implementation classes CallAdapted(usually), SuspendForResponse(used by suspend),SuspendForBody

// Retrofit.java
ServiceMethod<?> loadServiceMethod(Method method) {
  // 这里还做了缓存,这里注意喔,缓存的是method,而不是service对应的class
  ServiceMethod<?> result = serviceMethodCache.get(method);
  if (result != null) return result;

  synchronized (serviceMethodCache) {
    result = serviceMethodCache.get(method);
    if (result == null) {
      // 最终return的是这个result,跟进去看看
      result = ServiceMethod.parseAnnotations(this, method);
      serviceMethodCache.put(method, result);
    }
  }
  return result;
}

// ServiceMethod.java
static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
    // 这里进行了Request注解方面的解析,感兴趣的可以跟进去看,重点关注其中的`parseMethodAnnotation()`和`parseParameter()`
    // 内部区分了`methodAnnotations`和`parameterAnnotations`两种类型注解
    // 最终封装得到RequestFactory,也就转化为了okhttp请求时需要的一些内容
    RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);
    // ……
    // 继续往下看,其实返的就是HttpServiceMethod对象
    return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
}

// HttpServiceMethod.java
static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
      Retrofit retrofit, Method method, RequestFactory requestFactory) {
    Annotation[] annotations = method.getAnnotations();
    Type adapterType;
    // ……
    // 虽然suspend会有些区别,但你完全可以理解为 方法 return的对象类型
    // 比如联动rxjava,就会返回一个Observable<T>
    adapterType = method.getGenericReturnType();
    
    // ……
    // Call适配器,Call其实就是OkHttp的Call,适配器的意义就是将其转化为对应的类型,比如rxjava的Observable
    // 但其内部仍会调用Call的方法
    CallAdapter<ResponseT, ReturnT> callAdapter =
        createCallAdapter(retrofit, method, adapterType, annotations);
    
    // 这个是结果解析器,gson就是添加在这里
    Converter<ResponseBody, ResponseT> responseConverter =
        createResponseConverter(retrofit, method, responseType);

    okhttp3.Call.Factory callFactory = retrofit.callFactory;
    if (!isKotlinSuspendFunction) {
      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>) new SuspendForBody<>(requestFactory,
          callFactory, responseConverter, (CallAdapter<ResponseT, Call<ResponseT>>) callAdapter,
          continuationBodyNullable);
    }
}

As mentioned earlier invoke(), let's continue. For HttpServiceMethodseveral subclasses of , none of them implemented this method, and found that it was called in its parent class, adaptleaving it to its subclasses to implement

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

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

// 其实在call的设计中,就完全能看到okhttp的影子了
public interface Call<T> extends Cloneable {
  /**
   * Synchronously send the request and return its response.
   *
   * @throws IOException if a problem occurred talking to the server.
   * @throws RuntimeException (and subclasses) if an unexpected error occurs creating the request
   * or decoding the response.
   */
  Response<T> execute() throws IOException;

  /**
   * Asynchronously send the request and notify {@code callback} of its response or if an error
   * occurred talking to the server, creating the request, or processing the response.
   */
  void enqueue(Callback<T> callback);

  /**
   * Returns true if this call has been either {@linkplain #execute() executed} or {@linkplain
   * #enqueue(Callback) enqueued}. It is an error to execute or enqueue a call more than once.
   */
  boolean isExecuted();

  /**
   * Cancel this call. An attempt will be made to cancel in-flight calls, and if the call has not
   * yet been executed it never will be.
   */
  void cancel();

  /** True if {@link #cancel()} was called. */
  boolean isCanceled();

  /**
   * Create a new, identical call to this one which can be enqueued or executed even if this call
   * has already been.
   */
  Call<T> clone();

  /** The original HTTP request. */
  Request request();
}

Call the Service method

In createthe details of the dynamic proxy, we know that the invocation of each method of the retrofit service will be mapped to the corresponding type ofHttpServiceMethod.adapt()

normal call

// 调用接口方法,获取Call对象
Call<List<Repo>> repos = service.listRepos("octocat");

// 同步或异步执行Call对象,获取响应数据
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) {
    // 处理失败情况
    }
});

In the most common case, the return value is the original Call, which corresponds to CallAdapted, but the headache here is that it has another layer. The reason for the design here is to be compatible with scenarios such as Rxjava

// CallAdapted.java
@Override protected ReturnT adapt(Call<ResponseT> call, Object[] args) {
  return callAdapter.adapt(call);
}

Where do these callAdaptercome from? Return to the place where the previous input

callAdapterFactories()
    >>> retrofit.callAdapter(returnType, annotations)
        >>> nextCallAdapter(null, returnType, annotations)

// nextCallAdapter()
for (int i = start, count = callAdapterFactories.size(); i < count; i++) {
    // 可以想象的是,callAdapterFactories会根据returnType去匹配给到对象的adpter
    // 至于这个,对比下rxjava联动时需要添加的代码.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
    // 也就是说Retrofit在建造过程中,会逐步的传进去
    CallAdapter<?, ?> adapter = callAdapterFactories.get(i).get(returnType, annotations, this);
    if (adapter != null) {
        return adapter;
    }
}

// Retrofit.Builder.build()
List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor));

For the most common call of this kind, that must DefaultCallAdapterFactorybe it , and getting the adapter is ultimatelyExecutorCallbackCall

// DefaultCallAdapterFactory.java
public @Nullable CallAdapter<?, ?> get(
    Type returnType, Annotation[] annotations, Retrofit retrofit) {
  if (getRawType(returnType) != Call.class) {
    return null;
  }
  if (!(returnType instanceof ParameterizedType)) {
    throw new IllegalArgumentException(
        "Call return type must be parameterized as Call<Foo> or Call<? extends Foo>");
  }
  final Type responseType = Utils.getParameterUpperBound(0, (ParameterizedType) returnType);

  final Executor executor = Utils.isAnnotationPresent(annotations, SkipCallbackExecutor.class)
      ? null
      : callbackExecutor;

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

    @Override public Call<Object> adapt(Call<Object> call) {
      return executor == null
          ? call
          : new ExecutorCallbackCall<>(executor, call);
    }
  };
}

Looking back at the sample code, after the call service methodis obtained Call, call it enqueue, that is to say, it finally enteredExecutorCallbackCall.enqueue

// ExecutorCallbackCall.java
@Override public void enqueue(final Callback<T> callback) {
  checkNotNull(callback, "callback == null");
  // 这里的delegate,其实就是构造时传入的`OkHttpCall`,当然这还不是实际OkHttp的Call
  delegate.enqueue(new Callback<T>() {
    @Override public void onResponse(Call<T> call, final Response<T> response) {
      // 当结果回来时,需要进行线程切换
      // 至于这个callbackExecutor感兴趣的可以自行追踪下,其实就是retrofit在build时生成的一个MainThreadExecutor
      // 由platform.defaultCallbackExecutor()得到,实际内部就是个handler……
      callbackExecutor.execute(new Runnable() {
        @Override public void run() {
          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() {
          callback.onFailure(ExecutorCallbackCall.this, t);
        }
      });
    }
  });
}

The request is actually initiated by the user OkHttpCall, whether it is in the middle HttpServiceMethodor not CallAdapter, it is nothing more than a design for scalability and compatibility.

// OkHttpCall.java
@Override public void enqueue(final Callback<T> callback) {
  checkNotNull(callback, "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 {
        // 这里由callFactory.newCall生成
        // 而这个callFactory就是retrofit.client(okHttpClient)
        // 到这明白了吧,这个OkHttpCall终究还是OkHttpClient.RealCall套了一层壳
        call = rawCall = createRawCall();
      } catch (Throwable t) {
        throwIfFatal(t);
        failure = creationFailure = t;
      }
    }
  }

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

  if (canceled) {
    call.cancel();
  }
  // 下面的内容,对于熟悉OkHttp的小伙伴来说已经毫无悬念了
  call.enqueue(new okhttp3.Callback() {
    @Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
      Response<T> response;
      try {
        // 这里进行了结果的解析,其中用到了`responseConverter`,也就是addConverterFactory
        // 同样的,也是类似于CallAdapter的设计
        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
      }
    }
  });
}

In fact, at this point, the general design of Retrofit is over. where HttpServiceMethodand CallAdapteris the key

Rxjava call

When the book is connected to the above ordinary call, for the linkage of Rxjava, the difference is thatCallAdapter

addCallAdapterFactory(RxJava2CallAdapterFactory.create());

see the same factory.getwill getRxJavaCallAdapter

// RxJavaCallAdapter.java
@Override public Object adapt(Call<R> call) {
  // 这里根据同步\异步 将OkHttpCall传了进去,结合rxjava操作符,发起okhttp请求,并把结果发射出去
  // 可以看对应类中的call(),这里就不展开了蛤,相信你已经可以独当一面了
  // 当然,主要是我对rxjava操作符不太熟……
  OnSubscribe<Response<R>> callFunc = isAsync
      ? new CallEnqueueOnSubscribe<>(call)
      : new CallExecuteOnSubscribe<>(call);

  OnSubscribe<?> func;
  if (isResult) {
    func = new ResultOnSubscribe<>(callFunc);
  } else if (isBody) {
    func = new BodyOnSubscribe<>(callFunc);
  } else {
    func = callFunc;
  }
  // 这里包装之后,最终返回了出去,得到了通常使用的Observable
  Observable<?> observable = Observable.create(func);

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

  if (isSingle) {
    return observable.toSingle();
  }
  if (isCompletable) {
    return observable.toCompletable();
  }
  return observable;
}

Suspend call

For coroutines combined with retrofit, it is usually scope.launch{ service.method() }

CoroutineScope(Dispatchers.IO).launch {
    try {
        val repos = service.listRepos("octocat")
        // 处理响应数据,通常会给一个callback,外面再通过livedata去发射处理好的数据
    } catch (e: Exception) {
        // 处理异常情况
    }
}

For suspend, it must be combined with coroutines, which replaces the thread switching capability of rxjava, which HttpServiceMethodcorresponds to nothing fancy SuspendForResponse(主体差不多,那就挑这个将)\SuspendForBody, and what you get is the most commonCallAdapterCallExecutorCallbackCall

// SuspendForResponse.java
@Override protected Object adapt(Call<ResponseT> call, Object[] args) {
  call = callAdapter.adapt(call);

  //noinspection unchecked Checked by reflection inside RequestFactory.
  Continuation<Response<ResponseT>> continuation =
      (Continuation<Response<ResponseT>>) args[args.length - 1];

  // See SuspendForBody for explanation about this try/catch.
  try {
    // 对于suspend的阻塞,await是再熟悉不过了的
    return KotlinExtensions.awaitResponse(call, continuation);
  } catch (Exception e) {
    return KotlinExtensions.yieldAndThrow(e, continuation);
  }
}

Searching and searching, suddenly looking back, the person was in a dimly lit place, and finally ExecutorCallbackCallgot called, here is a suspendCancellableCoroutinehang-up application, combined with resumecalling

suspend fun <T : Any> Call<T>.awaitResponse(): Response<T> {
  return suspendCancellableCoroutine { continuation ->
    continuation.invokeOnCancellation {
      cancel()
    }
    enqueue(object : Callback<T> {
      override fun onResponse(call: Call<T>, response: Response<T>) {
        continuation.resume(response)
      }

      override fun onFailure(call: Call<T>, t: Throwable) {
        continuation.resumeWithException(t)
      }
    })
  }
}

Summarize

In fact, the most intriguing thing about the design of Retrofit is that through 动态代理the cooperation of the mechanism HttpServiceMethod, the extended design is realized at methodthe granularity, and it CallAdapteris Convertertantamount to adding icing on the cake in terms of overall compatibility and decoupling.

This actually provides a very good reference for the applicable scenarios of annotations, which may usually be limited to APT for instrumentation or code generation

• Retrofit uses a dynamic proxy mechanism to create a proxy class for the interface through the Proxy.newProxyInstance method
and delegates all method calls to the internal InvocationHandler for processing
• InvocationHandler is responsible for creating ServiceMethod objects based on method annotations.
ServiceMethod is an abstract class that encapsulates the request. Information, such as URL, HTTP method, parameters, request body, etc.
The specific implementation class of ServiceMethod is HttpServiceMethod,
which creates Call objects and parses response data according to the CallAdapter and Converter configured by Retrofit.
CallAdapter is an interface that defines how to convert Call objects. Retrofit provides a default CallAdapter for other types, such as Observable, Deferred, etc.
, and you can also add a custom CallAdapter through the addCallAdapterFactory method.
Converter is an interface that defines how to convert the request body and response body into Java objects
. Retrofit provides Some built-in Converters, such as GsonConverter, can also add custom Converters through the addConverterFactory method

Android study notes

Android Performance Optimization: https://qr18.cn/FVlo89
Android Vehicle: https://qr18.cn/F05ZCM
Android Reverse Security Study Notes: https://qr18.cn/CQ5TcL
Android Framework Principles: https://qr18.cn/AQpN4J
Android Audio and Video: https://qr18.cn/Ei3VPD
Jetpack (including Compose): https://qr18.cn/A0gajp
Kotlin: https://qr18.cn/CdjtAF
Gradle: https://qr18.cn/DzrmMB
OkHttp Source Code Analysis Notes: https://qr18.cn/Cw0pBD
Flutter: https://qr18.cn/DIvKma
Android Eight Knowledge Body: https://qr18.cn/CyxarU
Android Core Notes: https://qr21.cn/CaZQLo
Android Past Interview Questions: https://qr18.cn/CKV8OZ
2023 Latest Android Interview Question Collection: https://qr18.cn/CgxrRy
Android Vehicle Development Job Interview Exercises: https://qr18.cn/FTlyCJ
Audio and Video Interview Questions:https://qr18.cn/AcV6Ap

Guess you like

Origin blog.csdn.net/weixin_61845324/article/details/132167171