Android:Retrofit源碼解析

前言

本文主要是帶着三個問題去理解Retrofit的工做原理;java

  • 1.咱們調用定義好的接口後發生了什麼,接口api究竟是怎麼解析出來的?
  • 2.Retrofit是怎麼和Okhttp合做的,由於發送請求最終都是Okhttp;
  • 3.解析返回的數據怎麼corvert成RxJava的observable的?

帶着這三個問題,咱們開始下面的講解。android

Retrofit的使用

public interface MedalApi {
    String url = "employeeMedal/medalList.do";

    @FormUrlEncoded
    @POST(url)
    Observable<AchievedMedalResult> getAchievedMedal(@Field("account") String account,
                                                 @Field("accountType") String accountType,
                                                 @Field("queryEmployeeId263") String queryEmployeeId263);

    @GET(url)
    Observable<AchievedMedalResult> testMedal();

    @POST("/example_copy_copy/medallist")
    Observable<AchievedMedalDetailResult> getAchievedMedalDetail(@Field("account") String account,
                                                                 @Field("accountType") String accountType,
                                                                 @Field("queryEmployeeId263") String queryEmployeeId263,
                                                                 @Field("queryMedalCode") String queryMedalCode
                                                                 );

}


    public static <T> T createService(Class<T> service, String baseUrl) {
        //日誌處理
        HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
            @Override
            public void log(String message) {
                L.d(TAG, message);
            }
        });

        loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
        OkHttpClient okHttpClient = new OkHttpClient.Builder()
                //.addInterceptor(new LoggerInterceptor("TAG"))
                .connectTimeout(CONNECT_TIMEOUT, TimeUnit.MILLISECONDS)
                .readTimeout(READ_TIMEOUT, TimeUnit.MILLISECONDS)
                /* 關閉OkHttp失敗重試鏈接的機制,該問題致使發帖重複的問題 */
                .retryOnConnectionFailure(false)
                .addInterceptor(loggingInterceptor)
                .addInterceptor(new HttpHeadInterceptor())
                .addInterceptor(new SecurityInterceptor(AppInstance.INSTANCE)) // 加密解密
                .addNetworkInterceptor(new StethoInterceptor())
                .dns(new SunlandDNS())
                // 其餘配置
                .build();
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(baseUrl)
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                .addConverterFactory(GsonConverterFactory.create())
                .client(okHttpClient)
                .build();

        return retrofit.create(service);
    }
複製代碼

上面就是實際使用了,作一些簡單配置就能夠發送請求了,返回的數據是轉換後好的實體類,確實很方便。json

原理解析

上面簡單講了使用方法,下面開始進入正題,講講背後的原理。api

Retrofit實例化

首先看看它是怎麼被初始化的,就是入口在哪?很顯然,就是那個build,建造者模式,這個和okhttp是殊途同歸。緩存

public Retrofit build() {
      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();
      }

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

      // Make a defensive copy of the converters.
      List<Converter.Factory> converterFactories =
          new ArrayList<>(1 + this.converterFactories.size());

      // Add the built-in converter factory first. This prevents overriding its behavior but also
      // ensures correct behavior when using converters that consume all types.
      converterFactories.add(new BuiltInConverters());
      converterFactories.addAll(this.converterFactories);

      return new Retrofit(callFactory, baseUrl, unmodifiableList(converterFactories),
          unmodifiableList(callAdapterFactories), callbackExecutor, validateEagerly);
    }
  }
複製代碼

它會接收一些外面傳進來的參數,好比url,默認初始化okhttpclient,covert等,而後直接new了一個Retrofit,這纔是把初始化的一些值真正保存到Retrofit對象裏面去;bash

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; // Copy+unmodifiable at call site.
    this.callAdapterFactories = callAdapterFactories; // Copy+unmodifiable at call site.
    this.callbackExecutor = callbackExecutor;
    this.validateEagerly = validateEagerly;
  }
複製代碼

