What is Retrofit?

Introduction to the

Retrofit, which translates as “style renovation” in Chinese, is a RESTful Web request framework based on OKHttp. In layman’s terms, Retrofit is a wrapper around a web request framework. Square is also one of the popular Android web frameworks open source by Square. It is powerful, simple and easy to use, and highly extensible.

Website: Retrofit website

Making address: dead simple

The characteristics of

1. Based on OkHttp and following Restful API design style

2, through the form of annotations, can easily configure network request parameters

3, support synchronous and asynchronous network request

4. Support RxJava

5. Support parsing of multiple data formats (Json, XML, Protobuf, etc.)

How does Retrofit work?

Gradle imports the library

implementation 'com. Squareup. Retrofit2: retrofit: 2.4.0'
implementation 'com. Squareup. Retrofit2: converter - gson: 2.4.0'// Configure using Gson to parse response data with implementation optional'com. Squareup. Retrofit2: adapter - rxjava: 2.4.0'// Configuration support for RxJava optionalCopy the code

2. Initialize Retrofit objects

Retrofit Retrofit = new retrofit.builder ().baseurl (BASE_URL) // Configure baseUrl AddConverterFactory (GsonConverterFactory. The create ()) / / configuration using Gson parsing the response data AddCallAdapterFactory (RxJavaCallAdapterFactory. The create ()) / / support RxJava configuration. The build ();Copy the code

Network Interface Definition

Public interface WeatherService {// Use the GET request to return the original data."weather? location=%E5%98%89%E5%85%B4&output=json&ak=5slgyqGDENN7Sy7pw29IUvrZ") Call<ResponseBody> cityNameQueryWeather(); // Return Json mapping object @get ("{weather}? location=%E5%98%89%E5%85%B4&output=json")
        Call<WeatherResp> cityWeatherPath(@Path("weather") String weather, @Query("ak") String ak); // RxJava will return @formurlencoded () @post ()"{weather}")
        rx.Observable<WeatherResp> cityWeatherPost(@Path("weather") String weather, @Field("ak") String ak, @Field("location") String location, @Field("output") String output);
        
        }
Copy the code

A synchronous request

WeatherService weatherService = retrofit.create(WeatherService.class);
        Call<ResponseBody> responseBodyCall = weatherService.cityNameQueryWeather();
        try {
            Response<ResponseBody> responseBody = responseBodyCall.execute();
            System.out.println("call:" + responseBody.body().string());

        } catch (IOException e) {
            e.printStackTrace();
        }
Copy the code

An asynchronous request

 WeatherService weatherService = retrofit.create(WeatherService.class);
        Call<WeatherResp> responseBodyCall = weatherService.cityWeatherPath("weather"."5slgyqGDENN7Sy7pw29IUvrZ");
        responseBodyCall.enqueue(new Callback<WeatherResp>() {
            @Override
            public void onResponse(Call<WeatherResp> call, Response<WeatherResp> response) {
                System.out.println("call:" + response.toString());
            }

            @Override
            public void onFailure(Call<WeatherResp> call, Throwable t) {

            }
        });
Copy the code

RxJava support

 WeatherService weatherService = retrofit.create(WeatherService.class);
        weatherService.rxCityWeatherPost("weather"."5slgyqGDENN7Sy7pw29IUvrZ"."%E5%98%89%E5%85%B4"."json"). SubscribeOn (Schedulers. IO ()) / / in a new thread processing network request. ObserveOn (AndroidSchedulers. MainThread ()) / / in the main thread to accept the returned data. The subscribe (new  rx.Observer<WeatherResp>() { @Override public voidonCompleted() {

                    }

                    @Override
                    public void onError(Throwable e) {

                    }

                    @Override
                    public void onNext(WeatherResp weatherResp) {
                        System.out.println("call:"+ weatherResp.toString()); }});Copy the code

A detailed Retrofit annotation configuration and use of each annotation is recommended

This is a very detailed Retrofit 2.0 tutorial (with examples)

What is Retrofit’s core execution process?

Description of key class functions

