Note: This article is based on Retrofit version 2.0 and is analyzed with RxJava.
Com. Squareup. Retrofit2: retrofit: 2.0.0
Com. Squareup. Retrofit2: converter – gson: 2.0.0
Com. Squareup. Retrofit2: adapter – rxjava: 2.0.0
Retrofit adapts a Java interface to HTTP calls by using annotations on the declared methods to how requests are made.
This article analyzes Retrofit’s collaboration with RxJava to gain an in-depth understanding of how Retrofit works and to answer questions in my mind.
confusion
- How do we send the request after we call the interface’s methods? What’s going on behind this?
- How did Retrofit and OkHttp work together?
- What exactly happens to the data in Retrofit? How does it return rxJava.Observable?
public interface ApiService{
@GET("data/Android/"+ GankConfig.PAGE_COUNT+"/{page}")
Observable<GankResponse> getAndroid(@Path("page") int page);
}
//Builder pattern to build Retrofit
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(baseUrl)
.addConverterFactory(GsonConverterFactory.create(new GsonBuilder(a).create()))
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.client(okHttpClient)
.build();
//Generate a service(not a service in the four components) using retrofit.create method
ApiService service = retrofit.create(ApiService.class);
//Initiate a request to retrieve data
Observable<GankResponse> observable= service.getAndroid(1);
observable..Copy the code
Retrofit is simply configured to request data from the server, super simple.
Retrofit.create method analysis
Retrofit’s Create method, as the entry point for Retrofit, is of course the first to be analyzed.
public <T> T create(final Class<T> service) {
//Verify that the interface is reasonable
Utils.validateServiceInterface(service);
//The default false
if (validateEagerly) {
eagerlyValidateMethods(service);
}
//A dynamic proxy
return (T) Proxy.newProxyInstance(service.getClassLoader(), newClass<? >[] { service },new InvocationHandler() {
//Platform abstraction, specify the default CallbackExecutor CallAdapterFactory, Android platform is Android (Java8 iOS we don't care)
private final Platform platform = Platform.get();
//This is where all the method calls in ApiService go
@Override public Object invoke(Object proxy.Method method.Object.args)
throws Throwable {
// If the method is a method from Object then defer to normal invocation.
//The comment already indicates that Object methods do not matter
if (method.getDeclaringClass() = = Object.class) {
return method.invoke(this, args);
}
//Java8 default method, Android does not support the default method, so do not need to manage for the time being
if (platform.isDefaultMethod(method)) {
return platform.invokeDefaultMethod(method, service, proxy, args);
}
//Focus on the latter analysis
//Generate a ServiceMethod for Method
ServiceMethod serviceMethod = loadServiceMethod(method);
//Wrap it as OkHttpCall
OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args); //request
return serviceMethod.callAdapter.adapt(okHttpCall); }}); }Copy the code
As you can see in the code above, Retrofit’s main principle is to leverage Java’s dynamic proxy technology, The ApiService method calls on InvocationHandler. Invoke, and then build the ServiceMethod, OKHttpCall, return callAdapter. Adapt the results.
To find out, you need to analyze those last three lines of code.
One step at a time.
I think of ServiceMethod as an abstraction of the concrete methods of the interface. It is responsible for parsing the parameters of its corresponding method (it has methods like parseHeaders), such as annotations (@get), input arguments, Is also responsible for obtaining callAdapter, responseConverter Retrofit such as configuration, good for okhttp3. Behind the Request parameter prepare its toRequest OkHttp provide Request, It carries all the parameters required for subsequent Http requests.
Re-analyze the loadServiceMethod, which is simpler.
//The definition of serviceMethodCache
private final Map<Method.ServiceMethod> serviceMethodCache = new LinkedHashMap<>(a);//Obtain the ServiceMethod of method
ServiceMethod loadServiceMethod(Method method) {
ServiceMethod result;
synchronized (serviceMethodCache) {
//Get it from the cache first
result = serviceMethodCache.get(method);
if (result = = null) {
//If there is none in the cache, create one and save it to the cache
result = new ServiceMethod.Builder(this, method).build();
serviceMethodCache.put(method, result); }}return result;
}Copy the code
The loadServiceMethod method, which generates a ServiceMethod for method and caches the ServiceMethod.
Dynamic proxies have some performance costs, and the creation of ServiceMethod is accompanied by various annotation parameter parsing, which is also time consuming, plus an App call interface is very frequent, if every interface request needs to be regenerated, then there is a potential waste of resources and performance damage. So we did a cache here to make it more efficient.
OkHttpCall
OkHttpCall = new OkHttpCall<>(serviceMethod, args); An OkHttpCall is generated for ServiceMethod and args(parameters).
As you might guess from the name, OkHttpCall is a combination wrapper around okHttp3.Call, which it is. OkHttpCall has a member okhttp3.Call rawCall.
The last return serviceMethod. CallAdapter. Adapt (okHttpCall) seems to be a reached the final step.
If the front is to prepare, so here is really to action.
To analyze, involves callAdapter here, is the configuration of the Retrofit by us addCallAdapterFactory method in the incoming RxJavaCallAdapterFactory. The create (), The example is RxJavaCallAdapterFactory.
The general process of instance generation is as follows:
ServiceMethod.Bulider.Build()
->ServiceMethod.createCallAdapter()
->retrofit.callAdapter()
– > adapterFactories traversal
– > to RxJavaCallAdapterFactory. Eventually the get () # getCallAdapter ()
->return return new SimpleCallAdapter(observableType, scheduler);
Because of RxJava, we end up with a SimpleCallAdapter as the callAdapter, so we can analyze the SimpleCallAdapter adapt method:
The CallOnSubscriber involved here is given below:
@Override public <R> Observable<R> adapt(Call<R> call) {
//OkHttpCall OkHttpCall OkHttpCall = new OkHttpCall<>(serviceMethod, args
Observable<R> observable = Observable.create(new CallOnSubscribe<>(call)) //
.flatMap(new Func1<Response<R>.Observable<R>>() {
@Override public Observable<R> call(Response<R> response) {
if (response.isSuccessful()) {
return Observable.just(response.body());
}
return Observable.error(new HttpException(response)); }});if (scheduler ! = null) {
return observable.subscribeOn(scheduler);
}
returnobservable; }}Copy the code
static final class CallOnSubscribe<T> implements Observable.OnSubscribe<Response<T>> {
private final Call<T> originalCall;
CallOnSubscribe(Call<T> originalCall) {
this.originalCall = originalCall;
}
@Override public void call(final Subscriber<? super Response<T>> subscriber) {
// Since Call is a one-shot type, clone it for each new subscriber.
final Call<T> call = originalCall.clone();
// Attempt to cancel the call if it is still in-flight on unsubscription.
//Unsubscribe request when we unsubscribe awesome
subscriber.add(Subscriptions.create(new Action0() {
@Override public void call() {
call.cancel(); }}));try {
//Call is an instance of OkHttpCall
Response<T> response = call.execute();
if (!subscriber.isUnsubscribed()) {
subscriber.onNext(response); }}catch (Throwable t) {
Exceptions.throwIfFatal(t);
if (!subscriber.isUnsubscribed()) {
subscriber.onError(t);
}
return;
}
if (!subscriber.isUnsubscribed()) {
subscriber.onCompleted(); }}}Copy the code
The simplecalladapter. adapt is very simple. Create an Observable that takes a Response from a CallOnSubscribe, converts it to an Observable through a flatMap, and returns it. The task of sending a request for data is in the CallOnSubscribe. Call method. And it ends up in okHttpCall.execute.
// OkHttpCall.execute
@Override public Response<T> execute() throws IOException {
okhttp3.Call call;
synchronized (this) {
//The same request cannot be executed twice
if (executed) throw new IllegalStateException("Already executed.");
executed = true;
//. Ellipsis Execption
call = rawCall;
if (call = = null) {
try {
//Create okhttp3. Call
call = rawCall = createRawCall();
} catch (IOException | RuntimeException e) {
creationFailure = e;
throwe; }}}if (canceled) {
call.cancel();
}
//The call is okHttp3. The call is actually handed over to OkHttp to send the request
return parseResponse(call.execute());
}
//Parse the response
Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
//. Omit some processing and only show the key code
try {
T body = serviceMethod.toResponse(catchingBody);
return Response.success(body, rawResponse);
} catch (RuntimeException e) {
catchingBody.throwIfCaught();
throwe; }}// serviceMethod.toResponse
T toResponse(ResponseBody body) throws IOException {
//Remember? So this is the Converter when we configure Retrofit
return responseConverter.convert(body);
}Copy the code
The okHttp3.call is generated in the okHttpCall.execute () method and sent to the OkHttpClient to send the request. The Response is processed by our configured Converter(in this case, GsonConverterFactory), which returns to the SimpleCallAdapter for processing and returns the Observable we ultimately need.
Process analysis flowchart summary
The overall flow chart is as follows:
Answer questions
We can answer the question before.
Retrofit uses dynamic proxies to set up proxies for our defined interfaces. When we Call the interface’s methods, Retrofit intercepts them and then goes through a series of processes, such as parsing the method’s annotations, to generate OKHttp resources such as Call Requests. Finally, the request is sent to OkHttp, which is processed by callAdapter and convertr and finally gets the data we need.
Second question: How did Retrofit and OkHttp work together?
In Retrofit, ServiceMethod takes all the parameters of an Http request, OkHttpCall is wrapped as a combination of okHttp3. call, and the two work together. Generate the Request and okHttp3.call required by OkHttp and hand it to OkHttp to send the Request. (Call.execute () is used in this context)
Retrofit encapsulates OkHttp and adds functionality and extensions that reduce development costs.
The data in Retrofit is actually handed over to callAdapter and Converter for processing, The callAdapter converts the okHttpCall into the desired Observable type (in the context of this article), and the Converter converts the data returned by the server into a concrete entity class.
summary
The source code for Retrofit is actually very easy to follow and understand, unlike looking at the framework code and then disappearing.
Retrofit’s code is really beautiful, and it’s a great way to use design patterns.
Retrofit analysis – beautiful decoupling routines