這裏面的參數都是很是重要的,有的是默認的,有的是外面初始化的時候傳遞進來的。網絡

Retrofit的create()

好了retrofit實例有了,接着看create方法。app

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.adapt(okHttpCall);
          }
        });
  }
複製代碼

這裏把咱們定義好的api接口當作class參數傳遞進來,能夠看到,這裏面採用的是java動態代理技術,把接口api裏面的方法集中在代理的invoke方法中去執行了,作了以下幾件事異步

  • 對method作校驗,object默認的方法咱們不用去管,直接執行。
  • 對平臺的校驗,在java8中默認會作校驗,android中,咱們不用去理會。
  • 重點的是最後三行代碼,建立ServiceMethod,實例化OkHttpCall, serviceMethod.adapt。

接下來咱們對最後三行代碼作分析;ide

首先看loadServiceMethod :

private final Map<Method, ServiceMethod<?, ?>> serviceMethodCache = new ConcurrentHashMap<>();

  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;
  }
複製代碼

serviceMethodCache裏面存放的是Method和ServiceMethod的鍵值對,首先從map中去讀取ServiceMethod,若是當前的值存在,那就直接使用。否者就須要新建ServiceMethod。這個邏輯是很是簡單的,就是使用了緩存機制,由於java的動態代理和註解的解析都是比較耗時的,因此緩存是頗有必要的。接下來看看ServiceMethod究竟是何方神聖,須要咱們大費周折的去獲取或新建。 ServiceMethod的Build()

this.retrofit = retrofit;
      this.method = method;
      this.methodAnnotations = method.getAnnotations();
      this.parameterTypes = method.getGenericParameterTypes();
      this.parameterAnnotationsArray = method.getParameterAnnotations();
    }
複製代碼

這裏把retrofit和method看成參數傳遞過來,而後獲取method裏面的一些參數,是的,api接口看上去是很抽象的,裏面有註解,參數類型,還有參數的註解等等,首先獲取api裏的這些參數,而後看看build();

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);
    }
複製代碼

這個方法看上去比較長,可是並不難,咱們分步說明之:

  • 1.建立calladapter,這個方法最終的建立過程是在retrofit中,例如建立RxJava的CallAdapter
  • 2.建立responseConverter,返回結果的covert,這個最終也是在retrofit裏面建立的,例如建立GsonConveter
  • 3.查找method中的全部註解,並一個個解析出來,http的各類請求post,get,put等註解,都是在這裏解析出來的。
private void parseMethodAnnotation(Annotation annotation) {
      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);
        if (!Void.class.equals(responseType)) {
          throw methodError("HEAD method must use Void as response type.");
        }
    }
複製代碼
  • 4.對上面解析出來的註解作個校驗,須要有httpmethod,也就是說必需要有請求方法的註解才行,不然會拋異常,這也很好理解,發請求確定是要指明是post仍是get吧。
  • 5.對請求作一些其餘的校驗,好比post請求必須含有body等。再就是URL是否爲空。
  • 6.全部的校驗經過之後,最後就是實例化了,以下:
ServiceMethod(Builder<R, T> builder) {
    this.callFactory = builder.retrofit.callFactory();
    this.callAdapter = builder.callAdapter;
    this.baseUrl = builder.retrofit.baseUrl();
    this.responseConverter = builder.responseConverter;
    this.httpMethod = builder.httpMethod;
    this.relativeUrl = builder.relativeUrl;
    this.headers = builder.headers;
    this.contentType = builder.contentType;
    this.hasBody = builder.hasBody;
    this.isFormEncoded = builder.isFormEncoded;
    this.isMultipart = builder.isMultipart;
    this.parameterHandlers = builder.parameterHandlers;
  }
複製代碼

好了,這個扯得有點多了,讓咱們仍是回到那個代理類得invoke方法裏面去。

OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
複製代碼

