フレームワークの解釈 | レトロフィット設計分析

著者: Calculus_Xiao Wang

Retrofit は、アノテーションを通じて HTTP API を Java インターフェイスに変換し、動的プロキシ、CallAdapter および Converter を使用してリクエストを開始し、レスポンスを解析するタイプセーフな HTTP クライアントです。

この記事では、Retrofit のアーキテクチャ設計に焦点を当てており注解解析能力、詳しくは説明しません。

この記事は以下に基づいていますretrofit:2.6.2

この例では、retrofit によって開始される最も基本的なリクエストのみを例として取り上げますが、kotlin stop や rxjava などのパッケージについては、ソース コードの段階でまとめて解析されます。

// 定义一个接口,用注解描述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) {
    // 处理失败情况
    }
});

ソースコード分析

通常、retrofit を使用する場合は、rxjava または kotlin stop キーワードと組み合わせて使用​​しますが、これら 2 つの手順はサンプル コードと同じです。

  1. サービスオブジェクトを取得する
  2. Service メソッドを呼び出す
// 通过Retrofit创建接口的实现类
GitHubService service = retrofit.create(GitHubService.class);

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

改造に関しては、私たちが最も興味があるのは、OkHttp とどのようにリンクしてリクエストを行うかです。OkHttp で開始されたリクエストは渡されるCall.enqueue()ため、この記事のターゲット方向はこのメソッドの呼び出しです

サービスオブジェクトを取得する

retrofit.create取得するものは、newProxyInstance動的プロキシによって生成された Service オブジェクトです。焦点はその中のメソッドにありますinvoke。一般に、最後の呼び出しはloadServiceMethod(method)実行されたinvoke()です。次に、どのオブジェクトが返されるか、およびそのinvoke()が何を行うかを確認します。何

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

Service の各メソッドは最終的に 1 つに対応しHttpServiceMethod、またCallAdapted(通常は)、SuspendForResponse(サスペンドによって使用される) などの複数の実装クラスもあります。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);
    }
}

前述したようにinvoke()、続けてみましょう。のいくつかのサブクラスでHttpServiceMethodは、どのサブクラスもこのメソッドを実装しておらず、このメソッドが親クラスで呼び出されていることがわかり、adapt実装はサブクラスに任せられていました。

@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();
}

Service メソッドを呼び出す

動的プロキシの詳細ではcreate、レトロフィット サービスの各メソッドの呼び出しが、対応するタイプのHttpServiceMethod.adapt()

通常の通話

// 调用接口方法,获取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) {
    // 处理失败情况
    }
});

最も一般的なケースでは、戻り値は に対応する元の Call ですCallAdaptedが、ここでの問題は、別の層があることです。ここでの設計の理由は、Rxjava などのシナリオと互換性があるためです。

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

これらはどこcallAdapterから来たのでしょうか? 前に入力した場所に戻ります

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

この種の最も一般的な呼び出しはこれDefaultCallAdapterFactoryに違いなく最終的にはアダプターを入手することが重要です。ExecutorCallbackCall

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

サンプルコードを振り返ってみると、呼び出しをservice method取得した後Call、それを呼び出すenqueue、つまり最終的に入りましたExecutorCallbackCall.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);
        }
      });
    }
  });
}

リクエストは、途中であろうとOkHttpCallなかろうと、実際にはユーザーによって開始され、スケーラビリティと互換性のための設計にすぎません。HttpServiceMethodCallAdapter

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

実際、この時点で、Retrofit の一般的な設計は終了しました。鍵はどこにHttpServiceMethodありますかCallAdapter

Rxjava呼び出し

本が上記の通常の呼び出しに接続されている場合、Rxjava のリンクの場合、違いは次のとおりです。CallAdapter

addCallAdapterFactory(RxJava2CallAdapterFactory.create());

見てください、同じことfactory.getが得られますRxJavaCallAdapter

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

通話を一時停止する

レトロフィットと組み合わせたコルーチンの場合、通常はscope.launch{ service.method() }になります。

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

サスペンドの場合は、rxjava のスレッド切り替え機能を置き換えるコルーチンと組み合わせる必要があります。これは特別なことにはHttpServiceMethod相当せずSuspendForResponse(主体差不多,那就挑这个将)\SuspendForBody、得られるものは最も一般的なものです。CallAdapterCallExecutorCallbackCall

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

検索して検索して、突然振り返ると、その人は薄暗い場所にいて、ついにExecutorCallbackCall電話がかかりました。これが電話suspendCancellableCoroutineを切るアプリケーションであり、resume通話と組み合わせています

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

要約する

実際、Retrofit の設計で最も興味深い点は、动态代理メカニズムの連携によりHttpServiceMethod、拡張された設計がmethod粒度で実現され、全体的な互換性と分離の点でケーキにおまけを付け加えたようなものCallAdapterです。Converter

これは、実際には、アノテーションの適用可能なシナリオ (通常はインストルメンテーションまたはコード生成のための APT に限定される可能性があります) の非常に優れたリファレンスを提供します。

• Retrofit は、動的プロキシ メカニズムを使用して、Proxy.newProxyInstance メソッドを通じてインターフェイスのプロキシ クラスを作成し
、すべてのメソッド呼び出しを内部 InvocationHandler に委任して処理します
• InvocationHandler は、メソッド アノテーションに基づいて ServiceMethod オブジェクトを作成します。ServiceMethod
は抽象クラスです。 URL、HTTP メソッド、パラメータ、リクエスト本文などの情報
ServiceMethod の特定の実装クラスは HttpServiceMethod で、これは Call オブジェクトを作成し、Retrofit によって設定されたCallAdapter
および Converter に従って応答データを解析します。
Call オブジェクトの変換方法を定義するインターフェイスです。Retrofit は、Observable、Deferred などの他のタイプのデフォルトの CallAdapter を提供します。また、addCallAdapterFactory メソッドを通じてカスタム CallAdapter を追加することもできます。Converter は、変換方法を定義する
インターフェイス
ですリクエスト本文と応答本文を Java オブジェクトに変換
します。Retrofit は GsonConverter などの一部の組み込みコンバータを提供し、addConverterFactory メソッドを通じてカスタム コンバータを追加することもできます。

Androidの勉強メモ

Android パフォーマンスの最適化: https://qr18.cn/FVlo89
Android Vehicle: https://qr18.cn/F05ZCM
Android リバース セキュリティ調査ノート: https://qr18.cn/CQ5TcL
Android フレームワークの原則: https://qr18.cn/AQpN4J
Android オーディオとビデオ: https://qr18.cn/Ei3VPD
Jetpack (Compose を含む): https://qr18.cn/A0gajp
Kotlin: https://qr18.cn/CdjtAF
Gradle: https://qr18.cn/DzrmMB
OkHttp ソース コード分析ノート: 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過去の面接の質問: https://qr18.cn/CKV8OZ
2023 年最新の Android 面接の質問集: https://qr18.cn/CgxrRy
Android 車両開発の就職面接の演習:https://qr18.cn/FTlyCJ
音声とビデオの面接の質問:https://qr18.cn/AcV6Ap

おすすめ

転載: blog.csdn.net/weixin_61845324/article/details/132167171