Retrofit,源碼簡單分析

場景

Retrofit + Okhttp3 是現(xiàn)在開發(fā)標配的網(wǎng)絡(luò)請求框架
Okhttp3 負責底層請求邏輯
Retrofit 負責上層請求外觀,以接口方法定義的形式去直觀表現(xiàn)

分析帶問題

1.如何將接口外觀(包括參數(shù),注解)轉(zhuǎn)換為Ok3的請求
2.如何將Ok3的響應(yīng),解析為接口定義的返回,以及目標業(yè)務(wù)數(shù)據(jù)類型
3.核心類,CallAdapter, Converter的職責和意義

場景設(shè)定

下面,是一個retrofit的請求定義:

@POST("/uoms/servlet/dispatch")
Observable<BaseResponse> dispatch(@Field String workNo);

@POST("/uoms/servlet/dispatchBatch")
Call<BaseResponse> dispatchBatch(@Body ApiRequest request);

可以看到,將繁雜、多變的請求,變換為了注解+方法參數(shù),返回參數(shù)的形式,非常直觀,明了,根據(jù)上面的定義,我們探尋一下調(diào)用過程和設(shè)計邏輯。

解析接口

a) 我們先關(guān)注,最核心的類,所有業(yè)務(wù)的起點 Retrofit:
public final class Retrofit {
  private final Map<Method, ServiceMethod<?, ?>> serviceMethodCache = new ConcurrentHashMap<>();//這是一個方法的完整解析后的對象

  final okhttp3.Call.Factory callFactory;//OkhttpClient的引用
  final HttpUrl baseUrl;//請求的基礎(chǔ)
  final List<Converter.Factory> converterFactories;
  final List<CallAdapter.Factory> callAdapterFactories;
  ...
}

劃重點,這3個類代表了整個解析的核心:
ServiceMethod
單個接口方法的邏輯抽象,為了加快調(diào)用速度,Retrofit對它做了緩存

Converter 和 Converter.Factory
用于數(shù)據(jù)類型解析轉(zhuǎn)換,包括將參數(shù)解析為RequestBody以及將ResponseBody的數(shù)據(jù)解析為我們需要的類型

CallAdapter 和 CallAdapter.Factory
用于生成返回外觀,默認是Retrofit的Call<?>,如果用了RxJava2Adapter庫,返回形式可以變換到RxJava2的Obserable<?>形式

我們每次調(diào)用的時候,都是調(diào)用如下方法:

public <T> T create(final Class<T> service) {
    ...
    return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
        new InvocationHandler() {
          ...
          @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.
           ...
            ServiceMethod<Object, Object> serviceMethod =
                (ServiceMethod<Object, Object>) loadServiceMethod(method);
            OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
            return serviceMethod.adapt(okHttpCall);
          }
        });
  }

代碼邏輯很簡單:
1.Retrofit巧用動態(tài)代理模式,攔截方法調(diào)用,獲取到Method簽名,參數(shù)Argus,然后從Cache里面查找Method代表的 ServiceMethod,如果沒有,通過Method 生成一個ServiceMethod,并加入到Cache,方便下次調(diào)用
2.整個調(diào)用邏輯,都封成了OkHttpCall,OkHttpCall通過ServiceMethod,Argus,完成請求
3.最后調(diào)用serviceMethod.adapt(),返回,這里實際調(diào)用的就是CallAdapter.Factory去轉(zhuǎn)換為我們想要的Call<?>或者Obserable<?>,后面分析

b)ServiceMethod

ServiceMethod是所有調(diào)用的核心,包括怎么解析參數(shù)注解,怎么解析方法注解,包括返回形式是怎么返回,需要重點分析:

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

可以看到ServiceMethod的構(gòu)建很簡單,傳入Method,然后調(diào)用ServiceMethod的Build構(gòu)造函數(shù):

    Builder(Retrofit retrofit, Method method) {
      this.retrofit = retrofit;
      this.method = method;
      this.methodAnnotations = method.getAnnotations();
      this.parameterTypes = method.getGenericParameterTypes();
      this.parameterAnnotationsArray = method.getParameterAnnotations();
    }

在構(gòu)造函數(shù)中,ServiceMethod已經(jīng)拿到了方法的注解詳細信息,方法的參數(shù)注解信息,方法的參數(shù)類型信息,后面都是圍繞這些數(shù)據(jù)展開處理。