這裏實例化OkHttpCall,其中參數就是咱們剛剛新建好的serviceMethod,這個call實質上就是okhttp裏面的那個call,對的,最終調用網絡請求就靠它了。 若是上面的都是準備工做的話,接下來纔是正式開始發送請求了。這裏我決定分兩部分講,一種是adapt返回普通的Call,另一種返回RxJava的Observable,實質上這也是Retrofit的兩種不一樣用法。

return serviceMethod.adapt(okHttpCall);
複製代碼

返回類型是Call的調用

adapt方法返回類型是有okhttpcall裏面的servicemethod來決定的。OkHttpCall實現了Call,這裏返回的是call,接下來就是執行請求了。請求的執行分同步和異步。 同步的請求方法是 call.execute();

@Override public Response<T> execute() throws IOException {
    okhttp3.Call call;

    synchronized (this) {
      if (executed) throw new IllegalStateException("Already executed.");
      executed = true;

      if (creationFailure != null) {
        if (creationFailure instanceof IOException) {
          throw (IOException) creationFailure;
        } else if (creationFailure instanceof RuntimeException) {
          throw (RuntimeException) creationFailure;
        } else {
          throw (Error) creationFailure;
        }
      }

      call = rawCall;
      if (call == null) {
        try {
          call = rawCall = createRawCall();
        } catch (IOException | RuntimeException | Error e) {
          throwIfFatal(e); //  Do not assign a fatal error to creationFailure.
          creationFailure = e;
          throw e;
        }
      }
    }

    if (canceled) {
      call.cancel();
    }

    return parseResponse(call.execute());
  }
複製代碼

發送請求的是rawCall;

private okhttp3.Call createRawCall() throws IOException {
    okhttp3.Call call = serviceMethod.toCall(args);
    if (call == null) {
      throw new NullPointerException("Call.Factory returned null.");
    }
    return call;
  }
複製代碼

繼續跟

/** Builds an HTTP request from method arguments. */
  okhttp3.Call toCall(@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 callFactory.newCall(requestBuilder.build());
  }
複製代碼

這個過程調用servicemethod的toCall方法去構建網絡請求,返回okhttp3.call,以後再調用call裏面的ececute(),最後調用parseResponse對返回的結果進行解析;這裏解析就不作過多分析了。

接着看看異步的調用: 事實上了解同步之後,異步也就差很少了:

@Override public void enqueue(final Callback<T> callback) {
    checkNotNull(callback, "callback == null");

    okhttp3.Call call;
    Throwable failure;

    synchronized (this) {
      if (executed) throw new IllegalStateException("Already executed.");
      executed = true;

      call = rawCall;
      failure = creationFailure;
      if (call == null && failure == null) {
        try {
          call = rawCall = createRawCall();
        } catch (Throwable t) {
          throwIfFatal(t);
          failure = creationFailure = t;
        }
      }
    }

    if (failure != null) {
      callback.onFailure(this, failure);
      return;
    }

    if (canceled) {
      call.cancel();
    }

    call.enqueue(new okhttp3.Callback() {
      @Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
        Response<T> response;
        try {
          response = parseResponse(rawResponse);
        } catch (Throwable 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();
        }
      }
    });
  }
複製代碼

就是把剛剛新建的request放到線程池裏面去,而後使用callback對返回結果進行解析。以上就是普通的返回call的調用方法。

對於RxJava中的調用

咱們使用的時候是怎麼初始化的:

.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
     .addConverterFactory(GsonConverterFactory.create())
複製代碼

這裏是使用的RxJava和GsonConvert,將它們保存在Retrofit的對象裏面先存着,後面會用到。接着看ServiceMethod中build(),裏面會調用createCallAdapter(),從字面意思能夠理解爲建立adapter; 最終會調用Retrofit中的函數:

public CallAdapter<?, ?> nextCallAdapter(@Nullable CallAdapter.Factory skipPast, Type returnType,
      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;
      }
    }
...
  }
複製代碼

咱們只看重點,它就是遍歷callAdapterFactories裏面的CallAdapter,callAdapterFactories不就是咱們剛剛往裏面addCallAdapterFactory值了的嗎,沒錯,因此最終會調用RxJavaCallAdapterFactory的get方法

