Retrofit源碼分析

1.開發(fā)中一個常見網(wǎng)絡(luò)請求過程


url,參數(shù)---> request ---> 轉(zhuǎn)化成Http協(xié)議 -->請求執(zhí)行 ---> 返回結(jié)果--> 轉(zhuǎn)化成response ---> response轉(zhuǎn)化成我們的對象

只有頭和尾是我們?nèi)粘i_發(fā)進行自定義的,中間的其他過程都是網(wǎng)絡(luò)框架進行的。

2.Retrofit做的事情


  • 1.url,參數(shù)Retrofit主要采用接口+注解方式

  • 2.中間網(wǎng)絡(luò)請求框架采用動態(tài)代理去使用某個框架

  • 3.response轉(zhuǎn)化成我們的對象采用adapter模式進行適配

3.Retrofit的實現(xiàn)過程


1)toRequest,url,參數(shù)Retrofit主要采用接口+注解方式

我們以一個簡單的POST請求為例


@POST("ssoService/v1/logoutByCTGT")

Call<BaseResponseBean> logout(@Body LogoutRequest logoutRequest);


@Documented

@Target(METHOD)

@Retention(RUNTIME)

public @interface POST {

  String value() default "";

}

@Documented

@Target(PARAMETER)

@Retention(RUNTIME)

public @interface Body {

}

<u>注解知識點解析</u>

  • 注解的定義
  • @Documented

Documented注解表明這個注釋是由 javadoc記錄的,在默認(rèn)情況下也有類似的記錄工具。 如果一個類型聲明被注釋了文檔化,它的注釋成為公共API的一部分。

  • @Target(METHOD)

    注釋可能出現(xiàn)在Java程序中的語法位置,TYPE,F(xiàn)IELD,METHOD,PARAMETER等

  • @Retention(RUNTIME)

描述保留注釋的各種策略,SOURCE(源碼期,編譯的時候會被忽略),CLASS(會被編譯到類文件中,但是JVM中沒有),RUNTIME(各個階段都存在)

  • 注解的獲取(ServiceMethod.Builder類)

Builder(Retrofit retrofit, Method method) {

      this.retrofit = retrofit;

      this.method = method;

      this.methodAnnotations = method.getAnnotations();

      this.parameterTypes = method.getGenericParameterTypes();

      this.parameterAnnotationsArray = method.getParameterAnnotations();

    }

獲取注解的方法


this.methodAnnotations = method.getAnnotations();

this.parameterAnnotationsArray = method.getParameterAnnotations();

注解獲取相對還是比較簡單的,Method方法可以直接獲取各種注解,一個是方法上面的注解,一個是參數(shù)上的注解

  • Retrofit對于獲取到的注解的處理(ServiceMethod.Builder.build())

for (Annotation annotation : methodAnnotations) {

        parseMethodAnnotation(annotation);

      }



......

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);

      }

注解處理后轉(zhuǎn)化成相關(guān)屬性


  private final String httpMethod;

  private final String relativeUrl;

  private final Headers headers;

  private final MediaType contentType;

  private final boolean hasBody;

  private final boolean isFormEncoded;

  private final boolean isMultipart;

  private final ParameterHandler<?>[] parameterHandlers;

然后在ServiceMethod.toRequest方法里轉(zhuǎn)換成 Request


  Request toRequest(@Nullable Object... args) throws IOException {

    RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl, headers,

        contentType, hasBody, isFormEncoded, isMultipart);

    @SuppressWarnings("unchecked") // It is an error to invoke a method with the wrong arg types.

    ParameterHandler<Object>[] handlers = (ParameterHandler<Object>[]) parameterHandlers;

    int argumentCount = args != null ? args.length : 0;

    if (argumentCount != handlers.length) {

      throw new IllegalArgumentException("Argument count (" + argumentCount

          + ") doesn't match expected count (" + handlers.length + ")");

    }

    for (int p = 0; p < argumentCount; p++) {

      handlers[p].apply(requestBuilder, args[p]);

    }

    return requestBuilder.build();

  }

以上分析了注解到toRequest,接下去我們分析下代碼的調(diào)用過程

同步請求過程

OkHttpCall.execute()->OkHttpCall.createRawCall()->serviceMethod.toRequest()

異步過程

