Retrofit2 注解如何處理

近年來,Retrofit非承襞火异吻,配合Rxjava簡(jiǎn)直就是Android開發(fā)神器,今天談?wù)勊降资侨绾翁幚碜⒔獾摹?/p>

Retrofit注解分為方法注解和參數(shù)注解,具體就不多說诀浪。

Retrofit:compile 'com.squareup.retrofit2:retrofit:2.2.0'

直接進(jìn)源碼

  • Retrofit.create
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);
            }
//此處是重點(diǎn)
            ServiceMethod<Object, Object> serviceMethod =
                (ServiceMethod<Object, Object>) loadServiceMethod(method);
            OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
            return serviceMethod.callAdapter.adapt(okHttpCall);
          }
        });
  }

首先看Proxy.newProxyInstance:

try {
            return getProxyClass(loader, interfaces)
                    .getConstructor(InvocationHandler.class)
                    .newInstance(invocationHandler);
        } catch (NoSuchMethodException e) {
            cause = e;
        } catch (IllegalAccessException e) {
            cause = e;
        } catch (InstantiationException e) {
            cause = e;
        } catch (InvocationTargetException e) {
            cause = e;
        }

Proxy實(shí)例化就是返回一個(gè)InvocationHandler的示例棋返,類似代理模式;

回到上面的代碼雷猪,Retrofit.create就是創(chuàng)建了一個(gè)InvocationHandler睛竣,InvocationHandler執(zhí)行invoke返回ServiceMethod,

注解就是在ServiceMethod中處理的

  • ServiceMethod
    ServiceMethod是Builder模式求摇,靠?jī)?nèi)部類Builder實(shí)例化
  1. Builder初始化方法注解射沟、參數(shù)注解數(shù)組
Builder(Retrofit retrofit, Method method) {
      this.retrofit = retrofit;
      this.method = method;
      this.methodAnnotations = method.getAnnotations();
      this.parameterTypes = method.getGenericParameterTypes();
      this.parameterAnnotationsArray = method.getParameterAnnotations();
    }
  1. ServiceMethod.Builder.build()
public ServiceMethod build() {
      callAdapter = createCallAdapter();
      responseType = callAdapter.responseType();
     ...
      responseConverter = createResponseConverter();

      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];
   ......

        parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);//參數(shù)注解處理
      }
.....

      return new ServiceMethod<>(this);
    }

只看兩個(gè)方法, parseMethodAnnotation(annotation)和parseParameter与境,分別處理方法和參數(shù)注解

  • parseMethodAnnotation方法注解處理
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.");
        }
      } else if (annotation instanceof PATCH) {
        parseHttpMethodAndPath("PATCH", ((PATCH) annotation).value(), true);
      } else if (annotation instanceof POST) {
        parseHttpMethodAndPath("POST", ((POST) annotation).value(), true);
      } else if (annotation instanceof PUT) {
        parseHttpMethodAndPath("PUT", ((PUT) annotation).value(), true);
      } else if (annotation instanceof OPTIONS) {
        parseHttpMethodAndPath("OPTIONS", ((OPTIONS) annotation).value(), false);
      } else if (annotation instanceof HTTP) {
        HTTP http = (HTTP) annotation;
        parseHttpMethodAndPath(http.method(), http.path(), http.hasBody());
      } else if (annotation instanceof retrofit2.http.Headers) {
        String[] headersToParse = ((retrofit2.http.Headers) annotation).value();
        if (headersToParse.length == 0) {
          throw methodError("@Headers annotation is empty.");
        }
        headers = parseHeaders(headersToParse);
      } 
    }

方法注解實(shí)現(xiàn) parseHttpMethodAndPath:

賦值httpMethod和relativeUrl验夯,請(qǐng)求方法和url:
private void parseHttpMethodAndPath(String httpMethod, String value, boolean hasBody) {
    ...
      this.httpMethod = httpMethod;
      this.hasBody = hasBody;
  ....
      this.relativeUrl = value;
      this.relativeUrlParamNames = parsePathParameters(value);
    }

看parsePathParameters:

將方法注解值放入Set中
static Set<String> parsePathParameters(String path) {
    Matcher m = PARAM_URL_REGEX.matcher(path);
    Set<String> patterns = new LinkedHashSet<>();
    while (m.find()) {
      patterns.add(m.group(1));
    }
    return patterns;
  }
  • parseParameter-參數(shù)注解處理
