Retrofit源碼分析

Retrofit其實是對OkHttp的一個封裝唆迁,方便進行網絡API的調用

Paste_Image.png

使用方法:定義一個接口逢享,用注解標注方法,每個方法對應一個API接口眉抬。

public interface GitHubService {
  @GET("users/{user}/repos")
  Call<List<Repo>> listRepos(@Path("user") String user);
}

Retrofit retrofit = new Retrofit.Builder().baseUrl("https://api.github.com/").build();

GitHubService service = retrofit.create(GitHubService.class);

Call<List<Repo>> repos = service.listRepos("octocat");

Retrofit.Builder.build:

    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> adapterFactories = new ArrayList<>(this.adapterFactories);
      adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));

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

      return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
          callbackExecutor, validateEagerly);
    }

callFactory是OkHttpClient贯吓,callBackExecutor在android平臺上是MainThreadExecutor,CallAdaptorFactory在android平臺上是ExecutorCallAdapterFactory蜀变,如果有自定義converterFactory也會傳給Retrofit實例悄谐。validateEagerly代表create service時就驗證全部的方法,否則只有調用時才驗證库北。

先看看如何create service爬舰,Retrofit.create:

  @SuppressWarnings("unchecked") // Single-interface proxy creation guarded by parameter safety.
  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, 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 serviceMethod = loadServiceMethod(method);
            OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
            return serviceMethod.callAdapter.adapt(okHttpCall);
          }
        });
  }

對接口創(chuàng)建動態(tài)代理们陆。調用loadServiceMethod創(chuàng)建ServiceMethod,然后創(chuàng)建OkHttpCall(實際上包裝okhttp3.Call)情屹,然后再用service.callAdaptor適配一下(android平臺上默認是ExecutorCallbackCall坪仇,目的是在調用enqueue進行異步調用時在主線程上進行回調),converterFactory默認為BuiltInConverters;垃你。包裝關系:

Paste_Image.png

來看看ServiceMethod的創(chuàng)建流程椅文,Retrofit.loadServiceMethod:

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

先查看cache里有沒有,否則用ServiceMethod.builder創(chuàng)建一個蜡镶,放入cache中

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

Builder構造函數(shù)中得到method的注解雾袱,parameter的type,parameter的注解

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

    private CallAdapter<?> 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 {
        return 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);
      }
    }

build里先會創(chuàng)建CallAdapter官还。

    private CallAdapter<?> 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 {
        return 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);
      }
    }

從CallAdapterFactories里找到第一個創(chuàng)建CallAdapter的Factory芹橡。這里是ExecutorCallAdapterFactory

  @Override
  public CallAdapter<Call<?>> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
    if (getRawType(returnType) != Call.class) {
      return null;
    }
    final Type responseType = Utils.getCallResponseType(returnType);
    return new CallAdapter<Call<?>>() {
      @Override public Type responseType() {
        return responseType;
      }

      @Override public <R> Call<R> adapt(Call<R> call) {
        return new ExecutorCallbackCall<>(callbackExecutor, call);
      }
    };
  }

檢查返回的類型是不是Call,然后得到Call的泛型類型望伦。返回CallAdaptor林说。接著回到build

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

得到responseType,這里是List<Repo>屯伞,創(chuàng)建responseConverter腿箩。調用parseMethodAnnotation解析method的注解。這里代碼較多劣摇,我們就看get的處理邏輯

    private void parseMethodAnnotation(Annotation annotation) {
      if (annotation instanceof GET) {
        parseHttpMethodAndPath("GET", ((GET) annotation).value(), false);
      } 

    private void parseHttpMethodAndPath(String httpMethod, String value, boolean hasBody) {
      if (this.httpMethod != null) {
        throw methodError("Only one HTTP method is allowed. Found: %s and %s.",
            this.httpMethod, httpMethod);
      }
      this.httpMethod = httpMethod;
      this.hasBody = hasBody;

      if (value.isEmpty()) {
        return;
      }

      // Get the relative URL path and existing query string, if present.
      int question = value.indexOf('?');
      if (question != -1 && question < value.length() - 1) {
        // Ensure the query string does not have any named parameters.
        String queryParams = value.substring(question + 1);
        Matcher queryParamMatcher = PARAM_URL_REGEX.matcher(queryParams);
        if (queryParamMatcher.find()) {
          throw methodError("URL query string \"%s\" must not have replace block. "
              + "For dynamic query parameters use @Query.", queryParams);
        }
      }

      this.relativeUrl = value;
      this.relativeUrlParamNames = parsePathParameters(value);
    }

根據(jù)注解確定httpmethod珠移,得到注解里的值,判斷有沒有body末融。
從注解的path中得到需要替換的path钧惧,放入relativeUrlParamNames中,在這里就是user

回到build

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

對每一個參數(shù)勾习,根據(jù)其上的注解浓瞪,創(chuàng)建paramterHandler

最后返回一個創(chuàng)建的ServiceMethod。然后用這個ServiceMethod創(chuàng)建OkHttpCall巧婶,然后再創(chuàng)建ExecutroCallbackCall乾颁。

      return new ServiceMethod<>(this);

調用service的方法會返回一個ExecutroCallbackCall。要獲得response艺栈,需要調用call上的execute英岭。ExecutroCallbackCall的execute方法是調用OkHttpCall的execute方法。

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

    synchronized (this) {
      .....
      call = rawCall;
      if (call == null) {
        try {
          call = rawCall = createRawCall();
        } catch (IOException | RuntimeException e) {
          creationFailure = e;
          throw e;
        }
      }
    }
    return parseResponse(call.execute());
  }