然后我們再看ServiceMethod.Bulder.build()方法:

public ServiceMethod build() {
      callAdapter = createCallAdapter();//查找CallAdapter
      responseType = callAdapter.responseType();//獲取返回參數(shù)類型,這里實際是Call<?>或者Observable<?>里面的泛型類型
      ...
      responseConverter = createResponseConverter();// 通過ResponseType去Retrofit反查可以解析當前ResponseType的Converter

      for (Annotation annotation : methodAnnotations) {
        parseMethodAnnotation(annotation);//解析方法注解參數(shù),例如POST,HEAD之類
      }
     ...省略各種請求合法性判定代碼...
      int parameterCount = parameterAnnotationsArray.length;
      parameterHandlers = new ParameterHandler<?>[parameterCount];
      for (int p = 0; p < parameterCount; p++) {
        Type parameterType = parameterTypes[p];
       ...省略參數(shù)類型合法性判定代碼...
        Annotation[] parameterAnnotations = parameterAnnotationsArray[p];
        ...
        //獲取到對應(yīng)參數(shù)類型的解析器,并保存到parameterHandlers數(shù)組里面,稍后解析真實請求的時候用到
        parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);
      }
    ...省略各種請求合法性判定代碼...
      return new ServiceMethod<>(this);
    }

所以我們可以到,上面的解析流程:
解析返回參數(shù),查找到對應(yīng)的CallAdapter,保存 ->
解析方法注解,并保存相關(guān)請求信息 ->
解析方法參數(shù)注解,查找對應(yīng)的ParameterHandler并保存 ->
完成返回 ServiceMethod實例

所以我們可以知道,ServiceMethod對象的構(gòu)建完成,已經(jīng)確定了方法參數(shù)調(diào)用怎么解析,請求返回怎么解析,剩下就是執(zhí)行真實的請求了

c) ServiceMethod.callAdapter的作用和來源

CallAdapter接口源碼如下:

public interface CallAdapter<R, T> {
  //返回實際的返回參數(shù)類型,Call<Repo> 這里的Type就是Repo.class
  Type responseType();

  //適配功能,返回Call<R>結(jié)果的包裝器
  T adapt(Call<R> call);

  //抽象工程定義
  abstract class Factory {
    
    //能否處理,通過這個方法來判定,有效處理會返回CallAdapter實例,否則為NULL
    public abstract @Nullable CallAdapter<?, ?> get(Type returnType, Annotation[] annotations,
        Retrofit retrofit);
    ...
  }
}

CallAdapter,主要2個方法,一個提供返回值的確切類型,一個是將Call<R>轉(zhuǎn)換為其他形式的包裝器,比如返回 Observable<R>
CallAdapter.Factory,抽象工程,get返回Null代表該Factory處理不了該returnType

我們可以看到之前的Retrofit.create():

OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.adapt(okHttpCall);

這實際調(diào)用就是
return serviceMethod.callAdapter.adapt(okHttpCall);
始終處理對象,都是OkHttpCall類型

CallAdapter負責轉(zhuǎn)換返回值形式,基于Call<?>,轉(zhuǎn)換為我們想要的形式,我們看看這個serviceMethod.callAdapter怎么來的

查看ServiceMethod的createCallAdapter():

private CallAdapter<T, R> createCallAdapter() {
      Type returnType = method.getGenericReturnType();//拿到方法的返回類型
      ...
      Annotation[] annotations = method.getAnnotations();
      try {
        return (CallAdapter<T, R>) retrofit.callAdapter(returnType, annotations);
      } catch (RuntimeException e) { 
        ...
      }
    }

我們再看看Retrofit的callAdapter():

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

public CallAdapter<?, ?> nextCallAdapter(@Nullable CallAdapter.Factory skipPast, Type returnType,
      Annotation[] annotations) {
  ...
  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;
    }
  }
  ...注意:如果上面代碼沒有查找到合適的CallAdapter,一定拋出異常...
}

可以知道,Retrofit通過ServiceMethod提供的returnType, annotations,循環(huán)遍歷callAdapterFactories,來獲取可以處理該returnType的CallAdapter實例,在Retrofit.build()時候,默認加入了CallAdapter.Factory的默認實現(xiàn)DefaultCallAdapterFactory :