class Functional specifications
Retrofit It contains a lot of objects, ServiceMethodCache (a custom collection of interface mapping objects), baseUrl (request address), callFactory (OKHttpCall by default), converterFactories (a collection of data parser factories), callAdapterFacto Ries (Call adapter factory collection), callbackExecutor (callback execution, default for Android platform MainThreadExecutor) are built using the Builder model
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.
Call Network request interface defined by Retrofit, including execute, enQueue, and other methods
OkHttpCall The Call implementation of Ohttp, through createRawCall, gets the real OKHttp3. Call object, which is used to make the actual network request
CallAdapter.Factory Static factory for CallAdapter, containing abstract methods of GET, used to produce CallAdapter objects
ExecutorCallAdapterFactory As the default CallAdapter factory of the Android platform, the GET method implements CallAdapter using an anonymous internal class, and returns ExecutorCallbackCall, implementing Call
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
Converter.Factory A data parser factory that produces Converter instances
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
Response Retrofit Network requests a Response for a Response

Code execution flow

  1. Build Retrofit objects through Build mode
  2. User-defined interface interface, including interface methods and related annotations
  3. Execute the defined interface methods
  4. Dynamic Proxy interception is performed using proxy. newProxyInstance
  5. The defined annotation method is parsed through reflection to generate a ServiceMethod object (a wrapper object representing a network request) and added to the serviceMethodCache queue to avoid repeated parsing
  6. Create an OkHttpCall object that inherits the Call interface and Bridges over okHttp3.Call rawCall
  7. Get the CallAdapter from the callAdapterFactories collection of Retrofit objects and call the adapt method. If it is the use of the default ExecutorCallAdapterFactory ly returned the Call, if set RxJavaCallAdapterFactory, return to observables.
  8. Get the object transformed by the adapter and make a synchronous or asynchronous request.
  9. Create Okhttp RealCall objects
  10. With the reflected ARG argument, servicemethod.toCall () is called to build the Request object needed to generate Okhttp’s RealCall
  11. The corresponding synchronous or asynchronous request is performed through OkHttp’s RealCall
  12. Request successful, request parameters resolved through parseResponse
  13. Through data converter responseConverter. Convert (body), converts the raw data object
  14. Return the parsed data object Respone(T)
  15. How does Retrofit translate defined interfaces into network requests?

    As we all know, Retrofit describes an HTTP request with a custom interface and associated annotations that are very simple to use and easy to maintain.

    This is one of the essence of Retrofit’s design that dynamic proxy listening happens 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 method. The ServiceMethod encapsulates the parameters required for OKHttp network requests.

    The source code is as follows:

     public <T> T create(final Class<T> service) {
        Utils.validateServiceInterface(service);
        if (validateEagerly) {
          eagerlyValidateMethods(service);
        }
        return(T) Proxy.newProxyInstance(service.getClassLoader(), new Class<? >[] { service }, newInvocationHandler() {
              private final Platform platform = Platform.get();
    
              @Override public Object invoke(Object proxy, Method method, @Nullable Object[] args)
                  throws Throwable {
                // If the method is a method from Object then defer to normal invocation.
                if (method.getDeclaringClass() == Object.class) {
                  return method.invoke(this, args);
                }
                if (platform.isDefaultMethod(method)) {
                  returnplatform.invokeDefaultMethod(method, service, proxy, args); } // Generate ServiceMethod<Object, Object> ServiceMethod =o (ServiceMethod<Object, Object>) loadServiceMethod(method); OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);returnserviceMethod.adapt(okHttpCall); }}); }Copy the code

    The specific conversion method is (ServiceMethod<Object, Object>) loadServiceMethod(method).

    ServiceMethod<? ,? > loadServiceMethod(Method method) { ServiceMethod<? ,? > result = serviceMethodCache.get(method);if(result ! = null)return result;
    
        synchronized (serviceMethodCache) {
          result = serviceMethodCache.get(method);
          if(result == null) { result = new ServiceMethod.Builder<>(this, method).build(); serviceMethodCache.put(method, result); }}return result;
      }
    Copy the code

    Create a ServiceMethod object and add it to the cache. The specific construction is as follows:

     Builder(Retrofit retrofit, Method method) {
          this.retrofit = retrofit;
          this.method = method;
          this.methodAnnotations = method.getAnnotations();
          this.parameterTypes = method.getGenericParameterTypes();
          this.parameterAnnotationsArray = method.getParameterAnnotations();
        }
    
        public ServiceMethod build() {
          callAdapter = createCallAdapter();
          responseType = callAdapter.responseType();
          if (responseType == Response.class || responseType == okhttp3.Response.class) {
            throw methodError("'"
                + Utils.getRawType(responseType).getName()
                + "' is not a valid response body type. Did you mean ResponseBody?");
          }
          responseConverter = createResponseConverter();
    
          for (Annotation annotation : methodAnnotations) {
            parseMethodAnnotation(annotation);
          }
    
          if (httpMethod == null) {
            throw methodError("HTTP method annotation is required (e.g., @GET, @POST, etc.).");
          }
    
          if(! hasBody) {if (isMultipart) {
              throw methodError(
                  "Multipart can only be specified on HTTP methods with request body (e.g., @POST).");
            }
            if (isFormEncoded) {
              throw methodError("FormUrlEncoded can only be specified on HTTP methods with "
                  + "request body (e.g., @POST)."); } } int parameterCount = parameterAnnotationsArray.length; parameterHandlers = new ParameterHandler<? >[parameterCount];for (int p = 0; p < parameterCount; p++) {
            Type parameterType = parameterTypes[p];
            if (Utils.hasUnresolvableType(parameterType)) {
              throw parameterError(p, "Parameter type must not include a type variable or wildcard: %s",
                  parameterType);
            }
    
            Annotation[] parameterAnnotations = parameterAnnotationsArray[p];
            if (parameterAnnotations == null) {
              throw parameterError(p, "No Retrofit annotation found.");
            }
    
            parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);
          }
    
          if(relativeUrl == null && ! gotUrl) { throw methodError("Missing either @%s URL or @Url parameter.", httpMethod);
          }
          if(! isFormEncoded && ! isMultipart && ! hasBody && gotBody) { throw methodError("Non-body HTTP method cannot contain @Body.");
          }
          if(isFormEncoded && ! gotField) { throw methodError("Form-encoded method must contain at least one @Field.");
          }
          if(isMultipart && ! gotPart) { throw methodError("Multipart method must contain at least one @Part.");
          }
    
          return new ServiceMethod<>(this);
        }
    Copy the code

    The build() method resolves method annotations, parameter types, and so on by reflection. See the ParameterHandler implementation in the ParameterHandler source code for detailed analysis.

    To summarize, Retrofit describes an HTTP request through a custom interface and associated annotations, and dynamic proxy listening is performed 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 method. The ServiceMethod encapsulates the parameters required for OKHttp network requests. This is how Retrofit converts a defined interface into a network request object.

    How is Retrofit’s Converter mechanism implemented?

    The Converter type

    Retrofit supports multiple data parsing methods that require adding dependencies to Gradle.

    Data parser Gradle rely on
    Gson Com. Squareup. Retrofit2: converter – gson: 2.4.0
    Jackson Com. Squareup. Retrofit2: converter – Jackson: 2.4.0
    Simple XML Com. Squareup. Retrofit2: converter – simplexml: 2.4.0
    Protobuf Com. Squareup. Retrofit2: converter – protobuf: 2.4.0
    Moshi Com. Squareup. Retrofit2: converter – moshi: 2.4.0
    Wire Com. Squareup. Retrofit2: converter – wire: 2.4.0
    Scalars Com. Squareup. Retrofit2: converter – scalars: 2.4.0

    Converter Implementation Process

    Add Converter Factory

    Add the Converter in the first place in the Retrofit of initialization, addConverterFactory (GsonConverterFactory. The create ()). The concrete implementation is as follows:

      public Builder addConverterFactory(Converter.Factory factory) {
          converterFactories.add(checkNotNull(factory, "factory == null"));
          return this;
        }
    Copy the code

    Add the Converter factory to the converterFactories collection.

    Trigger data conversion

    From the process analysis above, we know that Retrofit executes the parseResponse method in OkHttpCall when a network request is successful.

     Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
        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(); int code = rawResponse.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); } ExceptionCatchingRequestBody catchingBody = new ExceptionCatchingRequestBody(rawBody); try { T body = serviceMethod.toResponse(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(); throw e; }}Copy the code

    T body = serviceMethod. ToResponse (catchingBody)

     /** Builds a method return value from an HTTP response body. */
      R toResponse(ResponseBody body) throws IOException {
        return responseConverter.convert(body);
      }
    Copy the code

    Data converter creation

    This is where Converter does the conversion, so where is responseConverter initialized? In the Build method of ServiceMethod, createResponseConverter() is called to create the data parser:

     private Converter<ResponseBody, T> createResponseConverter() {
          Annotation[] annotations = method.getAnnotations();
          try {
            return retrofit.responseBodyConverter(responseType, annotations);
          } catch (RuntimeException e) { // Wide exception range because factories are user code.
            throw methodError(e, "Unable to create converter for %s", responseType); }}Copy the code

    Call the retrofit responseBodyConverter method

     public <T> Converter<ResponseBody, T> responseBodyConverter(Type type, Annotation[] annotations) {
        return nextResponseBodyConverter(null, type, annotations);
      }
    
    Copy the code

    Call the retrofit nextResponseBodyConverter method

     public <T> Converter<ResponseBody, T> nextResponseBodyConverter(
          @Nullable Converter.Factory skipPast, Type type, Annotation[] annotations) {
        checkNotNull(type."type == null");
        checkNotNull(annotations, "annotations == null");
    
        int start = converterFactories.indexOf(skipPast) + 1;
        for(int i = start, count = converterFactories.size(); i < count; i++) { Converter<ResponseBody, ? > converter = converterFactories.get(i).responseBodyConverter(type, annotations, this);
          if(converter ! = null) { //noinspection uncheckedreturn (Converter<ResponseBody, T>) converter;
          }
        }
    
        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 new IllegalArgumentException(builder.toString());
      }
    Copy the code

    Get the data conversion factory to start adding through Retrofit’s converterFactories collection match

    The default data transformation factory

    The default data converter factory for Retrofit is BuiltInConverters, as shown in the following code found in Retrofit Build (), converterFactories.add(new BuiltInConverters()). The main implementation is as follows:

    @Override public Converter<ResponseBody, ? > responseBodyConverter(Typetype, Annotation[] annotations,
          Retrofit retrofit) {
        if (type == ResponseBody.class) {
          return Utils.isAnnotationPresent(annotations, Streaming.class)
              ? StreamingResponseBodyConverter.INSTANCE
              : BufferingResponseBodyConverter.INSTANCE;
        }
        if (type == Void.class) {
          return VoidResponseBodyConverter.INSTANCE;
        }
        return null;
      }
    
    Copy the code

    The data is converted to a ResponseBody object, so if Retrofit doesn’t use any data parsers by default, the interface method is defined to receive the data object using Call.

    How is Retrofit’s CallAdapter mechanism implemented?

    Retrofit supports a variety of network request adapter approaches: Guava, Java8, and RXJava. Android default ExecutorCallAdapterFactory adapter factory, the final correction by ExecutorCallbackCall switch to the main thread to run.

    CallAdapter species

    Network request adapter Gradle rely on
    guava Com. Squareup. Retrofit2: adapter – guava: 2.4.0
    Java8 Com. Squareup. Retrofit2: adapter – java8:2.4.0
    rxjava Com. Squareup. Retrofit2: adapter – rxjava: 2.4.0

    The CallAdapter implements the process

    Add the CallAdapter factory

    First, adding in the Retrofit of initialization CallAdapter factory addCallAdapterFactory (RxJavaCallAdapterFactory. Create ()). The concrete implementation is as follows:

        public Builder addCallAdapterFactory(CallAdapter.Factory factory) {
          callAdapterFactories.add(checkNotNull(factory, "factory == null"));
          return this;
        }
    Copy the code

    Add the CallAdapter factory to the callAdapterFactories collection.

    Adapt implementation of CallAdapter

    As you can see from Retrofit’s create method above, a return Servicemethod. adapt(okHttpCall) is invoked when a method call to an interface is intercepted. Adapt is implemented as follows:

      T adapt(Call<R> call) {
        return callAdapter.adapt(call);
      }
    Copy the code

    This is where the callAdapter adapt method is triggered. Returns the concrete object of the final adaptation.

    The default CallAdapter factory

    The default Android platform for ExecutorCallAdapterFactory CallAdapter factory. Through Retrofit’s build() method, it can be known that:

     // Make a defensive copy of the adapters and add the default Call adapter.
          List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
          callAdapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));
    Copy the code

    The realization of the platform. DefaultCallAdapterFactory is as follows:

     CallAdapter.Factory defaultCallAdapterFactory(@Nullable Executor callbackExecutor) {
        if(callbackExecutor ! = null) {return new ExecutorCallAdapterFactory(callbackExecutor);
        }
        return DefaultCallAdapterFactory.INSTANCE;
      }
    Copy the code

    ExecutorCallAdapterFactory CallAdapter create is as follows:

    public CallAdapter<? ,? > get(TypereturnType, Annotation[] annotations, Retrofit retrofit) {
            if (getRawType(returnType) ! = Call.class) {return null;
            } else {
                final Type responseType = Utils.getCallResponseType(returnType);
                returnnew CallAdapter<Object, Call<? >>() { public TyperesponseType() {
                        return responseType;
                    }
    
                    public Call<Object> adapt(Call<Object> call) {
                        returnnew ExecutorCallAdapterFactory.ExecutorCallbackCall(ExecutorCallAdapterFactory.this.callbackExecutor, call); }}; }}Copy the code

    The adapt method returns a Call object. The adaptation is implemented by 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;
            }
    
            public void enqueue(final Callback<T> callback) {
                Utils.checkNotNull(callback, "callback == null");
                this.delegate.enqueue(new Callback<T>() {
                    public void onResponse(Call<T> call, final Response<T> response) {
                        ExecutorCallbackCall.this.callbackExecutor.execute(new Runnable() {
                            public void run() {
                                if (ExecutorCallbackCall.this.delegate.isCanceled()) {
                                    callback.onFailure(ExecutorCallbackCall.this, new IOException("Canceled"));
                                } else{ callback.onResponse(ExecutorCallbackCall.this, response); }}}); } public void onFailure(Call<T> call, final Throwable t) { ExecutorCallbackCall.this.callbackExecutor.execute(newRunnable() {
                            public void run() { callback.onFailure(ExecutorCallbackCall.this, t); }}); }}); } public Response<T> execute() throws IOException {returnthis.delegate.execute(); }}Copy the code

    Specific execution or through the delegate (OkHttpCall) to perform, but used in callback ExecutorCallbackCall. Enclosing callbackExecutor. The execute () method, The callbackExecutor for Android is implemented as MainThreadExecutor. The specific source code is as follows:

    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

    So ExecutorCallAdapterFactory adaptation in the final set with objects or Call, implementation or OkHttpCall, through MainThreadExecutor, switch to callback to run in the UI thread

    The creation of CallAdapter

    In the Build method of ServiceMethod, createCallAdapter() creates the adapter as follows:

      private CallAdapter<T, R> createCallAdapter() {
          Type returnType = method.getGenericReturnType();
          if (Utils.hasUnresolvableType(returnType)) {
            throw methodError(
                "Method return type must not include a type variable or wildcard: %s".returnType);
          }
          if (returnType == void.class) {
            throw methodError("Service methods cannot return void.");
          }
          Annotation[] annotations = method.getAnnotations();
          try {
            //noinspection unchecked
            return (CallAdapter<T, R>) retrofit.callAdapter(returnType, annotations);
          } catch (RuntimeException e) { // Wide exception range because factories are user code.
            throw methodError(e, "Unable to create call adapter for %s".returnType); }}Copy the code

    Call the retrofit callAdapter

    public CallAdapter<? ,? > callAdapter(TypereturnType, Annotation[] annotations) {
       return nextCallAdapter(null, returnType, annotations);
     }
    Copy the code

    Call the retrofit nextCallAdapter

    public CallAdapter<? ,? > nextCallAdapter(@Nullable CallAdapter.Factory skipPast, TypereturnType,
         Annotation[] annotations) {
       checkNotNull(returnType, "returnType == null");
       checkNotNull(annotations, "annotations == null");
    
       int start = callAdapterFactories.indexOf(skipPast) + 1;
       for(int i = start, count = callAdapterFactories.size(); i < count; i++) { CallAdapter<? ,? > adapter = callAdapterFactories.get(i).get(returnType, annotations, this);
         if(adapter ! = null) {return adapter;
         }
       }
    
       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 new IllegalArgumentException(builder.toString());
     }
    Copy the code

    Get the adapter factory to start adding by matching Retrofit’s Collection of callAdapterFactories

    How to customize a Converter and CallAdapter?

    The custom Converter

    1. You need to define a Factory class that inherits Converter

    ResponseBodyConverter and requestBodyConverter method

    3. Add custom Converter, addConverterFactory(new CustomConverterFactory()) to Retrofit definition

    /** * @author Allen * @date 2018/8/7 * Returns a string result */ public class CustomConverterFactory extends Converter  { @Nullable @Override public Converter<ResponseBody, ? > responseBodyConverter(Typetype, Annotation[] annotations, Retrofit retrofit) {
            returnStringResponseBodyConverter.INSTANCE; } @Nullable @Override public Converter<? , RequestBody> requestBodyConverter(Typetype, Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
            return StringRequestBodyConverter.INSTANCE;
        }
    
        static final class StringResponseBodyConverter implements Converter<ResponseBody, String> {
            static final CustomConverterFactory.StringResponseBodyConverter INSTANCE = new CustomConverterFactory.StringResponseBodyConverter();
    
            @Override
            public String convert(ResponseBody value) {
    
                try {
                    System.out.println("CustomConverterFactory");
                    return value.string();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                return "";
            }
        }
        static final class StringRequestBodyConverter implements Converter<RequestBody, RequestBody> {
            static final CustomConverterFactory.StringRequestBodyConverter INSTANCE = new CustomConverterFactory.StringRequestBodyConverter();
    
            @Override
            public RequestBody convert(@NonNull RequestBody value) {
                returnvalue; }}}Copy the code

    Custom CallAdapter

    1. You need to define a Factory class that inherits callAdapter.factory

    2. Duplicate the CallAdapter get method

    AddCallAdapterFactory (new CustomCallAdapterFactory())

    /** * @author allen * @date 2018/8/7 */ public class CustomCallAdapterFactory extends CallAdapter.Factory { @Nullable @Override public CallAdapter<? ,? > get(TypereturnType, Annotation[] annotations, Retrofit retrofit) {
    
            return new CallAdapter<Object, Object>() {
                @Override
                public Type responseType() {
                    return String.class;
                }
    
                @Override
                public Object adapt(Call<Object> call) {
                    System.out.println("CustomCallAdapterFactory adapt(");
                    returncall; }}; }}Copy the code

    What design patterns are used 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.

    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.

    The default Android adapter ExecutorCallbackCall works in the static agent mode. The implementation delegate is OkHttpCall.

    5. Factory Mode Converter and CallAdapter are created using factory mode.

    The CallAdapter adapt uses the adaptor mode to dynamically expand the return object of an interface, increasing flexibility

    conclusion

    thinking

    Most of the popular open source frameworks are designed to be top-level coder and contain a lot of clever design. You can learn a lot about analysis.

    The resources

    This is a very detailed Retrofit 2.0 tutorial (with examples)

    Android: Take you hand by hand through Retrofit 2.0 source code

    recommended

    Android source code series – decrypt OkHttp

    Android source code series – Decrypt Retrofit

    Android source code series – Decrypt Glide

    Android source code series – Decrypt EventBus

    Android source code series – decrypt RxJava

    Android source code series – Decrypt LeakCanary

    Android source code series – decrypt BlockCanary

    about

    Welcome to pay attention to my personal public number

    Wechat search: Yizhaofusheng, or search the official ID: Life2Code

  • Author: Huang Junbin
  • Blog: junbin. Tech
  • GitHub: junbin1011
  • Zhihu: @ JunBin