preface
The basic okHTTP execution flow was explored in some detail in the previous okHTTP source code parsing (Execution Flow) article. In this article, we will do a source code analysis of Retrofit, another popular network request framework for Android development. We will not cover Retrofit here, but only the implementation process and the knowledge involved.
Basic knowledge preparation
Before exploring Retrofit, there are two things to know: annotations and dynamic proxies. The reason to be prepared for these two points is because they are covered in Retrofit’s source code, so hopefully you can understand them. Since today’s focus is on Retrofit, I’ve prepared a few articles for you on both topics:
Do you really fully understand Java dynamic proxies? Java Dynamic Proxy Design pattern dynamic proxy design pattern
Retrofit for basic use
Retrofit requests for annotations will not be covered in detail here, but there is a very detailed Retrofit 2.0 tutorial for you. If you are not familiar with Retrofit 2.0, please check it out.
Add the dependent
The following dependencies need to be added before use:
implementation 'com. Squareup. Retrofit2: retrofit: 2.6.2'
implementation 'com. Squareup. Retrofit2: converter - gson: 2.6.2'
Copy the code
The latter two dependencies are explained below.
Create the receiving data entity class and HTTP API interface
public class Bean {
private int errorCode;
private String errorMsg;
private List<DataBean> data;
public int getErrorCode(a) {
return errorCode;
}
public void setErrorCode(int errorCode) {
this.errorCode = errorCode;
}
public String getErrorMsg(a) {
return errorMsg;
}
public void setErrorMsg(String errorMsg) {
this.errorMsg = errorMsg;
}
public List<DataBean> getData(a) {
return data;
}
public void setData(List<DataBean> data) {
this.data = data;
}
public static class DataBean {
private int courseId;
private int id;
private String name;
private int order;
private int parentChapterId;
private boolean userControlSetTop;
private int visible;
privateList<? > children;public int getCourseId(a) {
return courseId;
}
public void setCourseId(int courseId) {
this.courseId = courseId;
}
public int getId(a) {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName(a) {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getOrder(a) {
return order;
}
public void setOrder(int order) {
this.order = order;
}
public int getParentChapterId(a) {
return parentChapterId;
}
public void setParentChapterId(int parentChapterId) {
this.parentChapterId = parentChapterId;
}
public boolean isUserControlSetTop(a) {
return userControlSetTop;
}
public void setUserControlSetTop(boolean userControlSetTop) {
this.userControlSetTop = userControlSetTop;
}
public int getVisible(a) {
return visible;
}
public void setVisible(int visible) {
this.visible = visible;
}
publicList<? > getChildren() {return children;
}
public void setChildren(List
children) {
this.children = children;
}
@Override
public String toString(a) {
return "name:"+ name; }}}Copy the code
The toString method is overridden to make it easier to display the data once it is received.
public interface ServiceApi {
@GET("wxarticle/chapters/json")
Call<Bean> getWxArtical(a);
}
Copy the code
The wanAndroid open API of Hongyang is used here to mark thanks.
Create Retrofit and request data
To make this clear, create a Retrofit object in each of the request methods, and just create one in the actual development.
// Synchronize the request
public void execute(a) {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://www.wanandroid.com")
.addConverterFactory(GsonConverterFactory.create())
.build();
ServiceApi serviceApi = retrofit.create(ServiceApi.class);
final Call<Bean> officialAccounts = serviceApi.getOfficialAccounts();
new Thread() {
@Override
public void run(a) {
try {
Response<Bean> response = officialAccounts.clone().execute();
for (int i = 0; i < response.body().getData().size(); i++) {
Log.i("retrofit"."execute: "+ response.body().getData().get(i).toString()); }}catch (IOException e) {
e.printStackTrace();
}
}
}.start();
}
Copy the code
// Asynchronous request
public void enqueue(a) {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://www.wanandroid.com")
.addConverterFactory(GsonConverterFactory.create())
.build();
ServiceApi serviceApi = retrofit.create(ServiceApi.class);
Call<Bean> officialAccounts = serviceApi.getOfficialAccounts();
officialAccounts.clone().enqueue(new Callback<Bean>() {
@Override
public void onResponse(Call<Bean> call, Response<Bean> response) {
for (int i = 0; i < response.body().getData().size(); i++) {
Log.i("retrofit"."enqueue: "+ response.body().getData().get(i).toString()); }}@Override
public void onFailure(Call<Bean> call, Throwable t) {}}); }Copy the code
Take a look at the request result:
The source code parsing
Create a Retrofit object
To create Retrofit, we call the new Retrofit.Builder() method:
public final class Retrofit {
// Network request cache, such as: request method, request header, request body, various adapters, etc
private finalMap<Method, ServiceMethod<? >> serviceMethodCache =new ConcurrentHashMap<>();
final okhttp3.Call.Factory callFactory;
final HttpUrl baseUrl;
final List<Converter.Factory> converterFactories;
final List<CallAdapter.Factory> callAdapterFactories;
final @Nullable Executor callbackExecutor;
final boolean validateEagerly;
Retrofit(okhttp3.Call.Factory callFactory
, HttpUrl baseUrl
,List<Converter.Factory> converterFactories
, List<CallAdapter.Factory> callAdapterFactories
,@Nullable Executor callbackExecutor
, boolean validateEagerly) {
this.callFactory = callFactory;
this.baseUrl = baseUrl;
this.converterFactories = converterFactories;
this.callAdapterFactories = callAdapterFactories;
this.callbackExecutor = callbackExecutor;
this.validateEagerly = validateEagerly; }...public static final class Builder {
// Platform information
private final Platform platform;
//okhttp3 request factory, the default is okhttp
private @Nullable okhttp3.Call.Factory callFactory;
// Base request address
private @Nullable HttpUrl baseUrl;
// Set of converters
private final List<Converter.Factory> converterFactories = new ArrayList<>();
// Request a collection of adapters
private final List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>();
// The thread switch that performs the asynchronous callback
private @Nullable Executor callbackExecutor;
// Whether to parse the method annotation identifier in advance
private boolean validateEagerly;
public Builder(a) {
this(Platform.get());
}
Builder(Platform platform) {
this.platform = platform;
}
Builder(Retrofit retrofit) {
platform = Platform.get();
callFactory = retrofit.callFactory;
baseUrl = retrofit.baseUrl;
// Do not add the default BuiltIntConverters and platform-aware converters added by build().
for (int i = 1,
size = retrofit.converterFactories.size() - platform.defaultConverterFactoriesSize();
i < size; i++) {
converterFactories.add(retrofit.converterFactories.get(i));
}
// Do not add the default, platform-aware call adapters added by build().
for (int i = 0,
size = retrofit.callAdapterFactories.size() - platform.defaultCallAdapterFactoriesSize();
i < size; i++) {
callAdapterFactories.add(retrofit.callAdapterFactories.get(i));
}
callbackExecutor = retrofit.callbackExecutor;
validateEagerly = retrofit.validateEagerly;
}
public Builder baseUrl(String baseUrl) {
// Check whether the baseUrl passed in is null
checkNotNull(baseUrl, "baseUrl == null");
return baseUrl(HttpUrl.get(baseUrl));
}
public Builder baseUrl(HttpUrl baseUrl) {
// Again check if baseUrl is null
checkNotNull(baseUrl, "baseUrl == null");
List<String> pathSegments = baseUrl.pathSegments();
// Determine if baseUrl ends with a '/', if not directly thrown
if (!"".equals(pathSegments.get(pathSegments.size() - 1))) {
throw new IllegalArgumentException("baseUrl must end in /: " + baseUrl);
}
this.baseUrl = baseUrl;
return this;
}
public Builder addConverterFactory(Converter.Factory factory) {
converterFactories.add(checkNotNull(factory, "factory == null"));
return this; }...public Retrofit build(a) {
if (baseUrl == null) {
throw new IllegalStateException("Base URL required.");
}
okhttp3.Call.Factory callFactory = this.callFactory;
if (callFactory == null) {
callFactory = new OkHttpClient();
}
Executor callbackExecutor = this.callbackExecutor;
if (callbackExecutor == null) {
callbackExecutor = platform.defaultCallbackExecutor();
}
List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor));
List<Converter.Factory> converterFactories = new ArrayList<>(
1 + this.converterFactories.size() + platform.defaultConverterFactoriesSize());
converterFactories.add(new BuiltInConverters());
converterFactories.addAll(this.converterFactories);
converterFactories.addAll(platform.defaultConverterFactories());
return newRetrofit(callFactory, baseUrl, unmodifiableList(converterFactories), unmodifiableList(callAdapterFactories), callbackExecutor, validateEagerly); }}}Copy the code
From the above code, we can see that by creating a Retrofit.Builder object, we use the Builder design pattern chain call to assign properties in Retrofit.Builder. When we call the build method, we assign these properties to a Retrofit object. Finally, the Retrofit object is returned. Let’s take a look at what these properties mean.
ServiceMethodCache collection
Inside the Retrofit class, a collection is first created:
private finalMap<Method, ServiceMethod<? >> serviceMethodCache =new ConcurrentHashMap<>();
Copy the code
The Key of this collection is a Mehtod object and the Value is a ServiceMethod object corresponding to the Key. The ServiceMethod object is derived from Method parsing, which contains Method annotations, parameter annotations, and other attributes. As you can see from the name of the collection, this is the collection that caches the Method and its corresponding ServiceMethod, which we encountered when parsing the EventBus source code. The purpose of doing so is to speed up the operation speed, we can also be used for reference and application in the future development. Method and ServiceMethod are covered in the following sections, if you don’t know what they mean.
Platform Platform information
This object is defined in the Retrofit.Builder object.
public static final class Builder {
private finalPlatform platform; . Builder(Retrofit retrofit) { platform = Platform.get(); }... }class Platform {
private static final Platform PLATFORM = findPlatform();
static Platform get(a) {
return PLATFORM;
}
private static Platform findPlatform(a) {
try {
Class.forName("android.os.Build");
// For Android
if(Build.VERSION.SDK_INT ! =0) {
return newAndroid(); }}catch (ClassNotFoundException ignored) {
}
try {
// If the platform is Java
Class.forName("java.util.Optional");
return new Java8();
} catch (ClassNotFoundException ignored) {
}
return newPlatform(); }... }Copy the code
Here we can see that the corresponding Platform object is returned based on the Platform information, respectively Android and Java8, focusing on Android.
static class Android extends Platform {
// check whether the method is the default method. The default method is defined in the Object class
// Return true if so, false otherwise
@IgnoreJRERequirement // Guarded by API check.
@Override boolean isDefaultMethod(Method method) {
if (Build.VERSION.SDK_INT < 24) {
return false;
}
return method.isDefault();
}
@Override public Executor defaultCallbackExecutor(a) {
// get an Executor object representing the callback Executor
return new MainThreadExecutor();
}
// Get the default network adapter set for Android version
@Override List<? extends CallAdapter.Factory> defaultCallAdapterFactories(
@Nullable Executor callbackExecutor) {
if (callbackExecutor == null) throw new AssertionError();
// The default request adapter
DefaultCallAdapterFactory executorFactory = new DefaultCallAdapterFactory(callbackExecutor);
/ / if the Android version is greater than 24, will return a collection of CompletableFutureCallAdapterFactory and DefaultCallAdapterFactory.
/ / otherwise returns only DefaultCallAdapterFactory collections of objects
return Build.VERSION.SDK_INT >= 24
? asList(CompletableFutureCallAdapterFactory.INSTANCE, executorFactory)
: singletonList(executorFactory);
}
@Override int defaultCallAdapterFactoriesSize(a) {
return Build.VERSION.SDK_INT >= 24 ? 2 : 1;
}
// Get the default converter set for Android
@Override List<? extends Converter.Factory> defaultConverterFactories() {
/ / if the Android version is greater than 24, will return only CompletableFutureCallAdapterFactory collections of objects
// Otherwise return a collection of length 0
return Build.VERSION.SDK_INT >= 24
? singletonList(OptionalConverterFactory.INSTANCE)
: Collections.<Converter.Factory>emptyList();
}
@Override int defaultConverterFactoriesSize(a) {
return Build.VERSION.SDK_INT >= 24 ? 1 : 0;
}
// 2.1 returns a callback to the Android main thread
// This means that when a network request completes, it is called back to the Android main thread, which is one of the differences between Retrofit and OkHttp
static class MainThreadExecutor implements Executor {
private final Handler handler = new Handler(Looper.getMainLooper());
@Override public void execute(Runnable r) { handler.post(r); }}}Copy the code
There are four things we’re looking at in Android:
2. Get the execution callback, which by default calls the network request result back to the Main thread of Android. 3. Get the default network adapter set under Android version, which will be returned according to Android version number. 4. Get the default converter set for Android version, which will also be returned based on the Android version number.
It is necessary to give you a preview of the request adapters (CallAdapter.factory) and Converter (Converter.factory) that will be presented in the following article, so let me give you an idea.
CallFactory object
The request factory class is the Call type of the request factory class OKHTTP3, which means Retrofit is encapsulated based on OKHTTP. Define callFactory object we can call the Retrofit. Builder. CallFactory methods for setting, in Retrofit. Builder. The build method of this object will be assigned a Retrofit object.
public final class Retrofit {...public static final class Builder {
private @Nullableokhttp3.Call.Factory callFactory; .public Builder callFactory(okhttp3.Call.Factory factory) {
this.callFactory = checkNotNull(factory, "factory == null");
return this; }...public Retrofit build(a) {... okhttp3.Call.Factory callFactory =this.callFactory;
if (callFactory == null) {
callFactory = newOkHttpClient(); }... }}... }Copy the code
We can see when we didn’t call Retrofit. Builder. CallFactory method, a OkHttpClient object will give us the default Settings.
BaseUrl Base request path
The baseUrl serves as the base path for the request, which makes up the complete request path with the relative path in the method annotations we define in the HTTP API interface. We can set this up by calling retrofit.Builder. baseUrl, which will then be assigned to a Retrofit object in the retrofit.Builder. build method.
public final class Retrofit {...public static final class Builder {
private @NullableHttpUrl baseUrl; ./ / the incoming URL
public Builder baseUrl(URL baseUrl) {
checkNotNull(baseUrl, "baseUrl == null");
return baseUrl(HttpUrl.get(baseUrl.toString()));
}
/ / the incoming String
public Builder baseUrl(String baseUrl) {
checkNotNull(baseUrl, "baseUrl == null");
return baseUrl(HttpUrl.get(baseUrl));
}
public Builder baseUrl(HttpUrl baseUrl) {
/ / found empty
checkNotNull(baseUrl, "baseUrl == null");
// Split the URL
List<String> pathSegments = baseUrl.pathSegments();
// If baseUrl does not end with "/", throw an exception
if (!"".equals(pathSegments.get(pathSegments.size() - 1))) {
throw new IllegalArgumentException("baseUrl must end in /: " + baseUrl);
}
this.baseUrl = baseUrl;
return this; }...public Retrofit build(a) {...// Call null again
if (baseUrl == null) {
throw new IllegalStateException("Base URL required."); }...// Passed into the Retrofit object
return newRetrofit(... , baseUrl, ... . . ,...). ; }}... }Copy the code
Setting process:
1. You can call two methods, passing in a URL and a String. 2. Void baseUrl, then split it, and throw an exception if it ends in a “/”. 3. Null again, and finally assign to Retrofit.
ConverterFactories collection
ConverterFactories set the internal storage is the Converter. The Factory object, we can call the Retrofit. Builder. Add addConverterFactory method.
public final class Retrofit {...public static final class Builder {
private final List<Converter.Factory> converterFactories = newArrayList<>(); .public Builder addConverterFactory(Converter.Factory factory) {
// Check if factory is null and add it to the collection
converterFactories.add(checkNotNull(factory, "factory == null"));
return this; }...public Retrofit build(a) {
List<Converter.Factory> converterFactories = new ArrayList<>(
1 + this.converterFactories.size() + platform.defaultConverterFactoriesSize());
// Add the built-in converter factory first. This prevents overriding its behavior but also
// ensures correct behavior when using converters that consume all types.
// First add BuiltInConverters
converterFactories.add(new BuiltInConverters());
// Then add the collection of ConverterFactory passed in by calling the addConverterFactory method
converterFactories.addAll(this.converterFactories);
// Finally add platform's default Converter.Factory collection
converterFactories.addAll(platform.defaultConverterFactories());
// Create an immutable collection according to converterFactories
return newRetrofit(... . , unmodifiableList(converterFactories), ... . ,...). ; }}... }Copy the code
Converter.Factory Literally, we can guess that it is related to conversion, which will be explored in the following.
CallAdapterFactories Collection of request adapters
CallAdapterFactories deposited in the collection is CallAdapter Factory object, invoke the Retrofit. Builder. AddCallAdapterFactory methods to set.
public final class Retrofit {...public static final class Builder {
private final List<CallAdapter.Factory> callAdapterFactories = newArrayList<>(); .public Builder addCallAdapterFactory(CallAdapter.Factory factory) {
callAdapterFactories.add(checkNotNull(factory, "factory == null"));
return this; }...public Retrofit build(a) {
List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
// Add not only the callAdapterFactory object that was set when the addCallAdapterFactory method was called
// The default callAdapter.factory object in platform was also added
callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor));
// Create an immutable collection according to callAdapterFactories
return newRetrofit(... . . , unmodifiableList(callAdapterFactories), ... ,...). ; }}... }Copy the code
The callAdapter. Factory is a request adapter, which will be explored in the following section.
CallbackExecutor callbackExecutor
We can set the callback actuators through Retrofit. Builder. CallbackExecutor to set, the default will use Platform callback actuator, namely will request the execution result of callback to Android in the main thread.
public final class Retrofit {...public static final class Builder {
private @NullableExecutor callbackExecutor; .public Builder callbackExecutor(Executor executor) {
this.callbackExecutor = checkNotNull(executor, "executor == null");
return this; }...public Retrofit build(a) {
Executor callbackExecutor = this.callbackExecutor;
if (callbackExecutor == null) {
// By default, the platform callback executor will call back the result of the request to the Android main thread
callbackExecutor = platform.defaultCallbackExecutor();
}
return newRetrofit(... . . . , callbackExecutor, ...) ; }}... }Copy the code
Validategbit/s Identifies validation methods in advance
This sign indicates whether need to verify in advance the HTTP methods in the API interface, we through a call to Retrofit. Builder. ValidateEagerly method set, the default is false.
public final class Retrofit {...public static final class Builder {
private booleanvalidateEagerly; .public Builder validateEagerly(boolean validateEagerly) {
this.validateEagerly = validateEagerly;
return this; }...public Retrofit build(a) {...return newRetrofit(... . . . . , validateEagerly); }}... }Copy the code
At this point we have analyzed the properties in Retrofit.buidler, but we are left with two problems: callAdapter.factory and Converter.Factory. What exactly do these two classes do? So let’s see.
CallAdapter.Factory
Now that we know that this class is used for request adaptation, let’s take a look at how it works inside.
// Change the Call response type R to T
public interface CallAdapter<R.T> {
// Returns the value type object used by this adapter to convert the HTTP response body to Java.
For example, the response type for Call
is Repo. This type is used to prepare calls that are passed to adapt.
Type responseType(a);
// This method converts the Call
object into the proxy class T
T adapt(Call<R> call);
/ / CallAdapter plant retrofit the default DefaultCallAdapterFactory the wrong Call for processing, is a direct return the Call.
abstract class Factory {
// Check if returnType is the type we support in this method,
/ / DefaultCallAdapterFactory didn't do, because in the HTTP API interface definition, returnType is Call < Requestbody >
// RxJavaCallAdapterFactory returnType Observable
type
// Return null if not supported
// The return value must be Custom with a generic type (parameter type). Determine the returnType based on the method return value in the HTTP API interface
// For example: CustomCall
getCategories(), then make sure that returnType is CustomCall
public abstract @NullableCallAdapter<? ,? > get(Type returnType, Annotation[] annotations, Retrofit retrofit);// The parameter used to get the generic type is Requestbody in Call
protected static Type getParameterUpperBound(int index, ParameterizedType type) {
return Utils.getParameterUpperBound(index, type);
}
// Get the original type of the generic type
// Call
gets the primitive type Call
protected staticClass<? > getRawType(Type type) {returnUtils.getRawType(type); }}}Copy the code
So with all this code, it’s probably a little bit confusing, so let’s look at two examples.
final class DefaultCallAdapterFactory extends CallAdapter.Factory {
@Override public @NullableCallAdapter<? ,? > get( Type returnType, Annotation[] annotations, Retrofit retrofit) {Return null if the return value is not of Call type
// Call
type, will not be processed
if(getRawType(returnType) ! = Call.class) {return null;
}
return newCallAdapter<Object, Call<? > > () {@Override public Type responseType(a) {
return. ; }@Override public Call<Object> adapt(Call<Object> call) {
return. ; }}; }... }Copy the code
We often see Retrofit + RxJava network encapsulation, and if you want to combine Retrofit and RxJava together you need the RxJavaCallAdapterFactory.
public final class RxJavaCallAdapterFactory extends CallAdapter.Factory {...@Override public @NullableCallAdapter<? ,? > get( Type returnType, Annotation[] annotations, Retrofit retrofit) { Class<? > rawType = getRawType(returnType);boolean isSingle = rawType == Single.class;
boolean isCompletable = rawType == Completable.class;
// Return nothing if the return value is not Observable
Observable<, Observable<, Observable<, Observable<, Observable<, Observable< > type, will not be processed
if(rawType ! = Observable.class && ! isSingle && ! isCompletable) {return null; }... }}Copy the code
The callAdapter.factory example should give you a sense of what it is.
When we define the HTTP API, the return value of the method in Retrofit needs to be determined by the callAdapter.factory added in Retrofit. If we define a type that callAdapter.factory does not support, the request will not be executed.
Converter.Factory
We also know that Converter.Factory is suitable for conversion. Let’s take a look at the source code to see how it is converted.
// Convert F type to T type
public interface Converter<F.T> {
@Nullable T convert(F value) throws IOException;
abstract class Factory {
// Determine if you can convert the API method return type from ResponseBody to type
// If null cannot be returned directly, return the corresponding Converter.Factory object instead
// Type is returned by the responseType() function in the CallAdapter interface.
public @NullableConverter<ResponseBody, ? > responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {return null;
}
// Convert the API method input parameter type from type to requestBody
// Convert the types of the annotated @body, @Part, and @partmap tags
public @NullableConverter<? , RequestBody> requestBodyConverter(Type type, Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {return null;
}
// Convert the API method's input parameter type from type to String
// Convert the types of the annotated @header, @headermap, @path, @query, and @queryMap tags
public @NullableConverter<? , String> stringConverter(Type type, Annotation[] annotations, Retrofit retrofit) {return null;
}
// Extract the upper bound of the generic parameter at index from type
// For example Map
returns Runnable with index 1
,?>
protected static Type getParameterUpperBound(int index, ParameterizedType type) {
return Utils.getParameterUpperBound(index, type);
}
// Extract the original class type from type
// For example: List
returns list.class
protected staticClass<? > getRawType(Type type) {returnUtils.getRawType(type); }}}Copy the code
Are still a little after watching the meng meng da, we are still looking for a example, remember we call Retrofit. Builder. AddConverterFactory incoming GsonConverterFactory, have a look.
// This Converter.Factory class uses Gson to parse Json
public final class GsonConverterFactory extends Converter.Factory {
// Use the default Gson object to create the GsonConverterFactory
// Use Gson to serialize or deserialize an object into a Json string
public static GsonConverterFactory create(a) {
return create(new Gson());
}
public static GsonConverterFactory create(Gson gson) {
// Check whether the gson object passed is empty
// Throw an exception if it is null
if (gson == null) throw new NullPointerException("gson == null");
return new GsonConverterFactory(gson);
}
@Override
publicConverter<ResponseBody, ? > responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) { TypeAdapter<? > adapter = gson.getAdapter(TypeToken.get(type));return new GsonResponseBodyConverter<>(gson, adapter);
}
@Override
publicConverter<? , RequestBody> requestBodyConverter(Type type, Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) { TypeAdapter<? > adapter = gson.getAdapter(TypeToken.get(type));return newGsonRequestBodyConverter<>(gson, adapter); }}// Convert the API method return type from ResponseBody to type
final class GsonResponseBodyConverter<T> implements Converter<ResponseBody.T> {
private final Gson gson;
private final TypeAdapter<T> adapter;
GsonResponseBodyConverter(Gson gson, TypeAdapter<T> adapter) {
this.gson = gson;
this.adapter = adapter;
}
@Override public T convert(ResponseBody value) throws IOException {
JsonReader jsonReader = gson.newJsonReader(value.charStream());
try {
T result = adapter.read(jsonReader);
if(jsonReader.peek() ! = JsonToken.END_DOCUMENT) {throw new JsonIOException("JSON document was not fully consumed.");
}
return result;
} finally{ value.close(); }}}// Convert the API method input parameter type from type to requestBody
final class GsonRequestBodyConverter<T> implements Converter<T.RequestBody> {
private static final MediaType MEDIA_TYPE = MediaType.get("application/json; charset=UTF-8");
private static final Charset UTF_8 = Charset.forName("UTF-8");
private final Gson gson;
private final TypeAdapter<T> adapter;
GsonRequestBodyConverter(Gson gson, TypeAdapter<T> adapter) {
this.gson = gson;
this.adapter = adapter;
}
@Override public RequestBody convert(T value) throws IOException {
Buffer buffer = new Buffer();
Writer writer = new OutputStreamWriter(buffer.outputStream(), UTF_8);
JsonWriter jsonWriter = gson.newJsonWriter(writer);
adapter.write(jsonWriter, value);
jsonWriter.close();
returnRequestBody.create(MEDIA_TYPE, buffer.readByteString()); }}Copy the code
From the GsonRequestBodyConverter example, we can summarize the role of Converter.Factory:
Converter.Factory converts parameters in HTTP API methods between requestBody and responseBody.
This is where we’ll really finish analyzing the properties and classes involved in Retrofit.buidler and take a break for the second half of the analysis.
Call the retrofit.create method
public final class Retrofit {...public <T> T create(final Class<T> service) {
// 1. Verify the HTTP API
Utils.validateServiceInterface(service);
if (validateEagerly) {
// 2. Verify HTTP API methods
eagerlyValidateMethods(service);
}
// 3. Dynamic proxy, one of the cores of Retrofit
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 {
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
if (platform.isDefaultMethod(method)) {
return platform.invokeDefaultMethod(method, service, proxy, args);
}
returnloadServiceMethod(method).invoke(args ! =null? args : emptyArgs); }}); }... }Copy the code
There are three comments in the code above, so let’s take a look at each one
Note 1: Utils. ValidateServiceInterface (Class service) :
We know from comment 1 that this method validates the HTTP API interface we defined. See how it validates.
final class Utils {...static <T> void validateServiceInterface(Class<T> service) {
if(! service.isInterface()) {throw new IllegalArgumentException("API declarations must be interfaces.");
}
if (service.getInterfaces().length > 0) {
throw new IllegalArgumentException("API interfaces must not extend other interfaces."); }}... }Copy the code
Here’s a two-step verification:
1. Verify that the incoming object is an interface, if not a direct exception. 2. If it is an interface, see if the interface is a successor interface, if inherited, will also throw an exception.
Note 2: eagerlyValidateMethods(Class<? > service)
if (validateEagerly) {
eagerlyValidateMethods(service);
}
Copy the code
Here is the code at comment 2. The validategbit/s variable is explained in the previous code comments. It is used to validate methods in advance and is usually false, but we need to take a look at how validation is performed in this method.
private void eagerlyValidateMethods(Class
service) {
Platform platform = Platform.get();
// Get all methods defined in the interface and iterate over them
for (Method method : service.getDeclaredMethods()) {
If the default method is used, Android returns false
// check whether the method is static
if(! platform.isDefaultMethod(method) && ! Modifier.isStatic(method.getModifiers())) {// If the condition is met, load directlyloadServiceMethod(method); }}}Copy the code
Two things are also done in the eagerlyValidateMethods method:
1. Find all the methods in the interface and see if they fit the criteria. 2. If the conditions are met, load the method directly.
public final class Retrofit {
// Cache collection, support multi-threaded access, thread safety
/ / key: Method
//value: Method Specifies the corresponding ServiceMethod
private finalMap<Method, ServiceMethod<? >> serviceMethodCache =newConcurrentHashMap<>(); . ServiceMethod<? > loadServiceMethod(Method method) {// 1ServiceMethod<? > result = serviceMethodCache.get(method);// if yes, return it directly
if(result ! =null) return result;
synchronized (serviceMethodCache) {
// check whether there are methods in the cache again
result = serviceMethodCache.get(method);
if (result == null) {
// 4. Parse method annotations
result = ServiceMethod.parseAnnotations(this, method);
// upload the method to the method cache collectionserviceMethodCache.put(method, result); }}returnresult; }}Copy the code
LoadServiceMethod does three things here:
1. Obtain the corresponding ServiceMethod object from the cache collection according to the Method passed in, if any, return it directly; If not, lock the collection and get it again. This method of caching objects has been seen in the analysis of EventBus source code to improve efficiency. 2. If the ServiceMethod object was not obtained in the previous step, the Method is parsed. 3. Store the Method and its corresponding ServiceMethod object into the cache collection and return the result.
abstract class ServiceMethod<T> {
static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);
Type returnType = method.getGenericReturnType();
if (Utils.hasUnresolvableType(returnType)) {
throw methodError(method,
"Method return type must not include a type variable or wildcard: %s", returnType);
}
if (returnType == void.class) {
throw methodError(method, "Service methods cannot return void.");
}
return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
}
abstract @Nullable T invoke(Object[] args);
}
Copy the code
The method of verifying the HTTP API in comment 2 will stop here. We won’t go into the above code because we’ll see the process in detail in the following analysis shortly, so hopefully you’ll get a sense of this method. Let’s first summarize what comment 2 does:
1. Get all the methods in the HTTP API interface and iterate through and load them. 2. Check whether there is a ServiceMethod object corresponding to the Method in the cache during the loading process. If not, parse the Method, encapsulate the parse result into a ServiceMethod object, store it in the cache collection, and return the ServiceMethod.
Note 3: Dynamic proxy
public final class Retrofit {...public <T> T create(final Class<T> service) {
return (T) Proxy.newProxyInstance(service.getClassLoader(), newClass<? >[] { service },new InvocationHandler() {
// The Android platform is returned
private final Platform platform = Platform.get();
// Create an Object array with length 0
private final Object[] emptyArgs = new Object[0];
@Override public @Nullable Object invoke(Object proxy, Method method, @Nullable Object[] args) throws Throwable {
// if this method is a method from Object, the normal call is followed.
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
On Android, always return false
if (platform.isDefaultMethod(method)) {
return platform.invokeDefaultMethod(method, service, proxy, args);
}
// 5
returnloadServiceMethod(method).invoke(args ! =null? args : emptyArgs); }}); }... }Copy the code
When retrofit.create(Serviceapi.class) is called, a ServiceApi proxy class object is actually generated. As we know from the basics above, when you call a method on a proxy object, you end up calling the invocationHandler. invoke method callback, the third parameter passed in to create the proxy object, where the method is processed. The main flow of the retrofit.create(Serviceapi.class) method is given in the code comments above. The most important step in this method is the final load method, which we will look at.
ServiceMethod<? > loadServiceMethod(Method method) { ServiceMethod<? > result = serviceMethodCache.get(method);if(result ! =null) return result;
synchronized (serviceMethodCache) {
result = serviceMethodCache.get(method);
if (result == null) {
result = ServiceMethod.parseAnnotations(this, method); serviceMethodCache.put(method, result); }}return result;
}
Copy the code
Uh huh? Does this code look familiar? Yes, we have already seen this method in the process of verifying the method in advance. The process of the method will not be described here. Let’s move on.
abstract class ServiceMethod<T> {
static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
// 1. Encapsulate the RequestFactory from the passed Retrofit and Method objects
// This step is mainly for parsing annotations
RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);
// get the returnType of the method
Type returnType = method.getGenericReturnType();
// 3. Validate the method's returnType
if (Utils.hasUnresolvableType(returnType)) {
throw methodError(method,
"Method return type must not include a type variable or wildcard: %s", returnType);
}
if (returnType == void.class) {
throw methodError(method, "Service methods cannot return void.");
}
// 4. Parse the annotations of the method and encapsulate them into a ServiceMethod object
// Return HttpServiceMethod, which is a subclass of ServiceMethod
return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
}
abstract @Nullable T invoke(Object[] args);
}
Copy the code
Alas, we’ve seen it before, but it impressed us, not explored it. There are four steps in this method, and their corresponding actions are given in the comments of the code above. Let’s focus on steps 1 and 4.
RequestFactory.parseAnnotations(retrofit, method)
This method is basically a RequestFactory object wrapped from the passed Retrofit object and Method object.
final class RequestFactory {
static RequestFactory parseAnnotations(Retrofit retrofit, Method method) {
return new Builder(retrofit, method).build();
}
/ / object method
private final Method method;
/ / url
private final HttpUrl baseUrl;
// Request method
final String httpMethod;
// Relative to the URL, which forms the request path with the base URL
private final @Nullable String relativeUrl;
/ / request header
private final @Nullable Headers headers;
// Indicates the media type information in the specific request
private final @Nullable MediaType contentType;
// Whether there is a request body
private final boolean hasBody;
// Whether the Form is submitted
private final boolean isFormEncoded;
// Whether file upload Form is supported
private final boolean isMultipart;
//
private finalParameterHandler<? >[] parameterHandlers;// Is the kotlin suspend function
final boolean isKotlinSuspendFunction;
/** * Inspects the annotations on an interface method to construct a reusable service method. This * requires potentially-expensive reflection so it is best to build each service method only once * and reuse it. Builders cannot be reused. */
// Check comments on interface methods to construct reusable service methods. This requires potentially expensive reflection, so it's best to build each service method once and reuse it.
// The builder cannot be reused.
static final class Builder {
private static final String PARAM = "[a-zA-Z][a-zA-Z0-9_-]*";
private static final Pattern PARAM_URL_REGEX = Pattern.compile("\ \ {(" + PARAM + ") \ \}");
private static final Pattern PARAM_NAME_REGEX = Pattern.compile(PARAM);
// The retrofit object passed in
final Retrofit retrofit;
// The method object passed in
final Method method;
// Annotations in the method object
final Annotation[] methodAnnotations;
// Array of parameter annotations in method
final Annotation[][] parameterAnnotationsArray;
// Array of parameter types in method
final Type[] parameterTypes;
boolean gotField;
boolean gotPart;
boolean gotBody;
boolean gotPath;
boolean gotQuery;
boolean gotQueryName;
boolean gotQueryMap;
boolean gotUrl;
@Nullable String httpMethod;
boolean hasBody;
boolean isFormEncoded;
boolean isMultipart;
@Nullable String relativeUrl;
@Nullable Headers headers;
@Nullable MediaType contentType;
@Nullable Set<String> relativeUrlParamNames;
@NullableParameterHandler<? >[] parameterHandlers;boolean isKotlinSuspendFunction;
Builder(Retrofit retrofit, Method method) {
this.retrofit = retrofit;
this.method = method;
// Get method annotations
this.methodAnnotations = method.getAnnotations();
// Get the method parameter type
this.parameterTypes = method.getGenericParameterTypes();
// Get an array of method annotations
this.parameterAnnotationsArray = method.getParameterAnnotations();
}
RequestFactory build(a) {
// Walk through the annotations to parse them
for (Annotation annotation : methodAnnotations) {
parseMethodAnnotation(annotation);
}
if (httpMethod == null) {
throw methodError(method, "HTTP method annotation is required (e.g., @GET, @POST, etc.).");
}
if(! hasBody) {if (isMultipart) {
throw methodError(method,
"Multipart can only be specified on HTTP methods with request body (e.g., @POST).");
}
if (isFormEncoded) {
throw methodError(method, "FormUrlEncoded can only be specified on HTTP methods with "
+ "request body (e.g., @POST)."); }}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);
}
if (relativeUrl == null && !gotUrl) {
throw methodError(method, "Missing either @%s URL or @Url parameter.", httpMethod);
}
if(! isFormEncoded && ! isMultipart && ! hasBody && gotBody) {throw methodError(method, "Non-body HTTP method cannot contain @Body.");
}
if(isFormEncoded && ! gotField) {throw methodError(method, "Form-encoded method must contain at least one @Field.");
}
if(isMultipart && ! gotPart) {throw methodError(method, "Multipart method must contain at least one @Part.");
}
// Return a RequestFactory object
return new RequestFactory(this);
}
// Parse the annotations
private void parseMethodAnnotation(Annotation annotation) {
// Determine the parsing type and parse accordingly according to the annotation type
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; }}// Parse Http request methods and paths
private void parseHttpMethodAndPath(String httpMethod, String value, boolean hasBody) {... }// Parse the request header
private Headers parseHeaders(String[] headers) {... }... }... }Copy the code
We don’t list all the methods in RequestFactory here, but we can see from the above code and comments what this class does:
Create a RequestFactory.Builder object and pass in a Retrofit object and a Method object. After the requestFactory. Builder object is created, the annotation of the Method object is parsed and the Buidler object is initialized. Finally, create a RequestFactory object and initialize it with the build method.
HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory)
After get RequestFactory object, is called HttpServiceMethod. ParseAnnotations method, will obtain RequestFactory incoming, we have a look at what is done in this operation.
/** Adapts an invocation of an interface method into an HTTP call. */
// Adjust the invocation of interface methods to HTTP requests
abstract class HttpServiceMethod<ResponseT.ReturnT> extends ServiceMethod<ReturnT> {
/** * Inspects the annotations on an interface method to construct a reusable service method that * speaks HTTP. This requires potentially-expensive reflection so it is best to build each service * method only once and reuse it. */
// Check the comments on the interface methods to construct a reusable service method that represents HTTP.
// This requires potentially expensive reflection, so it is best to build each service method once and reuse it.
static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations( Retrofit retrofit, Method method, RequestFactory requestFactory) {
boolean isKotlinSuspendFunction = requestFactory.isKotlinSuspendFunction;
boolean continuationWantsResponse = false;
boolean continuationBodyNullable = false;
// Get all annotations on all methods
Annotation[] annotations = method.getAnnotations();
// Method return value type
Type adapterType;
// Is the kotlin suspend function
if (isKotlinSuspendFunction) {
...
} else {
// Get the method return type
adapterType = method.getGenericReturnType();
}
// get the CallAdapter object
/ / before according to the analysis of the obtained here is DefaultCallAdapterFactory
CallAdapter<ResponseT, ReturnT> callAdapter =
createCallAdapter(retrofit, method, adapterType, annotations);
// Verify the return type is correct
Type responseType = callAdapter.responseType();
// The return type cannot be okhttp3.response
if (responseType == okhttp3.Response.class) {
throw methodError(method, "'"
+ getRawType(responseType).getName()
+ "' is not a valid response body type. Did you mean ResponseBody?");
}
// The return type cannot be Response, it must contain a generic type, similar to Response
if (responseType == Response.class) {
throw methodError(method, "Response must include generic type (e.g., Response<String>)");
}
// TODO support Unit for Kotlin?
if (requestFactory.httpMethod.equals("HEAD") && !Void.class.equals(responseType)) {
throw methodError(method, "HEAD method must use Void as response type.");
}
// Get the Converter object
// Since we set GsonConverterFactory, we get the GsonConverterFactory object here
Converter<ResponseBody, ResponseT> responseConverter =
createResponseConverter(retrofit, method, responseType);
// Get an okHttp3.call. Factory object, which is essentially an OkHttpClient object
okhttp3.Call.Factory callFactory = retrofit.callFactory;
if(! isKotlinSuspendFunction) {return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);
} else if (continuationWantsResponse) {
...
} else{... }}private static <ResponseT, ReturnT> CallAdapter<ResponseT, ReturnT> createCallAdapter( Retrofit retrofit, Method method, Type returnType, Annotation[] annotations) {
try {
// Obtain the CallAdapter from RetroFIT based on returnType and Annotations
return (CallAdapter<ResponseT, ReturnT>) retrofit.callAdapter(returnType, annotations);
} catch (RuntimeException e) { // Wide exception range because factories are user code.
throw methodError(method, e, "Unable to create call adapter for %s", returnType); }}private static <ResponseT> Converter<ResponseBody, ResponseT> createResponseConverter( Retrofit retrofit, Method method, Type responseType) {
Annotation[] annotations = method.getAnnotations();
try {
// Get Converter from retroFIT based on responseType and Annotations
return retrofit.responseBodyConverter(responseType, annotations);
} catch (RuntimeException e) { // Wide exception range because factories are user code.
throw methodError(method, e, "Unable to create converter for %s", responseType); }}}public final class Retrofit {...// Create a CallAdapter object
publicCallAdapter<? ,? > callAdapter(Type returnType, Annotation[] annotations) {// Call nextCallAdapter and pass in skipPast, returnType, annotations
// Note: the skipPast passed in here is null
return nextCallAdapter(null, returnType, annotations);
}
publicCallAdapter<? ,? > nextCallAdapter(@Nullable CallAdapter.Factory skipPast, Type returnType,
Annotation[] annotations) {
// Check whether returnType and Annotations are null
checkNotNull(returnType, "returnType == null");
checkNotNull(annotations, "annotations == null");
// Find the starting location of the callAdapterFactories collection
// Since skipPast is null, the index is -1, and then the index is 1. The starting position is 0
// Start traversing the collection
int start = callAdapterFactories.indexOf(skipPast) + 1;
for (int i = start, count = callAdapterFactories.size(); i < count; i++) {
// When we call callAdapter. Factory, we know that the get method means that,
// Check whether returnType is a type supported by the callAdapter.factory
// Null is returned if not supported
// The corresponding calladapter.factory is returned
/ / because there is no extra setup, so is DefaultCallAdapterFactory return hereCallAdapter<? ,? > adapter = callAdapterFactories.get(i).get(returnType, annotations,this);
// If there is a direct return
if(adapter ! =null) {
returnadapter; }}// There is no corresponding callAdapter.factory
// Concatenate error messages
StringBuilder builder = new StringBuilder("Could not locate call adapter for ")
.append(returnType)
.append(".\n");
if(skipPast ! =null) {
builder.append(" Skipped:");
for (int i = 0; i < start; i++) {
builder.append("\n * ").append(callAdapterFactories.get(i).getClass().getName());
}
builder.append('\n');
}
builder.append(" Tried:");
for (int i = start, count = callAdapterFactories.size(); i < count; i++) {
builder.append("\n * ").append(callAdapterFactories.get(i).getClass().getName());
}
// Throw an exception
throw newIllegalArgumentException(builder.toString()); }...// Create the Converter object
public <T> Converter<ResponseBody, T> responseBodyConverter(Type type, Annotation[] annotations) {
/ / call nextResponseBodyConverter incoming skipPast, returnType and annotations
// Note: the skipPast passed in here is null
return nextResponseBodyConverter(null, type, annotations);
}
public <T> Converter<ResponseBody, T> nextResponseBodyConverter( @Nullable Converter.Factory skipPast, Type type, Annotation[] annotations) {
// Check whether returnType and Annotations are null
checkNotNull(type, "type == null");
checkNotNull(annotations, "annotations == null");
// Find the starting location of the converterFactories collection here
// Since skipPast is null, the index is -1, and then the index is 1. The starting position is 0
// Start traversing the collection
int start = converterFactories.indexOf(skipPast) + 1;
for (int i = start, count = converterFactories.size(); i < count; i++) {
// When we call responseBodyConverter,
// Determine if you can convert the API method return type from ResponseBody to type
// If you cannot return null directly,
// Return the corresponding Converter.Factory object. We set GsonConverterFactory, so we return GsonConverterFactoryConverter<ResponseBody, ? > converter = converterFactories.get(i).responseBodyConverter(type, annotations,this);
if(converter ! =null) {
// If there is a direct return
return(Converter<ResponseBody, T>) converter; }}// There is no corresponding Converter Factory
// Concatenate error messages
StringBuilder builder = new StringBuilder("Could not locate ResponseBody converter for ")
.append(type)
.append(".\n");
if(skipPast ! =null) {
builder.append(" Skipped:");
for (int i = 0; i < start; i++) {
builder.append("\n * ").append(converterFactories.get(i).getClass().getName());
}
builder.append('\n');
}
builder.append(" Tried:");
for (int i = start, count = converterFactories.size(); i < count; i++) {
builder.append("\n * ").append(converterFactories.get(i).getClass().getName());
}
// Throw an exception
throw newIllegalArgumentException(builder.toString()); }... }Copy the code
There are 5 important comments in the above code. First, let’s take a look at comments 1 and 2, which are used to get callAdapter. Factory and Converter.Factory respectively. Comments 4 and 5 are derived from the retrofit object, the operation of the specific need to look at the lower part of the code, eventually obtaining DefaultCallAdapterFactory and GsonConverterFactory, specific process has been given in the comments. Let’s look at the action at comment 3. Here is the code at comment 3
abstract class HttpServiceMethod<ResponseT.ReturnT> extends ServiceMethod<ReturnT> {
static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations( Retrofit retrofit, Method method, RequestFactory requestFactory) {
booleanisKotlinSuspendFunction = requestFactory.isKotlinSuspendFunction; .// Get an okHttp3.call. Factory object, which is essentially an OkHttpClient object
okhttp3.Call.Factory callFactory = retrofit.callFactory;
if(! isKotlinSuspendFunction) {// Create a New CallTested object and pass in the requestFactory, callFactory, responseConverter, and callAdapter
// Finally back out
return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);
} else if (continuationWantsResponse) {
...
} else{... }}... HttpServiceMethod(RequestFactory requestFactory, okhttp3.Call.Factory callFactory, Converter<ResponseBody, ResponseT> responseConverter) {this.requestFactory = requestFactory;
this.callFactory = callFactory;
this.responseConverter = responseConverter;
}
@Override final @Nullable ReturnT invoke(Object[] args) {
Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
// The first thing we look for here is the subclass's adapt method
// Because we are creating a CallFranchise, the adapt release method of callFranchise is called
return adapt(call, args);
}
protected abstract @Nullable ReturnT adapt(Call<ResponseT> call, Object[] args);
// CallPicked inherits from HttpServiceMethod
static final class CallAdapted<ResponseT.ReturnT> extends HttpServiceMethod<ResponseT.ReturnT> {
private final CallAdapter<ResponseT, ReturnT> callAdapter;
// The callBored constructor
CallAdapted(RequestFactory requestFactory, okhttp3.Call.Factory callFactory,
Converter<ResponseBody, ResponseT> responseConverter,
CallAdapter<ResponseT, ReturnT> callAdapter) {
// Call the parent constructor, which is the HttpServiceMethod constructor
super(requestFactory, callFactory, responseConverter);
this.callAdapter = callAdapter;
}
@Override protected ReturnT adapt(Call<ResponseT> call, Object[] args) {
// Returns the result of the calladapter.adapt
returncallAdapter.adapt(call); }}... }Copy the code
Here we need to clarify the process in comment 3, which is somewhat convoluted because it is closely related to the previous analysis:
1, Create a New CallTested object and call the requestFactory, callFactory, responseConverter, and callAdapter. 2. Call the constructor when creating the CallCaller object. Since it inherits from HttpServiceMethod, call the constructor of HttpServiceMethod and pass in the parameters. Call loadServiceMethod(method).invoke(…) Method, the httpServicemethod. adapt method is called again. This method is abstract in HttpServiceMethod, so it ends up calling its subclass, callServicer.adapt. The callAdapter.adapt method is called in the callAdapter.adapt method, which translates the Call
object into the proxy class T. We not set CallAdapte Factory, so use the DefaultCallAdapterFactory, so again call DefaultCallAdapterFactory adapt in the method, and OkHttpCall into.
final class DefaultCallAdapterFactory extends CallAdapter.Factory {
private final @Nullable Executor callbackExecutor;
/ / this constructor in the Android Platform subclass defaultCallAdapterFactories method has been called.
DefaultCallAdapterFactory(@Nullable Executor callbackExecutor) {
//callbackExecutor is MainThreadExecutor, which means the response will be called back to the Android main thread
this.callbackExecutor = callbackExecutor;
}
//
@Override public @NullableCallAdapter<? ,? > get( Type returnType, Annotation[] annotations, Retrofit retrofit) {// If it is not a Call type, it is not processed
if(getRawType(returnType) ! = Call.class) {return null;
}
if(! (returnTypeinstanceof 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);
// Check whether the annotation contains the SkipCallbackExecutor type. We do not use the SkipCallbackExecutor annotation
// Executor is not null
final Executor executor = Utils.isAnnotationPresent(annotations, SkipCallbackExecutor.class)
? null
: callbackExecutor;
return newCallAdapter<Object, Call<? > > () {@Override public Type responseType(a) {
return responseType;
}
@Override public Call<Object> adapt(Call<Object> call) {
// Check whether executor is empty and return a Call that belongs to OkHttpCall
// If it is not empty, return ExecutorCallbackCall
return executor == null
? call
: newExecutorCallbackCall<>(executor, call); }}; }... }Copy the code
static final class ExecutorCallbackCall<T> implements Call<T> {... }Copy the code
Because this is closely related to the previous analysis, it is a little convoluted, so it is still a step by step analysis.
1, in subclasses of the Android Platform, have created the DefaultCallAdapterFactory object, and the incoming MainThreadExecutor, this ensures that the response will be called back to the Android is the main thread. 2, when get CallAdapter type before, already call DefaultCallAdapterFactory. The get method, so the executor object is not null, and returns an anonymous CallAdapter object. 3, we call on DefaultCallAdapterFactory. Adapt method, is called the anonymous objects adapt method, return here is ExecutorCallbackCall object. That is, we use this ExecutorCallbackCall object for network requests.
Perform the requested
As we know from our analysis above, the ExecutorCallbackCall object does network requests, so take a look at its source code.
A synchronous request
The method of synchronous request is also given in the previous example, let’s take a look at the source code
static final class ExecutorCallbackCall<T> implements Call<T> {
final Executor callbackExecutor;
final Call<T> delegate;
// The constructor passes callbackExecutor, Call
ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) {
// callbackExecutor belongs to MainThreadExecutor
this.callbackExecutor = callbackExecutor;
// Delegate is the OkHttpCall object
this.delegate = delegate; }...@Override public Response<T> execute(a) throws IOException {
// Delegate is an OkHttpCall object
returndelegate.execute(); }... }Copy the code
final class OkHttpCall<T> implements Call<T> {
@Override public Response<T> execute(a) throws IOException {
okhttp3.Call call;
synchronized (this) {... call = rawCall;if (call == null) {
try {
// requestFactory creates request objects based on args
// Then create the okHttp3.call object
call = rawCall = createRawCall();
} catch (IOException | RuntimeException | Error e) {
throwIfFatal(e); // Do not assign a fatal error to creationFailure.
creationFailure = e;
throwe; }}}if (canceled) {
call.cancel();
}
// Use the okHttp3. Call object to make the request and parse the response
return parseResponse(call.execute());
}
private okhttp3.Call createRawCall(a) throws IOException {
// requestFactory creates request objects based on args
// Then create an okHttp3. Call object based on the Request object
okhttp3.Call call = callFactory.newCall(requestFactory.create(args));
if (call == null) {
throw new NullPointerException("Call.Factory returned null.");
}
// Return the okHttp3. Call object
return call;
}
// Parse the response
Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
/ / response body
ResponseBody rawBody = rawResponse.body();
// Remove the body's source (the only stateful object) so we can pass the response along.
rawResponse = rawResponse.newBuilder()
.body(new NoContentResponseBody(rawBody.contentType(), rawBody.contentLength()))
.build();
// Get the response code
int code = rawResponse.code();
// Judge by the response code
if (code < 200 || code >= 300) {
try {
// Buffer the entire body to avoid future I/O.
ResponseBody bufferedBody = Utils.buffer(rawBody);
return Response.error(bufferedBody, rawResponse);
} finally{ rawBody.close(); }}if (code == 204 || code == 205) {
rawBody.close();
return Response.success(null, rawResponse);
}
ExceptionCatchingResponseBody catchingBody = new ExceptionCatchingResponseBody(rawBody);
try {
// Pass the response body into the responseConverter object, which is the GsonConverterFactory we set up earlier
// Convert the response body to the corresponding Java object
T body = responseConverter.convert(catchingBody);
return Response.success(body, rawResponse);
} catch (RuntimeException e) {
// If the underlying source threw an exception, propagate that rather than indicating it was
// a runtime exception.
catchingBody.throwIfCaught();
throwe; }}}Copy the code
The process of synchronizing requests:
ExecutorCallbackCall calls the Excute method of the incoming OkhttpCall object by calling the excute method. In the Excute method of OkhttpCall, the requestFactory first creates a Request object from args, and then creates an OkHttp3. Call object. 3.Call the excute method of okHttp3. Call to get the response. 4. After receiving the response, it will judge according to the response code, and then convert the response body into the corresponding Java object through the responseConverter object, and return.
An asynchronous request
The use of asynchronous requests was given in our previous example.
static final class ExecutorCallbackCall<T> implements Call<T> {
// callbackExecutor belongs to MainThreadExecutor
final Executor callbackExecutor;
final Call<T> delegate;
// Asynchronous request
@Override public void enqueue(final Callback<T> callback) {
checkNotNull(callback, "callback == null");
// We know that the delegate is an OkhttpCall object based on the synchronization request
// Call the okHttpCall. exqueue method to execute the asynchronous request
delegate.enqueue(new Callback<T>() {
// Get the callback for the response
@Override public void onResponse(Call<T> call, final Response<T> response) {
// Perform a thread switch
// The callbackExecutor is the MainThreadExecutor, which sends Runnable to the main thread via the main thread Handler
// To switch the thread
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); }}}); }// Failed callback request
@Override public void onFailure(Call<T> call, final Throwable t) {
// Thread switch, same as above
callbackExecutor.execute(new Runnable() {
@Override public void run(a) {
callback.onFailure(ExecutorCallbackCall.this, t); }}); }}); }}Copy the code
final class OkHttpCall<T> implements Call<T> {
// Asynchronous request method
@Override public void enqueue(final Callback<T> callback) {
checkNotNull(callback, "callback == null");
okhttp3.Call call;
Throwable failure;
synchronized (this) {... call = rawCall; failure = creationFailure;if (call == null && failure == null) {
try {
// Create a Call object
call = rawCall = createRawCall();
} catch(Throwable t) { throwIfFatal(t); failure = creationFailure = t; }}}if(failure ! =null) {
callback.onFailure(this, failure);
return;
}
if (canceled) {
call.cancel();
}
// Perform an asynchronous request
call.enqueue(new okhttp3.Callback() {
@Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
Response<T> response;
try {
// The network request is resolved in the same way as the synchronous request
response = parseResponse(rawResponse);
} catch (Throwable e) {
throwIfFatal(e);
callFailure(e);
return;
}
try {
// Request a successful callback
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) {
// The request failed
callFailure(e);
}
private void callFailure(Throwable e) {
try {
// Request failed callback
callback.onFailure(OkHttpCall.this, e);
} catch (Throwable t) {
throwIfFatal(t);
t.printStackTrace(); // TODO this is not great}}}); }private okhttp3.Call createRawCall(a) throws IOException {
// requestFactory creates a request object based on args
// Unpack the Request object into a Call object
okhttp3.Call call = callFactory.newCall(requestFactory.create(args));
if (call == null) {
throw new NullPointerException("Call.Factory returned null.");
}
returncall; }}Copy the code
An important operation in an asynchronous request is to send the result of the request to the main thread through the Handle of the main thread to achieve the effect of thread switching.
Design patterns involved
During source code analysis, we found that Retrofit used a number of Settings patterns, which had the advantage of encapsulating the code and making it easy to call. The design patterns used include: Builder pattern, singleton pattern, factory pattern, broker pattern, facade pattern, decorator pattern, policy pattern, adapter pattern. Since the Builder pattern, singleton pattern, and factory pattern have been covered in previous articles, I won’t go into more detail here, but I will show you where they come from in Retrofit.
1.Builder pattern: The most obvious is the new Retrofit.Builder(). Build () call when the Retrofit object is originally created. 2. Singleton: The singleton pattern is used to obtain the Plathform object from the Platform class. 3.Factory patterns: The most obvious ones are callAdapter. Factory and Converter.Factory, but there are also simple factories, abstract factories and Factory methods that need to be distinguished.
The proxy pattern
When it comes to proxy patterns, there are static and dynamic proxies. In Retrofit we were initially introduced to dynamic proxies.
public final class Retrofit {...public <T> T create(final Class<T> service) { Utils.validateServiceInterface(service); .return (T) Proxy.newProxyInstance(service.getClassLoader(), newClass<? >[] { service },new InvocationHandler() {
...
@Override public @Nullable Object invoke(Object proxy, Method method, @Nullable Object[] args) throws Throwable {... }}); }... }Copy the code
There are only dynamic proxies, where are the static proxies in Retrofit? Before we talk about static proxies, understand what static proxies are: static proxies are proxies that exist before the program runs.
static final class ExecutorCallbackCall<T> implements Call<T> {...// This delegate is a static delegate
final Call<T> 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) {
// Additional operations
if (delegate.isCanceled()) {
...
} else{... }}}); }@Override public void onFailure(Call<T> call, final Throwable t) {... }}); }}Copy the code
Regarding the proxy pattern, I have prepared several articles for you when preparing the basic knowledge. I will not list them here.
The appearance model
public interface ServiceApi {
@GET("wxarticle/chapters/json")
Call<Bean> getWxArtical(a);
}
ServiceApi serviceApi = retrofit.create(ServiceApi.class);
final Call<Bean> officialAccounts = serviceApi.getOfficialAccounts();
Copy the code
We define a lot of methods in the HTTP API. The parameters are defined on the method. It only tells the caller which parameters need to be passed in. Here is a design pattern for home appearance pattern.
Decorative pattern
Allows you to add new functionality to an existing object without changing its structure. It acts as a wrapper around an existing class.
static final class ExecutorCallbackCall<T> implements Call<T> {
final Executor callbackExecutor;
final Call<T> delegate;
ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) {
...
}
@Override public void enqueue(final Callback<T> callback) {
checkNotNull(callback, "callback == null");
delegate.enqueue(newCallback<T>() { ... }); }}Copy the code
We can think of ExecutorCallbackCall as a decoration class, but it’s the OkHttpCall that actually performs the request. The reason we have a decorator class is because we want to do something extra. The operation here is thread conversion, which switches the child thread to the main thread. There are two articles about decorative design patterns: the death of design patterns and decorator patterns (original) and decorator patterns.
The strategy pattern
To put it simply, the strategy pattern is to do something may have multiple schemes at the same time, different times to use different schemes, how to minimize code modification in the process of invocation, mainly using Java polymorphisms.
public final class RxJava2CallAdapterFactory extends CallAdapter.Factory {
@Override public @NullableCallAdapter<? ,? > get( Type returnType, Annotation[] annotations, Retrofit retrofit) { Type observableType = getParameterUpperBound(0, (ParameterizedType) returnType); Class<? > rawObservableType = getRawType(observableType);if (rawObservableType == Response.class) {
...
} else if (rawObservableType == Result.class) {
...
} else{... }}... }Copy the code
In RxJava2CallAdapterFactory the get method to use the strategy pattern, here will be according to the type of rawObservableType, make corresponding operation, if interested friends can look at the inside to the source. Here is an article about strategic patterns: LOL Design Patterns: Strategic Patterns.
Adapter mode
When we look at the Retrofit source code, we can get lost analyzing the CallAdapter. The concept of an adapter is to take something that already exists and turn it into something suitable for use. The most common scenario is when we travel abroad because of the different power interface standards, we need to bring a switch head. Looking back at Retrofit, if we were using it all the time on Android, we would have had to switch threads through a static agent called ExecutorCallbackCall, but then a new technology called Rxjava came along and it worked pretty well, eliminating the need for a Handler to switch threads. This is where a switch is needed to convert OkHttpCall to rXJava (Scheduler). There is also an article about adapter patterns: JAVA and Patterns, Adapter Patterns
conclusion
So far we’ve gone through the source code for Retrofit, and I recommend that you follow the source code yourself, because it’s easy to read on paper. Finally or that sentence, I am an Android primary school, if there is something wrong in the article also hope you don’t hesitate to give advice, I will be very grateful.
The resources
This is a very detailed Retrofit 2.0 tutorial (with examples) Java annotations in plain English Do you really fully understand Java dynamic proxies? Java Dynamic Proxy Design pattern Design pattern death by The claw decorator pattern decorator pattern Java and Pattern adaptor Pattern WanAndroid open API