循環(huán)處理每個(gè)注解
private ParameterHandler<?> parseParameter(
        int p, Type parameterType, Annotation[] annotations) {
      ParameterHandler<?> result = null;
      for (Annotation annotation : annotations) {
       //處理單個(gè)注解
        ParameterHandler<?> annotationAction = parseParameterAnnotation(
            p, parameterType, annotations, annotation);
        ...
        if (result != null) {
          throw parameterError(p, "Multiple Retrofit annotations found, only one allowed.");
        }

        result = annotationAction;
      }

...
      return result;
    }

處理單個(gè)注解:

這里我保留了Query,Path摔刁,F(xiàn)iled挥转,Body注解的處理,其他注解類似共屈;

private ParameterHandler<?> parseParameterAnnotation(
        int p, Type type, Annotation[] annotations, Annotation annotation) {
     ...
        gotUrl = true;

        if (type == HttpUrl.class
            || type == String.class
            || type == URI.class
            || (type instanceof Class && "android.net.Uri".equals(((Class<?>) type).getName()))) {
          return new ParameterHandler.RelativeUrl();
        } else {
          throw parameterError(p,
              "@Url must be okhttp3.HttpUrl, String, java.net.URI, or android.net.Uri type.");
        }

      } else if (annotation instanceof Path) {
        if (gotQuery) {
    ...
        gotPath = true;

        Path path = (Path) annotation;
        String name = path.value();
        validatePathName(p, name);

        Converter<?, String> converter = retrofit.stringConverter(type, annotations);
        return new ParameterHandler.Path<>(name, converter, path.encoded());

      } else if (annotation instanceof Query) {
        Query query = (Query) annotation;
        String name = query.value();
        boolean encoded = query.encoded();

        Class<?> rawParameterType = Utils.getRawType(type);
        gotQuery = true;
        if (Iterable.class.isAssignableFrom(rawParameterType)) {
          if (!(type instanceof ParameterizedType)) {
            throw parameterError(p, rawParameterType.getSimpleName()
                + " must include generic type (e.g., "
                + rawParameterType.getSimpleName()
                + "<String>)");
          }
          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 if (rawParameterType.isArray()) {
          Class<?> arrayComponentType = boxIfPrimitive(rawParameterType.getComponentType());
          Converter<?, String> converter =
              retrofit.stringConverter(arrayComponentType, annotations);
          return new ParameterHandler.Query<>(name, converter, encoded).array();
        } else {
          Converter<?, String> converter =
              retrofit.stringConverter(type, annotations);
          return new ParameterHandler.Query<>(name, converter, encoded);
        }

      } 
       ...
       else if (annotation instanceof Field) {
        if (!isFormEncoded) {
          throw parameterError(p, "@Field parameters can only be used with form encoding.");
        }
        Field field = (Field) annotation;
        String name = field.value();
        boolean encoded = field.encoded();

        gotField = true;

        Class<?> rawParameterType = Utils.getRawType(type);
        if (Iterable.class.isAssignableFrom(rawParameterType)) {
          if (!(type instanceof ParameterizedType)) {
            throw parameterError(p, rawParameterType.getSimpleName()
                + " must include generic type (e.g., "
                + rawParameterType.getSimpleName()
                + "<String>)");
          }
          ParameterizedType parameterizedType = (ParameterizedType) type;
          Type iterableType = Utils.getParameterUpperBound(0, parameterizedType);
          Converter<?, String> converter =
              retrofit.stringConverter(iterableType, annotations);
          return new ParameterHandler.Field<>(name, converter, encoded).iterable();
        } else if (rawParameterType.isArray()) {
          Class<?> arrayComponentType = boxIfPrimitive(rawParameterType.getComponentType());
          Converter<?, String> converter =
              retrofit.stringConverter(arrayComponentType, annotations);
          return new ParameterHandler.Field<>(name, converter, encoded).array();
        } else {
          Converter<?, String> converter =
              retrofit.stringConverter(type, annotations);
          return new ParameterHandler.Field<>(name, converter, encoded);
        }

      } ...
       ...
else if (annotation instanceof Body) {
        if (isFormEncoded || isMultipart) {
          throw parameterError(p,
              "@Body parameters cannot be used with form or multi-part encoding.");
        }
        if (gotBody) {
          throw parameterError(p, "Multiple @Body method annotations found.");
        }

        Converter<?, RequestBody> converter;
        try {
          converter = retrofit.requestBodyConverter(type, annotations, methodAnnotations);
        } catch (RuntimeException e) {
          // Wide exception range because factories are user code.
          throw parameterError(e, p, "Unable to create @Body converter for %s", type);
        }
        gotBody = true;
        return new ParameterHandler.Body<>(converter);
      }

      return null; // Not a Retrofit annotation.
    }

該方法針對(duì)注解類型绑谣,返回不同的ParameterHandler內(nèi)部類實(shí)例,比如ParameterHandler.Body拗引,ParameterHandler.Query借宵。

就說這么多,注解處理流程大概就是這么多矾削。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末壤玫,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子怔软,更是在濱河造成了極大的恐慌垦细,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,651評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件挡逼,死亡現(xiàn)場(chǎng)離奇詭異括改,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)家坎,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,468評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門嘱能,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人虱疏,你說我怎么就攤上這事惹骂。” “怎么了做瞪?”我有些...
    開封第一講書人閱讀 162,931評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵对粪,是天一觀的道長(zhǎng)右冻。 經(jīng)常有香客問我,道長(zhǎng)著拭,這世上最難降的妖魔是什么纱扭? 我笑而不...
    開封第一講書人閱讀 58,218評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮儡遮,結(jié)果婚禮上乳蛾,老公的妹妹穿的比我還像新娘。我一直安慰自己鄙币,他們只是感情好肃叶,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,234評(píng)論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著十嘿,像睡著了一般因惭。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上详幽,一...
    開封第一講書人閱讀 51,198評(píng)論 1 299
  • 那天筛欢,我揣著相機(jī)與錄音浸锨,去河邊找鬼唇聘。 笑死,一個(gè)胖子當(dāng)著我的面吹牛柱搜,可吹牛的內(nèi)容都是我干的迟郎。 我是一名探鬼主播,決...
    沈念sama閱讀 40,084評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼聪蘸,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼宪肖!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起健爬,我...
    開封第一講書人閱讀 38,926評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤控乾,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后娜遵,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蜕衡,經(jīng)...
    沈念sama閱讀 45,341評(píng)論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,563評(píng)論 2 333
  • 正文 我和宋清朗相戀三年设拟,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了慨仿。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,731評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡纳胧,死狀恐怖镰吆,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情跑慕,我是刑警寧澤万皿,帶...
    沈念sama閱讀 35,430評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響牢硅,放射性物質(zhì)發(fā)生泄漏慰于。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,036評(píng)論 3 326
  • 文/蒙蒙 一唤衫、第九天 我趴在偏房一處隱蔽的房頂上張望婆赠。 院中可真熱鬧,春花似錦佳励、人聲如沸休里。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,676評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽妙黍。三九已至,卻和暖如春瞧剖,著一層夾襖步出監(jiān)牢的瞬間拭嫁,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,829評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工抓于, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留做粤,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,743評(píng)論 2 368
  • 正文 我出身青樓捉撮,卻偏偏與公主長(zhǎng)得像怕品,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子巾遭,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,629評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • 一肉康、什么是Retrofit A type-safe HTTP client for Android and Jav...
    andcoder閱讀 770評(píng)論 2 3
  • retrofit2 官網(wǎng) http://square.github.io/retrofit/ 首先介紹下retro...
    wangliang0209閱讀 4,965評(píng)論 2 1
  • 本文將順著構(gòu)建請(qǐng)求對(duì)象->構(gòu)建請(qǐng)求接口->發(fā)起同步/異步請(qǐng)求的流程,分析Retrofit是如何實(shí)現(xiàn)的灼舍。 開始之前吼和,...
    zhuhf閱讀 1,614評(píng)論 0 10
  • 適配器模式上一篇文章我們已經(jīng)分析了Retrofit解析注解封裝進(jìn)ServiceMethod的流程,讀者在這里要記住...
    andcoder閱讀 652評(píng)論 0 2
  • 什么是注解(Annotation):Annotation(注解)就是Java提供了一種元程序中的元素關(guān)聯(lián)任何信息和...
    九尾喵的薛定諤閱讀 3,163評(píng)論 0 2