@Override
  public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
 ...
    if (!(returnType instanceof ParameterizedType)) {
      String name = isSingle ? "Single" : "Observable";
      throw new IllegalStateException(name + " return type must be parameterized"
          + " as " + name + "<Foo> or " + name + "<? extends Foo>");
    }

    Type observableType = getParameterUpperBound(0, (ParameterizedType) returnType);
    Class<?> rawObservableType = getRawType(observableType);

...
    return new RxJavaCallAdapter(responseType, scheduler, isAsync, isResult, isBody, isSingle,
        false);
  }
複製代碼

看重點,這個get方法通過必定處理後,最終會實例化一個RxJavaCallAdapter 對象,而後會去調用這個類裏面的adapt方法。

@Override public Object adapt(Call<R> call) {
    OnSubscribe<Response<R>> callFunc = isAsync
        ? new CallEnqueueOnSubscribe<>(call)
        : new CallExecuteOnSubscribe<>(call);

    OnSubscribe<?> func;
    if (isResult) {
      func = new ResultOnSubscribe<>(callFunc);
    } else if (isBody) {
      func = new BodyOnSubscribe<>(callFunc);
    } else {
      func = callFunc;
    }
    Observable<?> observable = Observable.create(func);

    if (scheduler != null) {
      observable = observable.subscribeOn(scheduler);
    }

    if (isSingle) {
      return observable.toSingle();
    }
    if (isCompletable) {
      return observable.toCompletable();
    }
    return observable;
  }
複製代碼
T adapt(Call<R> call) {
    return callAdapter.adapt(call);
  }

複製代碼

因此不一樣的callAdapter會調用不一樣的類中的adapt,返回的結果也不同,前面說的默認的都是返回的call,RxJava返回的是Observable。而後就是使用Rxjava去髮網絡請求咯:

@Override public void call(Subscriber<? super Response<T>> subscriber) {
    // Since Call is a one-shot type, clone it for each new subscriber.
    Call<T> call = originalCall.clone();
    CallArbiter<T> arbiter = new CallArbiter<>(call, subscriber);
    subscriber.add(arbiter);
    subscriber.setProducer(arbiter);

    Response<T> response;
    try {
      response = call.execute();
    } catch (Throwable t) {
      Exceptions.throwIfFatal(t);
      arbiter.emitError(t);
      return;
    }
    arbiter.emitResponse(response);
  }
}
複製代碼

裏面的call.execute()就是執行網絡請求的,這些就和前面沒有區別了,最後會獲得Respone,而後進行解析,這就講清楚了怎麼使用RxJava發送網絡請求的,那麼第二個問題,最終咱們怎麼把返回值轉換爲對應的Observable呢。接着看,分析方法相似於上面,在ServiceMethod的build中有

responseConverter = createResponseConverter();
複製代碼

這就是建立Convert,這個Convert就是初始化的時候傳遞進去的,咱們傳的是GsonConverterFactory,因此和adapter同樣最終會走到Retrofit裏面去:

{
    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;
      }
    }

  }
複製代碼

代碼和分析adapter的差很少,最終會走到GsonConverterFactory

public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
      Retrofit retrofit) {
    TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
    return new GsonResponseBodyConverter<>(gson, adapter);
  }
複製代碼

當Retrofit在解析Respone數據的時候會調用ServiceMothod的以下方法:

/** Builds a method return value from an HTTP response body. */
  R toResponse(ResponseBody body) throws IOException {
    return responseConverter.convert(body);
  }
複製代碼

繼續跟:

@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();
    }
  }
複製代碼

就是Gson將返回結果作了處理,直接convert成對象返回了,最後再封裝成Observable。

總結

分析完後咱們再來看看文章開頭的三個問題,是否是都已經有結果了,最後咱們上一張圖,整個Retrofit的分析流程圖:

相關文章
相關標籤/搜索