preface
Since the previous project was built with MVP architecture, which is a combination of RxJava + Glide + OKHttp + Retrofit and other open source frameworks, it was just on the use level without in-depth research. Recently, we plan to attack all of them. Students who have not paid attention to them can pay attention to a wave first. After reading this series of articles, you’ll be much more comfortable handling questions (whether in an interview or on the job) if you know how they work.
Android picture loading framework Glide 4.9.0 (a) From the perspective of source code analysis Glide execution process
Android Image loading framework Glide 4.9.0 (2) Analysis of Glide cache strategy from the perspective of source code
From the perspective of source code analysis of Rxjava2 basic execution process, thread switching principle
OKHttp3 (a) synchronous and asynchronous execution process is analyzed from the perspective of source code
Analyze the charm of OKHttp3 (ii) interceptor from the source point of view
Analyze OKHttp3 cache strategy from source code perspective
Analyze Retrofit network request from source code point of view, including RxJava + Retrofit + OKhttp network request execution process
introduce
Retrofit and OKHttp are both open source projects from Square, and both Retrofit and OKHttp are part of the HTTP Web request framework, so why should we analyze it if both are part of the web framework? For those of you who have used the Retrofit framework, the underlying version ofit is actually OKHttp based for web access. Strictly speaking, I personally think Retrofit is based on OKHttp repackaging, which is intended to be simpler to use.
Simple to use
This chapter focuses on analyzing the Retrofit source code, so if you’re unsure about how to use the Retrofit API, Here are to hong ocean great god wanandroid open API: wanandroid.com/wxarticle/c… To make network requests:
Define the Retrofit network request interface
public interface APIService {
// Define the interface Retrofit + OKHttp network access according to Retrofit requirements
@GET("/wxarticle/chapters/json")
Call<JsonObject> getWXarticle(a);
// Define interface RxJava + Retrofit + OKHttp network access according to Retrofit requirements
@GET("/wxarticle/chapters/json")
Observable<JsonObject> getWXarticle(a);
}
Copy the code
Initialize the Retrofit instance
public static final String API_URL = "https://wanandroid.com";
//1. Configure OKHttpClient to add view Request/Response data interceptor
OkHttpClient okHttpClient = new OkHttpClient.Builder().addInterceptor(new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
// Prints the request data before the request
Request request = chain.request();
Log.d(TAG, request.toString());
// Prints the server response data
Response response = chain.proceed(request);
MediaType mediaType = response.body().contentType();
String content = response.body().string();
Log.d("JSON:", content);
Log.d("MediaType:", mediaType.toString());
return response.newBuilder().
body(ResponseBody.create(mediaType, content)).
build();
}
}).build();
//2. Build Retrofit objects
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(API_URL) //http API
.addConverterFactory(GsonConverterFactory.create()) // Configure GSON transformation
.addCallAdapterFactory(RxJava2CallAdapterFactory.createAsync()) // Configure RxJava to support asynchronous access to create synchronization
.client(okHttpClient)// Proactively configure OKhttpClient
.build();
//3. Get the Retrofit request object
APIService api = retrofit.create(APIService.class);
Copy the code
Retrofit + OKHttp
Call<JsonObject> call = api.getWXarticles();
//OKHttp asynchronously accesses the network
call.enqueue(new Callback<JsonObject>() {
@Override
public void onResponse(Call<JsonObject> call, retrofit2.Response<JsonObject> response) {
Log.d(TAG, "Retrofit + OKHttp "+response.body().toString());
}
@Override
public void onFailure(Call<JsonObject> call, Throwable t) {
Log.d(TAG, "Retrofit + OKHttp "+t.getMessage()); }});Copy the code
RxJava + Retrofit + OKHttp
Observable<JsonObject> observable = api.getWXarticle();
observable.subscribeOn(Schedulers.io())
.subscribeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<JsonObject>() {
@Override
public void onSubscribe(Disposable d) {
Log.d(TAG, "RxJava + Retrofit + OKHttp subscription succeeded");
}
@Override
public void onNext(JsonObject s) {
Log.d(TAG, "RxJava + Retrofit + OKHttp "+s.toString());
}
@Override
public void onError(Throwable e) {
Log.d(TAG, "RxJava + Retrofit + OKHttp "+e.getMessage());
}
@Override
public void onComplete(a) {
Log.d(TAG, "RxJava + Retrofit + OKHttp onComplete"); }});Copy the code
Output result:
/ / request address com. Dn_alan. Myapplication. MainActivity: https://wanandroid.com/wxarticle/chapters/json/Request/Request information com. Dn_alan. Myapplication. MainActivity: Request{method=GET, url=https://wanandroid.com/wxarticle/chapters/json, Tags = {class retrofit2. Invocation = com. Dn_alan. Myapplication. APIService. GetWXarticles () []}}; / / Rxjava subscribing com. Dn_alan. Myapplication. MainActivity: Retrofit RxJava + + OKHttp subscribing / / Retrofit RxJava + + OKHttp server returned data D/com. Dn_alan. Myapplication. MainActivity: RxJava + Retrofit + OKHttp {"data":[{"children":[],"courseId":13," ID ":408,"name":"....} //RxJava + Retrofit + OKHttp Network request is completed D/com. Dn_alan. Myapplication. MainActivity: Retrofit RxJava + + OKHttp onComplete / / Retrofit + OKHttp network request is completed D/com. Dn_alan. Myapplication. MainActivity: Retrofit + OKHttp {"data":[{"children":.....}Copy the code
We can see that RxJava + Retrofit + OKHttp, Retrofit + OKHttp were successfully requested by printing, and we can see that Retrofit will be splicing together by requesting the address. This use is not very cool. If you are not familiar with the source code for RxJava and OKHttp, I suggest you take a look at my previous articles and review them, because I will cover them in this chapter, but I won’t go through them in detail, because the focus is on Retrofit.
Here’s where we really start to look at how Retrofit works with OKHttp and RxJava for web access.
Source code analysis
Retrofit class introduction
packageretrofit2; .public final class Retrofit {...// omit the code
Retrofit(okhttp3.Call.Factory callFactory, HttpUrl baseUrl,
List<Converter.Factory> converterFactories, List<CallAdapter.Factory> callAdapterFactories,
@Nullable Executor callbackExecutor, boolean validateEagerly) {
this.callFactory = callFactory; // Configure OKhttp
this.baseUrl = baseUrl;// The configured Http address
this.converterFactories = converterFactories; // Configure transformation functions such as GSON,
this.callAdapterFactories = callAdapterFactories; // Configure RxJava
this.callbackExecutor = callbackExecutor;
this.validateEagerly = validateEagerly;
}
@SuppressWarnings("unchecked") // Single-interface proxy creation guarded by parameter safety.
public <T> T create(final Class<T> service) {...// The main code for dynamic proxy is explained later}...// Omit some code
public static final class Builder {
private final Platform platform;
private @Nullable okhttp3.Call.Factory callFactory;
private @Nullable HttpUrl baseUrl;
private final List<Converter.Factory> converterFactories = new ArrayList<>();
private final List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>();
private @Nullable Executor callbackExecutor;
private boolean validateEagerly;
Builder(Platform platform) {
this.platform = platform;
}
public Builder(a) {
this(Platform.get()); }...// Omit some code
/** ** * build OKHttpClient */
public Builder client(OkHttpClient client) {
return callFactory(checkNotNull(client, "client == null"));
}
/** ** build OKHttpClient call */
public Builder callFactory(okhttp3.Call.Factory factory) {
this.callFactory = checkNotNull(factory, "factory == null");
return this;
}
/**
* Set the API base URL.
*
* @seeBuild URL */
public Builder baseUrl(String baseUrl) {
checkNotNull(baseUrl, "baseUrl == null");
return baseUrl(HttpUrl.get(baseUrl));
}
/** Build GSON */
public Builder addConverterFactory(Converter.Factory factory) {
converterFactories.add(checkNotNull(factory, "factory == null"));
return this;
}
/** * build RXJava {@link Call}.
*/
public Builder addCallAdapterFactory(CallAdapter.Factory factory) {
callAdapterFactories.add(checkNotNull(factory, "factory == null"));
return this;
}
public Retrofit build(a) {
// url
if (baseUrl == null) {
throw new IllegalStateException("Base URL required.");
}
/ / get the OKHttpClient
okhttp3.Call.Factory callFactory = this.callFactory;
if (callFactory == null) {
callFactory = new OkHttpClient();
}
Executor callbackExecutor = this.callbackExecutor;
if (callbackExecutor == null) {
callbackExecutor = platform.defaultCallbackExecutor();
}
// Initialize a container
List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
/ / add a default DefaultCallAdapterFactory for assisting OKHttp network requests
callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor));
// Initialize the default data converter
List<Converter.Factory> converterFactories = new ArrayList<>(
1 + this.converterFactories.size() + platform.defaultConverterFactoriesSize());
converterFactories.add(new BuiltInConverters());
converterFactories.addAll(this.converterFactories);
converterFactories.addAll(platform.defaultConverterFactories());
// Build Retrofit objects
return newRetrofit(callFactory, baseUrl, unmodifiableList(converterFactories), unmodifiableList(callAdapterFactories), callbackExecutor, validateEagerly); }}}Copy the code
Based on Retrofit’s internal code implementation, we can see that two main things are done
- The CREATE dynamic proxy executes the defined annotation interface functions (the internal implementation is covered in the next section).
- Build something like RxJava adaptation, transform GSON, network request OKHTTP configuration, etc.
So retrofit Create is kind of the core approach.
Retrofit Create dynamic proxy implementation
public <T> T create(final Class<T> service) {
/ / 1. Check the service
Utils.validateServiceInterface(service);
if (validateEagerly) {
eagerlyValidateMethods(service);
}
// Execute dynamic proxy
return (T) Proxy.newProxyInstance(service.getClassLoader(), newClass<? >[] { 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 {
// Is the Object type
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
if (platform.isDefaultMethod(method)) {// This is usually not implemented
return platform.invokeDefaultMethod(method, service, proxy, args);
}
//2. Actually execute the specific functions in the defined interface
// invoke the internal ServiceMethod function
returnloadServiceMethod(method).invoke(args ! =null? args : emptyArgs); }}); }Copy the code
If you are familiar with JAVA design patterns, you should know that dynamic proxy patterns are used here. If you are not familiar with dynamic proxy patterns or other design patterns, you can read my previous design pattern series articles, starting with note 2 loadServiceMethod
private finalMap<Method, ServiceMethod<? >> serviceMethodCache =newConcurrentHashMap<>(); ServiceMethod<? > loadServiceMethod(Method method) {//1. Get the ServiceMethod in the cacheServiceMethod<? > result = serviceMethodCache.get(method);if(result ! =null) return result;//1.1 Returns if it exists
// add a synchronization lock
synchronized (serviceMethodCache) {
// The lock is locked again
result = serviceMethodCache.get(method);
if (result == null) {// If it is empty
result = ServiceMethod.parseAnnotations(this, method); // Start parsing annotations on the interface
// The parsed data is cached to avoid parsing the same data againserviceMethodCache.put(method, result); }}return result;
}
Copy the code
We know from the code above that we take the ServiceMethod object from the Map, and if it’s not in the cache we parse it and define the Retrofit network request interface to build the ServiceMethod, ConcurrentHashMap parsing is recommended for those unfamiliar with ConcurrentHashMap parsing.
Below we see ServiceMethod parseAnnotations
static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
//1. Get a RetroFIT request object from a function method in the Retrofit instance and interface
RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);
//2. Get the return object in the annotation: http://loveshisong.cn/%E7%BC%96%E7%A8%8B%E6%8A%80%E6%9C%AF/2016-02-16-Type%E8%AF%A6%E8%A7%A3.html
Type returnType = method.getGenericReturnType();
// Internal check type
if (Utils.hasUnresolvableType(returnType)) {
...// Throw an exception
}
if (returnType == void.class) {
throw methodError(method, "Service methods cannot return void.");
}
//3. Perform HttpServiceMethod parsing
return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
}
Copy the code
We know that the code above does three main things
- Get the request object
- Determines the return value type of the function in the calling interface
- Perform parsing action
We’ll look at RequestFactory. ParseAnnotations (retrofit, method) internal implementation
// Defined as final cannot be inherited
final class RequestFactory {
static RequestFactory parseAnnotations(Retrofit retrofit, Method method) {
return new Builder(retrofit, method).build();
}
static final class Builder {...// Omit some code
Builder(Retrofit retrofit, Method method) {
this.retrofit = retrofit; // Get retrofit objects
this.method = method; // Get the transfer method
this.methodAnnotations = method.getAnnotations();// Get all the annotations above the method
this.parameterTypes = method.getGenericParameterTypes();// Get all parameter types
this.parameterAnnotationsArray = method.getParameterAnnotations();// Get the annotations defined in the parameters
}
RequestFactory build(a) {
for (Annotation annotation : methodAnnotations) {
//1. Parse method annotationsparseMethodAnnotation(annotation); }...// omit attribute judgment
// Get the number of parameter annotations and iterate over them to do some processing
int parameterCount = parameterAnnotationsArray.length;
parameterHandlers = newParameterHandler<? >[parameterCount];for (int p = 0, lastParameter = parameterCount - 1; p < parameterCount; p++) { parameterHandlers[p] = parseParameter(p, parameterTypes[p], parameterAnnotationsArray[p], p == lastParameter); }...// omit attribute judgment
Instantiate a RequestFactory object
return new RequestFactory(this); }}Copy the code
Let’s look at notation first
private void parseMethodAnnotation(Annotation annotation) {
if (annotation instanceof DELETE) {
parseHttpMethodAndPath("DELETE", ((DELETE) annotation).value(), false);
} else if (annotation instanceof GET) {
parseHttpMethodAndPath("GET", ((GET) annotation).value(), false);
} else if (annotation instanceof HEAD) {
parseHttpMethodAndPath("HEAD", ((HEAD) annotation).value(), false);
} else if (annotation instanceof PATCH) {
parseHttpMethodAndPath("PATCH", ((PATCH) annotation).value(), true);
} else if (annotation instanceof POST) {
parseHttpMethodAndPath("POST", ((POST) annotation).value(), true);
} else if (annotation instanceof PUT) {
parseHttpMethodAndPath("PUT", ((PUT) annotation).value(), true);
} else if (annotation instanceof OPTIONS) {
parseHttpMethodAndPath("OPTIONS", ((OPTIONS) annotation).value(), false);
} else if (annotation instanceof HTTP) {
HTTP http = (HTTP) annotation;
parseHttpMethodAndPath(http.method(), http.path(), http.hasBody());
} else if (annotation instanceof retrofit2.http.Headers) {
String[] headersToParse = ((retrofit2.http.Headers) annotation).value();
if (headersToParse.length == 0) {
throw methodError(method, "@Headers annotation is empty.");
}
headers = parseHeaders(headersToParse);
} else if (annotation instanceof Multipart) {
if (isFormEncoded) {
throw methodError(method, "Only one encoding annotation is allowed.");
}
isMultipart = true;
} else if (annotation instanceof FormUrlEncoded) {
if (isMultipart) {
throw methodError(method, "Only one encoding annotation is allowed.");
}
isFormEncoded = true; }}Copy the code
If the current function has a request annotation such as GET,POST,…., etc Let’s look at the internal implementation
private void parseHttpMethodAndPath(String httpMethod, String value, boolean hasBody) {
if (this.httpMethod ! =null) {
throw methodError(method, "Only one HTTP method is allowed. Found: %s and %s.".this.httpMethod, httpMethod);
}
this.httpMethod = httpMethod;
this.hasBody = hasBody;
if (value.isEmpty()) {
return;
}
// Get the relative URL path and existing query string, if present.
int question = value.indexOf('? ');
if(question ! = -1 && question < value.length() - 1) {
// Ensure the query string does not have any named parameters.
String queryParams = value.substring(question + 1);
Matcher queryParamMatcher = PARAM_URL_REGEX.matcher(queryParams);
if (queryParamMatcher.find()) {
throw methodError(method, "URL query string \"%s\" must not have replace block. "
+ "For dynamic query parameters use @Query.", queryParams); }}this.relativeUrl = value;
this.relativeUrlParamNames = parsePathParameters(value);
}
Copy the code
The inside is to do some pre-processing of the request link, and the last line of code is to do some processing on the data in the annotation and return a set.
Now, back to ServiceMethod class HttpServiceMethod parseAnnotations (retrofit, method, requestFactory);
static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations( Retrofit retrofit, Method method, RequestFactory requestFactory) {
boolean isKotlinSuspendFunction = requestFactory.isKotlinSuspendFunction; // Whether Kotlin is supported
boolean continuationWantsResponse = false;
boolean continuationBodyNullable = false;
// Get all the annotations in the current network request function
Annotation[] annotations = method.getAnnotations();
Type adapterType;
if (isKotlinSuspendFunction) { // There is no support for all internal implementations yet.//
} else {
// Returns the return type of the annotation in the interface
adapterType = method.getGenericReturnType();
}
/ / 1. Create createCallAdapter is returned RxJava observed object or is the default DefaultCallAdapterFactory OKHttp object, judging by the comments return values
CallAdapter<ResponseT, ReturnT> callAdapter =
createCallAdapter(retrofit, method, adapterType, annotations);
Type responseType = callAdapter.responseType();
if (responseType == okhttp3.Response.class) {
throw methodError(method, "'"
+ getRawType(responseType).getName()
+ "' is not a valid response body type. Did you mean ResponseBody?"); }...// omit judgment
//2. Create a data response conversion to return GSON
Converter<ResponseBody, ResponseT> responseConverter =
createResponseConverter(retrofit, method, responseType);
//3. Get the OKHttp Call Factory
okhttp3.Call.Factory callFactory = retrofit.callFactory;
if(! isKotlinSuspendFunction) {//4. Create a CallStuck
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>) newSuspendForBody<>(requestFactory, callFactory, responseConverter, (CallAdapter<ResponseT, Call<ResponseT>>) callAdapter, continuationBodyNullable); }}Copy the code
It does some preprocessing of the annotations and returns a ServiceMethod object.
Note 1 from above we can know here according to the analytical returns an annotation RxJava2CallAdapter object or is the Builder of the default DefaultCallAdapterFactory Retrofit
When a ServiceMethod object is returned, an invoke function is continued, and we look directly at the HttpServiceMethod function, since it inherits from ServiceMethod
@Override final @Nullable ReturnT invoke(Object[] args) {
Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
return adapt(call, args);
}
protected abstract @Nullable ReturnT adapt(Call<ResponseT> call, Object[] args);
Copy the code
Take the OKHttpCall above and execute an Adapt abstract function. Since we parse the annotation directly we know that the CallAdapter object is instantiated inside it, so look for its internal ADAPT implementation
static final class CallAdapted<ResponseT.ReturnT> extends HttpServiceMethod<ResponseT.ReturnT> {
private final CallAdapter<ResponseT, ReturnT> callAdapter;
CallAdapted(RequestFactory requestFactory, okhttp3.Call.Factory callFactory,
Converter<ResponseBody, ResponseT> responseConverter,
CallAdapter<ResponseT, ReturnT> callAdapter) {
super(requestFactory, callFactory, responseConverter);
this.callAdapter = callAdapter;
}
@Override protected ReturnT adapt(Call<ResponseT> call, Object[] args) {
// Call the CallAdapter adapt
returncallAdapter.adapt(call); }}Copy the code
Here we know that the CallAdapter.adapt implements two classes, RxJava2CallAdapter and DefaultCallAdapterFactory respectively under the analysis of their implementation adapt function (Call Call)
- RxJava2CallAdapter adapt
final class RxJava2CallAdapter<R> implements CallAdapter<R.Object> {
@Override public Object adapt(Call<R> call) {
. / / the isAsync is configured here RxJava2CallAdapterFactory createAsync inside has a record of a Boolean ()
The CallEnqueueObservable object is returned because it is asynchronous
Observable<Response<R>> responseObservable = isAsync
? new CallEnqueueObservable<>(call)
: newCallExecuteObservable<>(call); Observable<? > observable;//2. As long as the generic parameter in the request interface is not Result, isBody is normally executed to return BodyObservable holding a CallEnqueueObservable object inside
if (isResult) {// If the generic argument is Result
observable = new ResultObservable<>(responseObservable);
} else if (isBody) {
observable = new BodyObservable<>(responseObservable);
} else {
observable = responseObservable;
}
/ / there is no specified so empty, is also specified in RxJava2CallAdapterFactory configuration
if(scheduler ! =null) {
observable = observable.subscribeOn(scheduler);
}
// Define whether Flowable is observed
if (isFlowable) {
return observable.toFlowable(BackpressureStrategy.LATEST);
}
// Is the Single observed defined
if (isSingle) {
return observable.singleOrError();
}
// Define whether Maybe be observed
if (isMaybe) {
return observable.singleElement();
}
// Defines whether the Completable is being observed
if (isCompletable) {
return observable.ignoreElements();
}
// Return BodyObservable if neither of them exists
returnRxJavaPlugins.onAssembly(observable); }}Copy the code
Go here to api.getwxarticle (); If the observed object is defined, it returns the observed object that holds a CallEnqueueObservable inside the BodyObservable.
- DefaultCallAdapterFactory adapt
final class DefaultCallAdapterFactory extends CallAdapter.Factory {
private final @Nullable Executor callbackExecutor;
DefaultCallAdapterFactory(@Nullable Executor callbackExecutor) {
this.callbackExecutor = callbackExecutor;
}
@Override public @NullableCallAdapter<? ,? > get( Type returnType, Annotation[] annotations, Retrofit retrofit) { ...// Omit some code
return newCallAdapter<Object, Call<? > > () {@Override public Type responseType(a) {
return responseType;
}
@Override public Call<Object> adapt(Call<Object> call) {
return executor == null
? call
: newExecutorCallbackCall<>(executor, call); }}; }static final class ExecutorCallbackCall<T> implements Call<T> {
final Executor callbackExecutor;
final Call<T> delegate;
ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) {
this.callbackExecutor = callbackExecutor;
this.delegate = delegate;
}
@Override public void enqueue(final Callback<T> callback) {
checkNotNull(callback, "callback == null");
delegate.enqueue(new Callback<T>() {
@Override public void onResponse(Call<T> call, final Response<T> response) {
callbackExecutor.execute(new Runnable() {
@Override public void run(a) {
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(a) {
callback.onFailure(ExecutorCallbackCall.this, t); }}); }}); }...// Omit some code}}Copy the code
As you can see, if the network request interface function returns a Call, then what is returned is the ExecutorCallbackCall object
The summary of this chapter
As you can see, the CREATE function uses dynamic proxies to call functions in the interface, first parsing the annotation data, then parsing the function return value, and finally creating the corresponding implementation class based on the function return value.
The next section covers the actual invocation
Retrofit + OKHttp
Call Call = api.getwxarticles (); Call is the ExecutorCallbackCall object, so depending on how you call it
Call<JsonObject> call = api.getWXarticles();
call.enqueue(new Callback<JsonObject>() {
@Override
public void onResponse(Call<JsonObject> call, retrofit2.Response<JsonObject> response) {
Log.d(TAG, "Retrofit + OKHttp " + response.body().toString());
}
@Override
public void onFailure(Call<JsonObject> call, Throwable t) {
Log.d(TAG, "Retrofit + OKHttp "+ t.getMessage()); }});Copy the code
We’ll look directly at the Enqueue function of the ExecutorCallbackCall
static final class ExecutorCallbackCall<T> implements Call<T> {
final Executor callbackExecutor;
final Call<T> delegate;
ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) {
this.callbackExecutor = callbackExecutor;
this.delegate = delegate;
}
@Override public void enqueue(final Callback<T> callback) {
//1. Check whether callBack is empty
checkNotNull(callback, "callback == null");
// delegate is a Call from OKHttpClient
delegate.enqueue(new Callback<T>() {
@Override public void onResponse(Call<T> call, final Response<T> response) {
callbackExecutor.execute(new Runnable() {
@Override public void run(a) {
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(a) {
callback.onFailure(ExecutorCallbackCall.this, t); }}); }}); }...// Omit some code
}
Copy the code
As noted in comment 2 above, the delegate is passed in via the upper-layer HttpServiceMethod invoke function
@Override final @Nullable ReturnT invoke(Object[] args) {
Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
return adapt(call, args);
}
Copy the code
Okhttpcall.enqueue (new Callback()))
// Inherits Call from OKHttp
final class OkHttpCall<T> implements Call<T> {
@Override public void enqueue(final Callback<T> callback) {
checkNotNull(callback, "callback == null");
// Get the Call from OKHttp
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 {
1. Create a RealCall object
call = rawCall = createRawCall();
} catch(Throwable t) { throwIfFatal(t); failure = creationFailure = t; }}}if(failure ! =null) {
callback.onFailure(this, failure);
return;
}
if (canceled) {
call.cancel();
}
//2. Execute the enqueue asynchronous network request through the RealCall object
call.enqueue(new okhttp3.Callback() {
@Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
Response<T> response;
try {
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}}}); }}Copy the code
There are two main things going on here
- Create an OKHttp call, also known as a RealCall
- Execute the RealCall enQueue asynchronous function
Let’s look at comment 1 because the OKHttp Request also requires a Request object, which we haven’t seen yet so keep looking
private okhttp3.Call createRawCall(a) throws IOException {
/ / 1.
okhttp3.Call call = callFactory.newCall(requestFactory.create(args));
if (call == null) {
throw new NullPointerException("Call.Factory returned null.");
}
return call;
}
Copy the code
Callfactory.newcall (requestFactory.create(args)) returns an okHttp. call object. But those familiar with OKHTTP know that newCall requires a Request object to be passed in
//OKHttpClient.java
@Override public Call newCall(Request request) {
return RealCall.newRealCall(this, request, false /* for web socket */);
}
Copy the code
So let’s just look at requestFactory.create(args)
1. Return a Request object
okhttp3.Request create(Object[] args) throws IOException {
@SuppressWarnings("unchecked") // It is an error to invoke a method with the wrong arg types.
ParameterHandler<Object>[] handlers = (ParameterHandler<Object>[]) parameterHandlers;
int argumentCount = args.length;
if(argumentCount ! = handlers.length) { ...// omit exception code
}
// Build a Retrofit Request object
RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl,
headers, contentType, hasBody, isFormEncoded, isMultipart);
if (isKotlinSuspendFunction) {
argumentCount--;
}
List<Object> argumentList = new ArrayList<>(argumentCount);
for (int p = 0; p < argumentCount; p++) {
argumentList.add(args[p]);
handlers[p].apply(requestBuilder, args[p]);
}
/ / 2.
return requestBuilder.get()
.tag(Invocation.class, new Invocation(method, argumentList))
.build();
}
Copy the code
From the above code we know that we built a Retrofit Request object internally. Finally, we call the comment 2 code and continue to follow
// requestBuilder.get()
Request.Builder get(a) {
HttpUrl url;
HttpUrl.Builder urlBuilder = this.urlBuilder;
if(urlBuilder ! =null) { // Create a new one if the URL is empty
url = urlBuilder.build();
} else {
/ / here is connect a complete spliced together "https://wanandroid.com/wxarticle/chapters/json"
url = baseUrl.resolve(relativeUrl);
if (url == null) {
throw new IllegalArgumentException(
"Malformed URL. Base: " + baseUrl + ", Relative: "+ relativeUrl); }}// Build the request body
RequestBody body = this.body;
if (body == null) {
// Try to pull from one of the builders.
if(formBuilder ! =null) {
body = formBuilder.build();
} else if(multipartBuilder ! =null) {
body = multipartBuilder.build();
} else if (hasBody) {
// Body is absent, make an empty body.
body = RequestBody.create(null.new byte[0]); }}// Build the request data type
MediaType contentType = this.contentType;
if(contentType ! =null) {
if(body ! =null) {
body = new ContentTypeOverridingRequestBody(body, contentType);
} else {
headersBuilder.add("Content-Type", contentType.toString()); }}// Construct a Request object containing the URL,headers, and body
return requestBuilder
.url(url)
.headers(headersBuilder.build())
.method(method, body);
}
Copy the code
Here the get function is a procedure for Retrofit Request – > OKHttp Request.
Now that the OKHttp Request and RealCall objects are available, the corresponding asynchronous functions can be executed and the data can be called back to the calling layer. If you are not familiar with OKHttp, read the introduction to this article.
RxJava + Retrofit + OKHttp
Observable
Observable = api.getwxarticle (); An Observable is actually a BodyObservable and upstream of it is the CallEnqueueObservable asynchronous observer object, so according to call
Observable<JsonObject> observable = api.getWXarticle();
// An Observable is a BodyObservable that holds an upstream CallEnqueueObservable observable
observable.subscribeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<JsonObject>() {
@Override
public void onSubscribe(Disposable d) {
Log.d(TAG, "RxJava + Retrofit + OKHttp subscription succeeded");
}
@Override
public void onNext(JsonObject s) {
Log.d(TAG, "RxJava + Retrofit + OKHttp " + s.toString());
}
@Override
public void onError(Throwable e) {
Log.d(TAG, "RxJava + Retrofit + OKHttp " + e.getMessage());
}
@Override
public void onComplete(a) {
Log.d(TAG, "RxJava + Retrofit + OKHttp onComplete"); }});Copy the code
Due to return here is BodyObservable observed, then according to the execution process in ObservableSubscribeOn. The s.o subscribeActual nSubscribe (parent); The subscription succeeded in the function. According to RxJava subscription successful execution flow upstream and downstream can know eventually callback to BodyObservable. SubscribeActual function, specific implementation is as follows
@Override protected void subscribeActual(Observer<? super T> observer) {
upstream.subscribe(new BodyObserver<T>(observer));
}
Copy the code
According to the call execution process in THE RxJava source code, upstream is the asynchronous observed object of the upstream CallEnqueueObservable, and the specific implementation of its subscribeActual function is directly looked at
//CallEnqueueObservable.java
@Override protected void subscribeActual(Observer<? super Response<T>> observer) {
/ / 1.
Call<T> call = originalCall.clone();
/ / 2.
CallCallback<T> callback = new CallCallback<>(call, observer);
/ / 3.
observer.onSubscribe(callback);
if(! callback.isDisposed()) {/ / 4.call.enqueue(callback); }}Copy the code
Summarize the meaning of the above notes
- Get the OKhttpCall Call object
- Wrap the call and Observer again
- Invoke the underlying observer object, the BodyObserver
- Finally, the OKHttp asynchronous network request is executed, passing the encapsulated CallCallback to the OKHttpCall object
Let’s look directly at the implementation of the OKHttpCall enQueue function
@Override public void enqueue(final Callback<T> callback) {
checkNotNull(callback, "callback == null"); .// Omit some code
call.enqueue(new okhttp3.Callback() {
@Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
Response<T> response;
try {
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}}}); }Copy the code
Here to see if the request is successful correction directly to OKhttp. The callback. OnResponse and callback to CallCallback onResponse function, the following is the specific implementation
@Override public void onResponse(Call<T> call, Response<T> response) {
if (disposed) return;
try {
observer.onNext(response);
if(! disposed) { terminated =true; observer.onComplete(); }}catch (Throwable t) {
Exceptions.throwIfFatal(t);
if (terminated) {
RxJavaPlugins.onError(t);
} else if(! disposed) {try {
observer.onError(t);
} catch (Throwable inner) {
Exceptions.throwIfFatal(inner);
RxJavaPlugins.onError(newCompositeException(t, inner)); }}}}Copy the code
By the observer. OnNext (response); A layer of transfer to spread a layer of end ObservableSubscribeOn# SubscribeOnObserver. OnNext () function here the entire request is completed.
conclusion
RxJava + Retrofit + OKHttp, RxJava + Retrofit + OKHttp, RxJava + Retrofit + OKHttp, RxJava + Retrofit + OKHttp, RxJava + Retrofit + OKHttp It is recommended that you understand the fundamentals of OKHttp and RxJava before learning Retrofit network requests, otherwise Retrofit will not be able to watch.
reference
Look at Retrofit from a dynamic proxy perspective, that’s the essence of Retrofit!