先調用createRawCall創(chuàng)建一個rawCall眼滤,也就是Okhttp3的call巴席,然后調用它的execute方法,將返回的response傳給parseResponse诅需。

  private okhttp3.Call createRawCall() throws IOException {
    Request request = serviceMethod.toRequest(args);
    okhttp3.Call call = serviceMethod.callFactory.newCall(request);
    if (call == null) {
      throw new NullPointerException("Call.Factory returned null.");
    }
    return call;
  }

先調用serviceMethod.toRequest創(chuàng)建Okhttp的request漾唉,args是service方法調用的參數(shù),然后調用OkHttpClient創(chuàng)建Call堰塌。

  Request toRequest(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();
  }

調用ParamterHandler來處理每個參數(shù)赵刑,然后生成Okttp中的request。

最后調用call.execute方法得到okhttp的response场刑,然后調用parseResponse來解析這個response般此。

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

最后是調用responseConverter.convert來講okhttp的ResponseBody轉換成List<Repo>, 然后放入Retrofit的response的body中

T body = serviceMethod.toResponse(catchingBody); -> responseConverter.convert(body);
return Response.success(body, rawResponse);

最后將這個Retrofit的response從execute中返回出去

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市牵现,隨后出現(xiàn)的幾起案子铐懊,更是在濱河造成了極大的恐慌,老刑警劉巖瞎疼,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件科乎,死亡現(xiàn)場離奇詭異,居然都是意外死亡贼急,警方通過查閱死者的電腦和手機茅茂,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來太抓,“玉大人空闲,你說我怎么就攤上這事∽叩校” “怎么了碴倾?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長掉丽。 經常有香客問我跌榔,道長,這世上最難降的妖魔是什么机打? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任矫户,我火速辦了婚禮,結果婚禮上残邀,老公的妹妹穿的比我還像新娘皆辽。我一直安慰自己,他們只是感情好芥挣,可當我...
    茶點故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布驱闷。 她就那樣靜靜地躺著,像睡著了一般空免。 火紅的嫁衣襯著肌膚如雪空另。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天蹋砚,我揣著相機與錄音扼菠,去河邊找鬼摄杂。 笑死,一個胖子當著我的面吹牛循榆,可吹牛的內容都是我干的析恢。 我是一名探鬼主播,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼秧饮,長吁一口氣:“原來是場噩夢啊……” “哼映挂!你這毒婦竟也來了?” 一聲冷哼從身側響起盗尸,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤柑船,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后泼各,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體鞍时,經...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年历恐,在試婚紗的時候發(fā)現(xiàn)自己被綠了寸癌。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡弱贼,死狀恐怖蒸苇,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情吮旅,我是刑警寧澤溪烤,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站庇勃,受9級特大地震影響檬嘀,放射性物質發(fā)生泄漏。R本人自食惡果不足惜责嚷,卻給世界環(huán)境...
    茶點故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一鸳兽、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧罕拂,春花似錦揍异、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至柿菩,卻和暖如春戚嗅,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工懦胞, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留替久,地道東北人。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓医瘫,卻偏偏與公主長得像侣肄,于是被迫代替她去往敵國和親旧困。 傳聞我的和親對象是個殘疾皇子醇份,可洞房花燭夜當晚...
    茶點故事閱讀 44,713評論 2 354

推薦閱讀更多精彩內容

  • 最近非常流行 Retrofit+RxJava+OkHttp 這一整套的網絡請求和異步操作的開源框架,從 Jake ...
    慌不要慌閱讀 1,974評論 1 7
  • Retrofit Version:2.2.0分析代碼地址:https://github.com/Blankeer/...
    Blankeer閱讀 223評論 0 1
  • 本篇為個人理解心得 多有不詳細 還請移步:http://www.reibang.com/p/0c055ad46b...
    番茄tomato閱讀 164評論 0 1
  • Retrofit是用在網絡請求中非常常用的一個庫吼具,這里從2.5.0版本的源碼去分析一下它的工作機制僚纷。看看它實際做了...
    kisass閱讀 209評論 0 0
  • 黑龍閣主 最近有一句話比較流行:回歸傳統(tǒng)。而回歸傳統(tǒng)實質上就是認識歷史陡蝇,了解歷史痊臭,以史為鑒。中華歷史上下五千年登夫,傳...
    Luibe閣主閱讀 298評論 0 3