Autor: Calculus_Xiao Wang
O Retrofit é um cliente HTTP seguro que converte APIs HTTP em interfaces Java por meio de anotações e usa proxies dinâmicos, CallAdapter e Converter para iniciar solicitações e analisar respostas.
Este artigo se concentra no projeto arquitetônico do Retrofit e não
注解解析能力
o detalharáEste artigo é baseado em
retrofit:2.6.2
exemplo
Este exemplo pega apenas a requisição mais básica iniciada por retrofit como exemplo. Em relação a pacotes como kotlin suspend ou rxjava, eles serão analisados juntos na etapa de código-fonte
// 定义一个接口,用注解描述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álise do código-fonte
Normalmente, no uso de retrofit, ele será usado em conjunto com as palavras-chave rxjava ou kotlin suspend, mas essas duas etapas são as mesmas do código de exemplo:
- Obter o objeto Service
- Chame o método Service
// 通过Retrofit创建接口的实现类
GitHubService service = retrofit.create(GitHubService.class);
// 调用接口方法,获取Call对象
Call<List<Repo>> repos = service.listRepos("octocat");
Quanto ao retrofit, o que mais nos interessa é como ele está vinculado ao OkHttp para fazer solicitações com ele. A requisição iniciada em OkHttp é passada Call.enqueue()
, então a direção alvo deste artigo é a chamada deste método
Obter o objeto Service
retrofit.create
O que você obtém é newProxyInstance
o objeto Service gerado pelo proxy dinâmico. O foco está nos métodos nele invoke
. Em geral, a chamada final é loadServiceMethod(method)
o invoke() executado . Em seguida, veja qual objeto ele retorna e o que seu invoke() faz. o que
// 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 Service eventualmente corresponde a um HttpServiceMethod
, e também possui várias classes de implementação CallAdapted
(geralmente), SuspendForResponse
(usadas por 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);
}
}
Como mencionado anteriormente invoke()
, vamos continuar. Para HttpServiceMethod
várias subclasses, nenhuma delas implementou esse método e descobriu que ele era chamado em sua classe pai, adapt
deixando para suas subclasses implementá-lo
@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();
}
Chame o método Service
Nos create
detalhes do proxy dinâmico, sabemos que a invocação de cada método do serviço de retrofit será mapeada para o tipo deHttpServiceMethod.adapt()
chamada 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) {
// 处理失败情况
}
});
No caso mais comum, o valor de retorno é o Call original, que corresponde a CallAdapted
.No entanto, a dor de cabeça aqui é que ele tem outra camada. O motivo do design aqui é ser compatível com cenários como Rxjava
// CallAdapted.java
@Override protected ReturnT adapt(Call<ResponseT> call, Object[] args) {
return callAdapter.adapt(call);
}
De onde vêm esses callAdapter
? Retorne ao local onde a 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 a chamada mais comum desse tipo, deve DefaultCallAdapterFactory
ser essa , e obter o adaptador é, em última análise,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);
}
};
}
Olhando novamente para o código de exemplo, depois que a chamada service method
for obtida Call
, chame-a enqueue
, ou seja, ela finalmente entrouExecutorCallbackCall.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);
}
});
}
});
}
A requisição na verdade é iniciada pelo usuário OkHttpCall
, seja no meio HttpServiceMethod
ou não CallAdapter
, nada mais é do que um projeto para escalabilidade e compatibilidade.
// 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
}
}
});
}
Na verdade, neste ponto, o design geral do Retrofit acabou. onde HttpServiceMethod
e CallAdapter
é a chave
chamada Rxjava
Quando o livro está conectado à chamada ordinária acima, para a ligação do Rxjava, a diferença é queCallAdapter
addCallAdapterFactory(RxJava2CallAdapterFactory.create());
veja o mesmo factory.get
vai conseguirRxJavaCallAdapter
// 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 chamada
Para corrotinas combinadas com retrofit, geralmente é scope.launch{ service.method() }
CoroutineScope(Dispatchers.IO).launch {
try {
val repos = service.listRepos("octocat")
// 处理响应数据,通常会给一个callback,外面再通过livedata去发射处理好的数据
} catch (e: Exception) {
// 处理异常情况
}
}
Para suspender, ele deve ser combinado com corrotinas, que substituem a capacidade de troca de thread do rxjava, que HttpServiceMethod
corresponde a nada extravagante SuspendForResponse(主体差不多,那就挑这个将)\SuspendForBody
, e o que você obtém é o mais comumCallAdapter
Call
ExecutorCallbackCall
// 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);
}
}
Procurando e procurando, de repente olhando para trás, a pessoa estava em um local mal iluminado e finalmente ExecutorCallbackCall
foi chamada, aqui está um suspendCancellableCoroutine
aplicativo de desligar, combinado com resume
a chamada
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 fato, a coisa mais intrigante sobre o design do Retrofit é que, por meio 动态代理
da cooperação do mecanismo HttpServiceMethod
, o design estendido é realizado na method
granularidade e CallAdapter
equivale Converter
a adicionar a cereja do bolo em termos de compatibilidade e desacoplamento geral.
Na verdade, isso fornece uma referência muito boa para os cenários aplicáveis de anotações, que geralmente podem ser limitados ao APT para instrumentação ou geração de código
• Retrofit usa um mecanismo de proxy dinâmico para criar uma classe de proxy para a interface por meio do método Proxy.newProxyInstance
e delega todas as chamadas de método para o InvocationHandler interno para processamento
• InvocationHandler é responsável por criar objetos ServiceMethod com base em anotações de método.
ServiceMethod é uma classe abstrata que encapsula a solicitação. Informações, como URL, método HTTP, parâmetros, corpo da solicitação, etc.
A classe de implementação específica do ServiceMethod é HttpServiceMethod,
que cria objetos Call e analisa os dados de resposta de acordo com o CallAdapter e o Converter configurados pelo Retrofit.
CallAdapter é uma interface que define como converter objetos Call. O Retrofit fornece um CallAdapter padrão para outros tipos, como Observable, Deferred, etc.
, e você também pode adicionar um CallAdapter personalizado por meio do método addCallAdapterFactory.
Converter é uma interface que define como converter o corpo da solicitação e o corpo da resposta em objetos Java
. O Retrofit fornece Alguns conversores integrados, como o GsonConverter, também podem adicionar conversores personalizados por meio do método addConverterFactory
notas de estudo do Android
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 (incluindo 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 Perguntas da entrevista anterior: https://qr18.cn/CKV8OZ
Coleção de perguntas da entrevista mais recente do Android de 2023: https://qr18.cn/CgxrRy
Exercícios de entrevista de trabalho de desenvolvimento de veículo Android: https://qr18.cn/FTlyCJ
Perguntas de entrevista de áudio e vídeo:https://qr18.cn/AcV6Ap