final class DefaultCallAdapterFactory extends CallAdapter.Factory {
  ...
  @Override
  public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
    ...
    final Type responseType = Utils.getCallResponseType(returnType);
    return new CallAdapter<Object, Call<?>>() {
      @Override public Type responseType() {
        return responseType;
      }
      @Override public Call<Object> adapt(Call<Object> call) {
        return call;
      }
    };
  }
}

所以默認,我們就可以定義返回接口為 Call<?>類型
到此,CallAdapter的作用和來源分析完畢

d) Converter 和 Converter.Factory 作用意義

Converter,源碼定義:

public interface Converter<F, T> {//將 F類型的數(shù)據(jù),轉(zhuǎn)換為 T類型
  T convert(F value) throws IOException;
  ...
}

Converter.Factory源碼定義:

public interface Converter<F, T> {
  ...
  abstract class Factory {
    
    public @Nullable Converter<ResponseBody, ?> responseBodyConverter(Type type,
        Annotation[] annotations, Retrofit retrofit) {
      return null;
    }

    public @Nullable Converter<?, RequestBody> requestBodyConverter(Type type,
        Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
      return null;
    }

    public @Nullable Converter<?, String> stringConverter(Type type, Annotation[] annotations,
        Retrofit retrofit) {
      return null;
    }
    ...
  }
}

與CallAdapter.Factory類似,Converter.Factory可以處理某類型的數(shù)據(jù),則返回有效的Converter實例,否則就返回null
Converter.Factory.requestBodyConverter();負責將對應(yīng)類型參數(shù)數(shù)據(jù),轉(zhuǎn)換為RequestBody,這個方法在拼裝請求的時候,大量用到
Converter.Factory.responseBodyConverter();負責將ResponseBody的數(shù)據(jù),轉(zhuǎn)換為對應(yīng)需要的類型
stringConverter();負責將對應(yīng)類型數(shù)據(jù),轉(zhuǎn)換為String形式

ConverterFactory負責生產(chǎn)3類Converter,一個是將參數(shù)轉(zhuǎn)變?yōu)镽equestBody形式的,請求裝配Converter,一個是將ResponseBody解析為方法定義的返回值(這個是指的Call<T>里面的T類型),一個是將方法參數(shù)形式轉(zhuǎn)換為字符串

e) Converter使用:ParameterHandler

上面介紹了Converter作用,我們先看看最多使用的地方。
ParameterHandler,對參數(shù)注解進行解析,先看定義:

abstract class ParameterHandler<T> {
  abstract void apply(RequestBuilder builder, @Nullable T value) throws IOException;
  ...解析參數(shù),加入進ReuqestBuilder
}

注:RequestBuilder是Retrofit對構(gòu)建Ok3的Request邏輯封裝
這里可以看出,ParameterHandler負責將參數(shù)合理的裝配進入RequestBuilder
它的實現(xiàn)類,我們應(yīng)該很眼熟:

ParameterHandler.Query
ParameterHandler.Header
ParameterHandler.Path
...

這些就是具體這些注解內(nèi)容,將以什么形式去apply到RequestBuilder里面,以
ParameterHandler.Query舉例:

static final class Query<T> extends ParameterHandler<T> {
    private final String name;
    private final Converter<T, String> valueConverter;
    private final boolean encoded;

    Query(String name, Converter<T, String> valueConverter, boolean encoded) {
      this.name = checkNotNull(name, "name == null");
      this.valueConverter = valueConverter;
      this.encoded = encoded;
    }

    @Override void apply(RequestBuilder builder, @Nullable T value) throws IOException {
      if (value == null) return; // Skip null values.

      String queryValue = valueConverter.convert(value);
      if (queryValue == null) return; // Skip converted but null values

      builder.addQueryParam(name, queryValue, encoded);
    }
  }

很簡單,就是用Converter去轉(zhuǎn)換T類型的參數(shù)為字符串,然后拼接進入RequestBuilder,我們再回到ServiceMethod.Build.build()方法,解析方法注解:

int parameterCount = parameterAnnotationsArray.length;
parameterHandlers = new ParameterHandler<?>[parameterCount];
for (int p = 0; p < parameterCount; p++) {
  Type parameterType = parameterTypes[p];
  ...
  Annotation[] parameterAnnotations = parameterAnnotationsArray[p];
  ...
  parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);
}

再看ServiceMethod.parseParameter(),最終調(diào)用的ServiceMethod.parseParameterAnnotation()

