Interpretación del marco | Análisis de diseño de reacondicionamiento

Autor: Cálculo_Xiao Wang

Retrofit es un cliente HTTP con seguridad de tipos que convierte las API HTTP en interfaces Java a través de anotaciones y utiliza proxies dinámicos, CallAdapter y Converter para iniciar solicitudes y analizar respuestas.

Este artículo se enfoca en el diseño arquitectónico de Retrofit y no lo explicará注解解析能力 en detalle.

Este artículo está basado enretrofit:2.6.2

ejemplo

Este ejemplo solo toma como ejemplo la solicitud más básica iniciada por retrofit.Con respecto a paquetes como kotlin suspend o rxjava, se analizarán juntos en la etapa de código fuente.

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

Análisis de código fuente

Por lo general, en el uso de la actualización, se usará junto con las palabras clave de suspensión rxjava o kotlin, pero estos dos pasos son los mismos que el código de muestra:

  1. Obtener el objeto de servicio
  2. Llame al método de servicio
// 通过Retrofit创建接口的实现类
GitHubService service = retrofit.create(GitHubService.class);

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

En cuanto al retrofit, lo que más nos llama la atención es cómo se enlaza con OkHttp para realizar peticiones con él. Se pasa la solicitud iniciada en OkHttp Call.enqueue(), por lo que la dirección de destino de este artículo es la llamada de este método.

Obtener el objeto de servicio

retrofit.createLo que obtienes es newProxyInstanceel objeto de servicio generado por el proxy dinámico. El enfoque está en los métodos que contiene invoke. En general, la llamada final es loadServiceMethod(method)la ejecución de invocar() . Luego, vea qué objeto devuelve y qué hace su invocación() . qué

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

Cada método de Servicio eventualmente corresponde a uno HttpServiceMethod, y también tiene varias clases de implementación CallAdapted(usualmente), SuspendForResponse(usado por suspensión),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);
    }
}

Como se mencionó anteriormente invoke(), continuemos. Para HttpServiceMethodvarias subclases de , ninguna de ellas implementó este método, y encontró que fue llamado en su clase principal, adaptdejando que sus subclases lo implementen

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

Llame al método de servicio

En createlos detalles del proxy dinámico, sabemos que la invocación de cada método del servicio de actualización se asignará al tipo correspondiente deHttpServiceMethod.adapt()

llamada normal

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

En el caso más común, el valor devuelto es el Call original, que corresponde a CallAdapted.Sin embargo, el dolor de cabeza aquí es que tiene otra capa. El motivo del diseño aquí es ser compatible con escenarios como Rxjava

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

callAdapter¿De dónde vienen estos ? Volver al lugar donde se hizo la entrada anterior

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

Para la llamada más común de este tipo, debe DefaultCallAdapterFactoryser así , y obtener el adaptador es, en última instancia,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);
    }
  };
}

Volviendo al código de muestra, después service methodde obtener la llamada Call, llámela enqueue, es decir, finalmente ingresó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);
        }
      });
    }
  });
}

La solicitud es realmente iniciada por el usuario OkHttpCall, ya sea que esté en el medio HttpServiceMethodo no CallAdapter, no es más que un diseño de escalabilidad y compatibilidad.

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

De hecho, en este punto, el diseño general de Retrofit ha terminado. donde HttpServiceMethody CallAdapteresta la llave

Llamada rxjava

Cuando el libro está conectado a la llamada ordinaria anterior, para el enlace de Rxjava, la diferencia es queCallAdapter

addCallAdapterFactory(RxJava2CallAdapterFactory.create());

ver lo mismo factory.getobtendrá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;
}

suspender llamada

Para rutinas combinadas con retroadaptación, generalmente es scope.launch{ service.method() }

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

Para suspender, debe combinarse con coroutines, que reemplaza la capacidad de cambio de subprocesos de rxjava, que no HttpServiceMethodcorresponde a nada sofisticado SuspendForResponse(主体差不多,那就挑这个将)\SuspendForBody, y lo que obtiene es lo más común.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);
  }
}

Buscando y buscando, de repente mirando hacia atrás, la persona estaba en un lugar con poca luz y finalmente ExecutorCallbackCallla llamaron, aquí hay una suspendCancellableCoroutineaplicación para colgar, combinada con resumellamadas

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

Resumir

De hecho, lo más intrigante sobre el diseño de Retrofit es que a través de 动态代理la cooperación del mecanismo HttpServiceMethod, el diseño extendido se realiza en methodla granularidad, y CallAdapterequivale Convertera agregar la guinda del pastel en términos de compatibilidad general y desacoplamiento.

En realidad, esto proporciona una muy buena referencia para los escenarios aplicables de anotaciones, que generalmente pueden limitarse a APT para instrumentación o generación de código.

• Retrofit utiliza un mecanismo de proxy dinámico para crear una clase de proxy para la interfaz a través del método Proxy.newProxyInstance
y delega todas las llamadas de método al InvocationHandler interno para su procesamiento
• InvocationHandler es responsable de crear objetos ServiceMethod basados ​​en anotaciones de métodos.
ServiceMethod es una clase abstracta que encapsula la solicitud Información, como URL, método HTTP, parámetros, cuerpo de la solicitud ,
etc. una interfaz que define cómo convertir objetos Call. Retrofit proporciona un CallAdapter predeterminado para otros tipos, como Observable, Deferred, etc. , y también puede agregar un CallAdapter personalizado a través del método addCallAdapterFactory. Converter es una interfaz que define cómo convertir el cuerpo de la solicitud y el cuerpo de la respuesta en objetos Java Retrofit proporciona Algunos convertidores integrados, como GsonConverter, también pueden agregar convertidores personalizados a través del método addConverterFactory




Notas de estudio de Android

Optimización del rendimiento de Android: https://qr18.cn/FVlo89
Vehículo de Android: https://qr18.cn/F05ZCM
Notas de estudio de seguridad inversa de https://qr18.cn/CQ5TcL
Android: Principios del marco de trabajo de Android: https://qr18.cn/AQpN4J
Audio y video de Android: https://qr18.cn/Ei3VPD
Jetpack (incluido Compose): https://qr18.cn/A0gajp
Kotlin: https://qr18.cn/CdjtAF
Gradle: https://qr18.cn/DzrmMB
OkHttp Notas de análisis de código fuente: https://qr18.cn/Cw0pBD
Flutter: https://qr18.cn/DIvKma
Android Eight Knowledge Body: https://qr18.cn/CyxarU
Notas principales de Android: https://qr21.cn/CaZQLo
Android Preguntas de entrevistas anteriores: https://qr18.cn/CKV8OZ
2023 Última colección de preguntas de entrevistas de Android: https://qr18.cn/CgxrRy
Ejercicios de entrevistas de trabajo de desarrollo de vehículos de Android: https://qr18.cn/FTlyCJ
Preguntas de entrevistas de audio y video:https://qr18.cn/AcV6Ap

Supongo que te gusta

Origin blog.csdn.net/weixin_61845324/article/details/132167171
Recomendado
Clasificación