OkHttpCall.enqueue()->OkHttpCall.createRawCall()->serviceMethod.toRequest()

2)toResponse

優(yōu)先解析toResponse的使用,可以方便我們反推Retrofit如何使用動態(tài)代理進行網(wǎng)絡(luò)框架的調(diào)用

相比toRequest,toResponse則簡單很多,日常工作當(dāng)中我們也是經(jīng)常使用Gson進行結(jié)果的轉(zhuǎn)化,我們通過轉(zhuǎn)化器的添加,以及轉(zhuǎn)化的調(diào)用流程對toResponse進行一次分析

  • 我們先來下轉(zhuǎn)化成我們自己的對象的方法是什么時候添加的

public static <T> T createJsonApi(@Url String baseUrl, Class<T> service) {

        if (!checkUrl(retrofitRxJson, baseUrl)) {

            retrofitRxJson = new Retrofit.Builder()

                    .baseUrl(baseUrl)

                    .addConverterFactory(GsonConverterFactory.create())

                    .addCallAdapterFactory(RxJava2CallAdapterFactory.create())

                    .client(okHttpClient)

                    .build();

        }

        return retrofitRxJson.create(service);

    }


                    .addConverterFactory(GsonConverterFactory.create())

采用Gson把平臺返回的內(nèi)容轉(zhuǎn)化成我們的對象


public class BaseResponseBean<T> {

    public Integer type;

    public String code;

    public String msg;

    public T data;

}

  • toResponse整個調(diào)用過程
  • 同步請求

OkHttpCall.excute()-> OkHttpCall.parseResponse()->serviceMethod.toResponse()->serviceMethod.responseConverter.convert()

  • 異步請求

OkHttpCall.enqueue()-> hik.lib.okhttp3.Callback.onResponse()->OkHttpCall.parseResponse()->serviceMethod.toResponse()->serviceMethod.responseConverter.convert()

  • responseConverter的初始化

ServiceMehtond.Builder.build()->ServiceMehtond.Builder.createResponseConverter()->retrofit.responseBodyConverter()-> retrofit.nextResponseBodyConverter()

<u>這里有個比較好的設(shè)計,看代碼</u>


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 unchecked

        return (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());

  }

<font color="Hotpink"><u>如果返回轉(zhuǎn)換的類型有多個,會根據(jù)接口的返回類型進行匹配</u></font>

3)動態(tài)代理進行網(wǎng)絡(luò)框架的使用

我們分析了toRequest()與toResponse(),以及從OkHttpCall網(wǎng)絡(luò)請求調(diào)用兩個方法的過程,接下去我們分析動態(tài)代理如何調(diào)用OkHttpCall里的execute()與enqueue()

我們在使用的時候都是定義接口,但是并沒有定義實現(xiàn)類,這里就是使用動態(tài)代理方式實現(xiàn)了接口的實現(xiàn)類

這里有個要注意的地方*******************

<u>接口返回的是一個對網(wǎng)絡(luò)請求進行了封裝的對象,并沒有進行網(wǎng)絡(luò)請求</u>

  • 動態(tài)代理的創(chuàng)建

public <T> T create(final Class<T> service) {

    Utils.validateServiceInterface(service);

    if (validateEagerly) {

      eagerlyValidateMethods(service);

    }

    return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },

        new InvocationHandler() {

          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)) {

              return platform.invokeDefaultMethod(method, service, proxy, args);

            }

            ServiceMethod<Object, Object> serviceMethod =

                (ServiceMethod<Object, Object>) loadServiceMethod(method);

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

            return serviceMethod.callAdapter.adapt(okHttpCall);

          }

        });

  }

從代碼來說,動態(tài)代理是比較簡單的。

<u>動態(tài)代理知識點解析</u>

動態(tài)代理(dynamic proxy)

       利用Java的反射技術(shù)(Java Reflection),在運行時創(chuàng)建一個實現(xiàn)某些給定接口的新類(也稱“動態(tài)代理類”)及其實例(對象),代理的是接口(Interfaces),不是類(Class),也不是抽象類。在運行時才知道具體的實現(xiàn),spring aop就是此原理。

  public static Object newProxyInstance(ClassLoader loader,

                                          Class<?>[] interfaces,

                                          InvocationHandler h)throws IllegalArgumentException