private ParameterHandler<?> parseParameterAnnotation(
        int p, Type type, Annotation[] annotations, Annotation annotation) {
  if (annotation instanceof Url) {
  ...
  } else if (annotation instanceof Path) {
  ...
  } else if (annotation instanceof Query) {
        Query query = (Query) annotation;
        String name = query.value();
        boolean encoded = query.encoded();
        Class<?> rawParameterType = Utils.getRawType(type);
        gotQuery = true;
          ParameterizedType parameterizedType = (ParameterizedType) type;
          Type iterableType = Utils.getParameterUpperBound(0, parameterizedType);
          Converter<?, String> converter =
              retrofit.stringConverter(iterableType, annotations);
          return new ParameterHandler.Query<>(name, converter, encoded).iterable();
        }else{
  ...      
  }
  ...
}

流程:
判定注解類型 -> 是Query -> 去Retrofit查找合適的Converter -> 拿到Converter,生成 ParameterHandler.Query實例,保存進入ServiceMethod的數(shù)組里面,等待解析真實請求的時候調(diào)用。

我們再去跟蹤Retrofit.stringConverter()方法:

public <T> Converter<T, String> stringConverter(Type type, Annotation[] annotations) {
  ...
  for (int i = 0, count = converterFactories.size(); i < count; i++) {
    Converter<?, String> converter =  converterFactories.get(i).stringConverter(type, annotations, this);
    if (converter != null) {
      return (Converter<T, String>) converter;
    }
  }
  ...
  return (Converter<T, String>) BuiltInConverters.ToStringConverter.INSTANCE;
}

最終返回的是默認實現(xiàn),ToStringConverter.INSTANCE,它也是簡單調(diào)用toString()方法而已。

至此,方法的參數(shù)解析也完畢了,
我們得到了一個ServiceMethod實例,它包含了:
1.請求地址、類型、頭部信息
2.包含了正確解析參數(shù)的ParameterHandler數(shù)組
3.包含了正確解析響應(yīng)類型的responseConverter
4.包含了方法的返回適配器callAdapter

發(fā)送請求

a)了解Retrofit.Call
Retrofit抽象出了自己的Call邏輯,一個可以操作的請求對象:

//請求過程抽象
public interface Call<T> extends Cloneable {
  
  //返回有效的Response
  Response<T> execute() throws IOException;

  //異步請求
  void enqueue(Callback<T> callback);

  boolean isExecuted();

  //主動取消請求
  void cancel();

  /** True if {@link #cancel()} was called. */
  boolean isCanceled();

  Call<T> clone();

  //轉(zhuǎn)換出原始的Ok3請求
  Request request();
}

//異步回調(diào)
public interface Callback<T> {

  void onResponse(Call<T> call, Response<T> response);

  void onFailure(Call<T> call, Throwable t);
}

//Retrofit返回結(jié)果封裝
public final class Response<T> {
  ...
  private final okhttp3.Response rawResponse;
  private final @Nullable T body;
  private final @Nullable ResponseBody errorBody;
  ...
  /** The raw response from the HTTP client. */
  public okhttp3.Response raw() {
    return rawResponse;
  }

  /** HTTP status code. */
  public int code() {
    return rawResponse.code();
  }

  /** HTTP status message or null if unknown. */
  public String message() {
    return rawResponse.message();
  }

  /** HTTP headers. */
  public Headers headers() {
    return rawResponse.headers();
  }

  /** Returns true if {@link #code()} is in the range [200..300). */
  public boolean isSuccessful() {
    return rawResponse.isSuccessful();
  }

  /** The deserialized response body of a {@linkplain #isSuccessful() successful} response. */
  public @Nullable T body() {
    return body;
  }
  ...
}

Retrofit,封裝了完整的Call, Callback, Response,對底層Ok3做進一步透明化
我們再看Call具體實現(xiàn)OkhttpCall具體執(zhí)行調(diào)用:

@Override 
public Response<T> execute() throws IOException {
    okhttp3.Call call;
    ...
    call = rawCall = createRawCall();
    ...
    return parseResponse(call.execute());
}

