The introduction
Retrofit is a framework for Android web requests developed by Square. It follows a Restful design style. We look at Invoke and find that it is based on OkHttp and has the following advantages over other web request frameworks:
- Best performance, fastest speed (dynamic proxy advantage)
- Simple and easy to use, code simplification
- Thoroughly decoupled, responsibilities subdivided
- Easy to work with other frameworks (Rxjava)
Of course, there are drawbacks: because of the high encapsulation, the scalability is less than OkHttp, but overall it meets the needs of the project. So today I’m going to take a look at how Retrofit actually works and how does it work
Summary of a.
role
OKHttp is simpler, more convenient, and hides some of OKHttp’s features.
type-safe
Retrofit is based on OKHTTP, so all of Retrofit’s work is around request body and response body. Retrofit provides a variety of converters and you can customize converters to build your request body and serialize the response body to the type you want from the converters. This ensures that both the request body and the response body are secure
Use two.
Add 1.retrofitRely on
implementation 'com. Squareup. Retrofit2: retrofit: 2.5.0'
implementation 'com. Squareup. Retrofit2: converter - gson: 2.5.0'
Copy the code
2. Addnetworkpermissions
<uses-permission android:name="android.permission.INTERNET"/>
Copy the code
3. Create an entity class
Access the github warehouse and obtain the following packets through get request:
class MicroKibacoRepo {
private int id;
private String node_id;
private String name;
private String full_name;
// -- to avoid wasting space, omit useless code --
}
Copy the code
4. Create an interface as a collection of Web Service requests, and use annotations to write request methods that need to be configured
public interface GitHubService {
@GET("users/{user}/repos")
Call<List<MicroKibacoRepo>> listRepos(@Path("user") String user);
}
Copy the code
5. Create an instance of an interface using Retrofit in formal code
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.addConverterFactory(GsonConverterFactory.create())
.build();
GitHubService service = retrofit.create(GitHubService.class);
Copy the code
6. Invoke the corresponding method of the created Service instance to create a corresponding Call object that can be used to initiate network requests
Call<List<MicroKibacoRepo>> repos = service.listRepos("MicroKibaco");
Copy the code
7. Use call.execute () or call.enqueue () to initiate the request
repos.enqueue(new Callback<List<MicroKibacoRepo>>() {
@Override
public void onResponse(Call<List<MicroKibacoRepo>> call, Response<List<MicroKibacoRepo>> response) {}@Override
public void onFailure(Call<List<MicroKibacoRepo>> call, Throwable t) {}});Copy the code
Common apis for Retrofit
methods | role |
---|---|
serviceMethodCache | Custom collection of interface mapping objects |
baseUrl | Request the address |
callFactory | The default is OKHttpCall |
converterFactories | A collection of data parser factories |
callbackExecutor | The callback is executed, and the Android platform defaults to MainThreadExecutor |
Platform | Retrofit is a way to manage multiple platforms, with support for Android and Java8. Through findPlatform access to the corresponding platform, at the same time also initialize the defaultCallAdapterFactory factory |
ServiceMethod | The network Request object of interface mapping converts the annotation of custom interface into this object through dynamic proxy, and generates the Request object required by OkHttp by annotation and parameter. Retrofit’s Create transforms each custom interface into a ServiceMethod object via dynamic proxy interception, which is cached via serviceMethodCache |
ExecutorCallAdapterFactory | The callback is executed, and the Android platform defaults to MainThreadExecutor |
CallAdapter.Factory | Static factory for CallAdapter, containing abstract methods of GET, used to produce CallAdapter objects |
OkHttpCall | The Call implementation of OkHttpCall, createRawCall, gets the real OkHttp3. Call object, which is used to make the actual network request |
Call | Network request interface defined by Retrofit, including execute, enQueue, and other methods |
Converter.Factory | A data parser factory that produces Converter instances |
ExecutorCallAdapterFactory | As the default CallAdapter factory of the Android platform, the GET method implements CallAdapter using an anonymous internal class, returns ExecutorCallbackCall, and implements CallExecutorCallbackCall |
ExecutorCallbackCall | With a static proxy design, the delegate is actually an OkHttpCall, and the callbackExecutor is used to implement the callback in the main thread |
RxJavaCallAdapterFactory | In the Rxjava platform’s CallAdapter factory, the get method returns the RxJavaCallAdapter object |
RxJavaCallAdapter | An Rxjava platform allocator that returns an Observable object |
GsonConverterFactory | Data parsing factory instance, returned to the parser GsonResponseBodyConverter data |
GsonResponseBodyConverter | Gson’s data parser converts json objects returned by the server into the corresponding Java model |
Four. Source code analysis
How to start reading the source code?
Looking at the source code with some questions,Retrofit has a few key processes
- How does Retrofit translate defined interfaces into network requests?
- How is Retrofit’s Converter mechanism implemented?
- How is Retrofit’s CallAdapter mechanism implemented?
1. Find the entrance
Read line by line, is certainly not feasible, too tired, and the brain capacity is not enough to remember, the appropriate way to read the source code is to start with the program entrance. The current function starts reading.
The entry to the current request takes place in Enqueue, so let’s start reading the source code from this Api.
Enqueue, which means to enter a queue, goes in and looks at it
void enqueue(Callback<T> callback);
Copy the code
If a similar abstract method appears; I have to go back because I can’t read anymore
2. Find the interface implementation
So we go up there, and the Api is EnQueue, and we look for the implementation of the interface, and we cry, and the implementation of the interface is GithubService
public interface GitHubService {
@GET("users/{user}/repos")
Call<List<MicroKibacoRepo>> listRepos(@Path("user") String user);
}
Copy the code
Githubservice.java was created by githubservice.java. I’m not going to dig through the entire source code, but I’m going to go through the key parts. There are apis that I need to talk about:
- eagerlyValidateMethods
- newProxyInstance
- Platform
- invoke
- invokeDefaultMethod
public <T> T create(final Class<T> service) {
Utils.validateServiceInterface(service);
if (validateEagerly) {
eagerlyValidateMethods(service);
}
return (T)
// -------- dynamic proxy --------
Proxy.newProxyInstance(
// Provide an instance of a dynamic class
service.getClassLoader(),
// Get an instance of GithubService, get the interface that requires the dynamic proxy class, passed in retrofit.create
newClass<? >[] { service },//
new InvocationHandler() {
JDK 7.0/8.0 or Android/IOS
private final Platform platform = Platform.get();
private final Object[] emptyArgs = new Object[0];
@Override public Object invoke(Object proxy, Method method, @Nullable Object[] args)
throws Throwable {
// If this method is not declared directly, then it is called directly
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
// Since Java 8, interface execution has default methods implemented
if (platform.isDefaultMethod(method)) {
return platform.invokeDefaultMethod(method, service, proxy, args);
}
returnloadServiceMethod(method).invoke(args ! =null? args : emptyArgs); }}); }static <T> void validateServiceInterface(Class<T> service) {
// 1. Verify that GitHubService is an interface
if(! service.isInterface()) {throw new IllegalArgumentException("API declarations must be interfaces.");
}
// 2. Verify that GitHubService inherits other interfaces and accepts only native interfaces
if (service.getInterfaces().length > 0) {
throw new IllegalArgumentException("API interfaces must not extend other interfaces."); }}// 3. When it is initialized, annotations are read during initialization, which is time-consuming. Distributed network requests are loaded
private void eagerlyValidateMethods(Class
service) {
Platform platform = Platform.get();
for (Method method : service.getDeclaredMethods()) {
if(! platform.isDefaultMethod(method)) { loadServiceMethod(method); }}}Copy the code
Java Platform. Java Platform. Java Platform. Java Platform
private static Platform findPlatform(a) {
try {
Class.forName("android.os.Build");
if(Build.VERSION.SDK_INT ! =0) {
/ / the Android platform
return newAndroid(); }}catch (ClassNotFoundException ignored) {
}
try {
Class.forName("java.util.Optional");
/ / Java8 platform
return new Java8();
} catch (ClassNotFoundException ignored) {
}
return new Platform();
}
Copy the code
LoadServiceMethod also has no invoke, so let’s see if we can find invoke using parseAnnotations.
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
Java, but it’s also an abstract method, so let’s go back to parseAnnotations and see if there’s an invoke in there Annotations parseAnnotations are a subclass of ServiceMethod, and HttpServiceMethod must have an invoke implementation
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.");
}
// Use parseAnnotations to find the invoke implementation
return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
}
abstract T invoke(Object[] args);
}
Copy the code
Invoke (CTR + f); invoke (OKHttp);
final class HttpServiceMethod<ResponseT.ReturnT> extends ServiceMethod<ReturnT> {
// ---- to avoid wasting space, omit some useless code ----
@Override ReturnT invoke(Object[] args) {
return callAdapter.adapt(
newOkHttpCall<>(requestFactory, args, callFactory, responseConverter)); }}Copy the code
OkHttpCall implements the Call interface. Let’s focus on the methods:
-
createRawCall
// OKhttp3's Call helps Retrofit implement calls for network requests
private okhttp3.Call createRawCall(a) throws IOException {
okhttp3.Call call = callFactory.newCall(requestFactory.create(args));
if (call == null) {
throw new NullPointerException("Call.Factory returned null.");
}
return call;
}
Copy the code
- call.enqueue
call.enqueue(new okhttp3.Callback() {
@Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
// Response results are given to Retrofit for use
Response<T> response;
try {
response = parseResponse(rawResponse);
} catch (Throwable e) {
throwIfFatal(e);
callFailure(e);
return;
}
try {
callback.onResponse(OkHttpCall.this, response);
} catch(Throwable t) { t.printStackTrace(); }}@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) { t.printStackTrace(); }}}); }Copy the code
What is dynamic proxy?
Dynamic proxy creates an object that passes in an interface and helps you implement each method of each interface, and each method of each interface points to the Invoke method of each method of each interface.
- callAdapter
The constructor in HttpServiceMethod. Java, we find the CallAdapter, and we go to the createCallAdapter and return it
private static <ResponseT, ReturnT> CallAdapter<ResponseT, ReturnT> createCallAdapter( Retrofit retrofit, Method method) {
Type returnType = method.getGenericReturnType();
Annotation[] annotations = method.getAnnotations();
try {
/ / CallAdapter created
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); }}Copy the code
Go to Retrofit.java and check it out:
publicCallAdapter<? ,? > callAdapter(Type returnType, Annotation[] annotations) {return nextCallAdapter(null, returnType, annotations);
}
publicCallAdapter<? ,? > nextCallAdapter(@Nullable CallAdapter.Factory skipPast, Type returnType,
Annotation[] annotations) {
// Get the callAdapter using callAdapterFactories
for (inti = start, count = callAdapterFactories.size(); i < count; i++) { CallAdapter<? ,? > adapter = callAdapterFactories.get(i).get(returnType, annotations,this);
if(adapter ! =null) {
returnadapter; }}// ---- to avoid wasting space, omit some useless code ----
}
Copy the code
The callAdaperFactory is built from Retrofit’s build method.
public Retrofit build(a) {
converterFactories.addAll(this.converterFactories);
converterFactories.addAll(platform.defaultConverterFactories());
return new Retrofit(callFactory, baseUrl, unmodifiableList(converterFactories),
unmodifiableList(callAdapterFactories), callbackExecutor, validateEagerly);
}
Copy the code
We see callAdaperFactory addAll one-time add to come over, so have we see defaultCallAdapterFactories method.
@Override List<? extends CallAdapter.Factory> defaultCallAdapterFactories(
@Nullable Executor callbackExecutor) {
if (callbackExecutor == null) throw new AssertionError();
ExecutorCallAdapterFactory executorFactory = new ExecutorCallAdapterFactory(callbackExecutor);
return Build.VERSION.SDK_INT >= 24
? asList(CompletableFutureCallAdapterFactory.INSTANCE, executorFactory)
: singletonList(executorFactory);
}
Copy the code
Then we could look at the ExecutorCallAdapterFactory
Sharing interview questions
Retrofit request parameter description
Design patterns in Retrofit
1. Builder mode
Both Retrofit object creation and ServiceMethod object creation use Build mode to separate the creation and presentation of complex objects. Callers do not need to know the complex creation process, and use Build related methods to configure the creation of objects.
2. Appearance mode
Retrofit provides uniform scheduling externally, shielding internal implementations, making it easy and convenient to use the network library. Facade pattern: Provides a unified interface to access multiple different interfaces of multiple subsystems. It provides a unified high-level interface for a set of interfaces in a subsystem. Using subsystems is easier to use
3. Dynamic proxy mode
With dynamic proxies, dynamic proxy listening is done when Retrofit’s Create () method is called. InvocationHandler is called back when a specific interface method is executed. The ServiceMethod object is generated by reflecting the annotations and parameters of the method.
4. Static proxy mode
The default Android adapter ExecutorCallbackCall uses the static agent mode. The implementation delegate is OkHttpCall.
5. Factory mode
Both Converter and CallAdapter are created in factory mode.
6. Adapter mode
The CallAdapter ADAPT uses the adapter mode to dynamically expand the return object of an interface, increasing flexibility
The types of CallAdapter
The Converter type
Retrofit supports multiple data parsing methods that require adding dependencies to Gradle.