newProxyInstance,方法有三個參數(shù):

loader: 用哪個類加載器去加載代理對象

interfaces:動態(tài)代理類需要實現(xiàn)的接口

h:動態(tài)代理方法在執(zhí)行時,會調(diào)用h里面的invoke方法去執(zhí)行

<u>結(jié)合我們Retrofit.create方法以及動態(tài)代理的知識,也就是我們接口里的所有方法最后都是調(diào)用</u>


serviceMethod.callAdapter.adapt(okHttpCall);

這里返回了一個包裹OkHttpCall的對象

這里又有一個知識點

Retrofit


  final hik.lib.okhttp3.Call.Factory callFactory;

  final HttpUrl baseUrl;

  final List<Converter.Factory> converterFactories;

  final List<CallAdapter.Factory> adapterFactories;

  final @Nullable Executor callbackExecutor;

  final boolean validateEagerly;



adapterFactories 返回的對象到底是RxJava2CallAdapterFactory,還是默認(rèn)的DefaultCallAdapterFactory

我們看下serviceMethod中callAdapter屬性的初始化

ServiceMethod.Builder.buid()->ServiceMethod.Builder.createCallAdapter->retrofit.callAdapter->retrofit.nextCallAdapter

上代碼


public CallAdapter<?, ?> nextCallAdapter(@Nullable CallAdapter.Factory skipPast, Type returnType,

      Annotation[] annotations) {

    checkNotNull(returnType, "returnType == null");

    checkNotNull(annotations, "annotations == null");

    int start = adapterFactories.indexOf(skipPast) + 1;

    for (int i = start, count = adapterFactories.size(); i < count; i++) {

      CallAdapter<?, ?> adapter = adapterFactories.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(adapterFactories.get(i).getClass().getName());

      }

      builder.append('\n');

    }

    builder.append("  Tried:");

    for (int i = start, count = adapterFactories.size(); i < count; i++) {

      builder.append("\n  * ").append(adapterFactories.get(i).getClass().getName());

    }

    throw new IllegalArgumentException(builder.toString());

  }

<font color="Hotpink"><u>最后是根據(jù)接口的返回類型去匹配使用那個AdapterFactory</u></font>

  • 接下去我們看下AdaperFactory的實現(xiàn)

我們先看默認(rèn)的一個DefaultCallAdapterFactory的實現(xiàn)


final class DefaultCallAdapterFactory extends CallAdapter.Factory {

  static final CallAdapter.Factory INSTANCE = new DefaultCallAdapterFactory();

  @Override

  public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {

    if (getRawType(returnType) != Call.class) {

      return null;

    }

    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;

      }

    };

  }

}

直接返回的是OkHttpCall 所以網(wǎng)絡(luò)請求是 execute,enqueue。

如果是RxJava2CallAdapter 則返回的是Observable,然后通過RXJava方法進行網(wǎng)絡(luò)請求調(diào)用,上代碼


  public static void asyHttpRequest(Observable observable , BaseNetCallback netCallback){

        observable.subscribeOn(Schedulers.io())

                .observeOn(AndroidSchedulers.mainThread());

        observable.subscribe(new BaseNetObserver<>(netCallback));

    }



設(shè)置執(zhí)行的線程,監(jiān)聽結(jié)果的線程,然后發(fā)起信號執(zhí)行

我們在看下信號執(zhí)行里的代碼


  @Override protected void subscribeActual(Observer<? super Response<T>> observer) {

    // Since Call is a one-shot type, clone it for each new observer.

    Call<T> call = originalCall.clone();

    CallCallback<T> callback = new CallCallback<>(call, observer);

    observer.onSubscribe(callback);

    call.enqueue(callback);

  }



到這里的話我們基本已經(jīng)了解了Retrofit2 整個代碼的過程了

4.Retrofit的代碼總結(jié)


Retrofit 主要的代碼實現(xiàn)

  • OKHttpCall 主要是網(wǎng)絡(luò)請求實際實現(xiàn)的地方,主要有

    • execute

    • enqueue

    • createRawCall

  • ServiceMethod

    • toRequest

    • toResponse

    • Builder.build()

  • Retrofit

    • create

    • nextCallAdapter

    • nextResponseBodyConverter

最后編輯于
?著作權(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ù)。

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