@Override 
public void enqueue(final Callback<T> callback) {
    ...
    okhttp3.Call call;
    synchronized (this) {
      ...
      if (call == null && failure == null) {
        try {
            call = rawCall = createRawCall();
        } catch (Throwable t) {
            ...
        }
      }
    }
    ...
    call.enqueue(new okhttp3.Callback() {
        @Override 
        public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
            Response<T> response;
            ...
            response = parseResponse(rawResponse);
            ...
            callback.onResponse(OkHttpCall.this, response);
            ...
        }

        ...
    });
}

 private okhttp3.Call createRawCall() throws IOException {
    okhttp3.Call call = serviceMethod.toCall(args);//這里負責轉(zhuǎn)換
    if (call == null) {
      throw new NullPointerException("Call.Factory returned null.");
    }
    return call;
  }

Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
    ResponseBody rawBody = rawResponse.body();
    ...

    ExceptionCatchingRequestBody catchingBody = new ExceptionCatchingRequestBody(rawBody);
    try {
      T body = serviceMethod.toResponse(catchingBody);//這里負責轉(zhuǎn)換
      return Response.success(body, rawResponse);
    } catch (RuntimeException e) {
      ...
    }
}

這里我只列出了核心代碼,可以看出,最終都是調(diào)用都是ServiceMethod.toCall()方法生成請求,跟蹤toCall()源碼:

okhttp3.Call toCall(@Nullable Object... args) throws IOException {
    RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl, headers,
        contentType, hasBody, isFormEncoded, isMultipart);
    ...
    ParameterHandler<Object>[] handlers = (ParameterHandler<Object>[]) parameterHandlers;
  ...
    for (int p = 0; p < argumentCount; p++) {
      handlers[p].apply(requestBuilder, args[p]);
    }

    return callFactory.newCall(requestBuilder.build());
  }

終于用到我們上面分析保存的信息了,ServiceMethod的相關(guān)屬性,ParameterHandler數(shù)組,生成RequestBuilder,再構(gòu)建出Ok3的Request,生成Ok3的Call。
注: 這里callFactory指的是OkHttpClient

然后收到響應(yīng)的適合,都調(diào)用ServiceMethod.toResponse():

  R toResponse(ResponseBody body) throws IOException {
    return responseConverter.convert(body);
  }

serviceMethod.responseConverter在這里解析響應(yīng)結(jié)果類型

到此,整個請求調(diào)用過程,跟蹤完畢

總結(jié)流程

CallAdapter,轉(zhuǎn)換響應(yīng)形式,默認是Call<T>,有興趣的再去看看RxJava2Adapter下面的幾個類
Converter,轉(zhuǎn)換參數(shù)類型,Converter.Factory轉(zhuǎn)換參數(shù)到RequestBody,ResponseBody到參數(shù)

1.生成ServiceMethod,解析了方法注解,保存了核心的請求屬性,頭部,其次是ParameterHandler數(shù)組用于解析方法參數(shù)值、類型,方法參數(shù)的注解
2.生成OkhttpCall,內(nèi)部調(diào)用ServiceMethod拼裝請求 和 解析響應(yīng)
3.請求的生成靠 ServiceMethod.toRequest() 響應(yīng)解析靠ServiceMethod.toResponse(),實際都是依賴第一步的解析保存的信息

其他

我們可以自己基于現(xiàn)有的RxJava2Adapter, GsonConvertFactory,重寫CallAdapter,Converter,以達到我們的目標,比如:統(tǒng)一處理請求錯誤,統(tǒng)一處理額外業(yè)務(wù)數(shù)據(jù)(resultCode, resultMessage之類的)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 簡介 剛接觸Retrofit的時候,就寫了一篇簡單的使用介紹:Retrofit 2.0基本使用方法,算是對Retr...
    Whyn閱讀 3,111評論 4 24
  • 眾所周知,在現(xiàn)在的Android開發(fā)中,針對與網(wǎng)絡(luò)請求,Retrofit+okHttp的組合絕對是不二之選,而在網(wǎng)...
    jh352160閱讀 382評論 0 5
  • 整體Retrofit內(nèi)容如下: 1、Retrofit解析1之前哨站——理解RESTful2、Retrofit解析2...
    隔壁老李頭閱讀 4,110評論 8 19
  • Retrofit這個開源庫出來也有一定年頭了,記得之前還是在V1.0的版本的時候,之前在三月份也寫過一個Retro...
    lovejjfg閱讀 1,512評論 0 5
  • 西廂小樓沏下一壺茶 一夜聽春雨 溪水庭院窗前流 誰拿一壺酒 說你愿帶我浪跡天涯 花開不敗 胭脂畫 多情的風 帶隨著...
    不俗小七閱讀 472評論 9 31

友情鏈接更多精彩內(nèi)容