Retrofit 中的注解以及如何自定义接口方法注解


接口方法和方法参数注解

Retrofit 框架中一个最大的特点就是用注解来描述 HTTP 请求

  • 支持 URL 参数替换和查询参数
  • 将对象转换为请求主体(例如,JSON、协议缓冲区)
  • 多部分请求正文和文件上传

注解分为方法注解和方法参数注解,它标志着一个请求将会被如何处理。

1.方法注解

1.1 注解描述请求方式
注解名字表示意思
@GETGET请求
@POSTPOST请求
@DELETEDELETE请求
@HEADHEAD请求
@OPTIONSOPTIONS请求
@PATCHPATCH请求
例子

1.注解@GET

    @GET("/users/query/all")
    Call<List<User>> getUsers();

2.注解@POST

    @POST("/users/query")
    @FormUrlEncoded
    Call<User> getUser(@Field("id") String id);
1.2 注解描述请求内容类型、返回结果和请求头
注解名字表示意思
@FormUrlEncoded该注解与方法注解 @POST 和参数注解 @Field 一起使用,标志着请求实体(body部分)内容会经过 URL 编码后上传,以及该请求(POST)下的 Content-Type 为 " application/x-www-form-urlencoded " 。换一句话说,就是请求的参数以 key1=value1&key2=value2… 的方式发送到服务端,并且参数会经过 URL 编码。
@Multipart该注解与方法注解 @POST 和参数注解 @Part 一起使用,标志着请求实体(body部分)内容使用表单上传,以及该请求(POST)下的 Content-Type 为 " multipart/form-data " 。使用表单上传的数据可以是纯文本,纯文件,也可以是文本+文件。
@Streaming将返回结果(ResponseBody)转换为流(字节)。
@Headers标志请求头。
例子

1.注解@FormUrlEncoded

发送POST请求,请求头Content-Type:application/x-www-form-urlencoded,请求实体id=value

    @POST("/users/query")
    @FormUrlEncoded
    Call<User> getUser(@Field("id") String id);

2.注解@Multipart

发送POST请求,请求头Content-Type: multipart/form-data,请求实体id=value和图片文件。

    @POST("/users/update")
    @Multipart
    Call<ResponseBody> updateUserPortrait(@Part("id") int id, @Part(value = "image", encoding = "8-bit") RequestBody image);

3.注解@Streaming

    @GET("/users/download")
    @Streaming
    Call<ResponseBody> downloadUserFile(@Query("id") int id);

4.注解@Headers

    @Headers("Cache-Control: max-age=640000")
    @GET("/users/download")
    @Streaming
    Call<ResponseBody> downloadUserFile(@Query("id") int id);

2.参数注解

注解名字表示意思
@Body该注解与方法注解 @POST 或 @PUT 一起使用,标志参数是一个POJO 或 RequestBody对象,最终会直接将参数对象转换为请求实体(body部分)内容。
@Field该注解与方法注解 @POST 和 @FormUrlEncoded 一起使用,标志着参数是一个键值对,也就是请求实体(body部分)是以key1=value1&key2=value2… 或 key=value1&key=value2… 方式传递。
@FieldMap与@Field注解一样,只不过参数对象是一个Map。
@Header标志参数是一个请求头
@HeaderMap标志参数是一个Map集合的请求头
@Part该注解与方法注解 @POST 和参数注解 @Multipart 一起使用
@PartMap与@Part注解一样,只不过参数对象是一个Map,Map接受的类型是<String,RequestBody>
@Path标志参数是用来替换请求路径的
@Query标志参数(@Query(“key1”) String value1)会追加在请求路径后面(?key1=value1)。
@QueryMap与@Query注解一样,只不过参数对象是一个Map集合,会Map集合转换为键值对。
@QueryName与@Query注解相似,只不过参数(@QueryName String filter)会直接追加在路径后面(?filter)
@Tag标志参数用来给这次请求打个Tag
@Url标志参数用来替换请求路径,会忽略baseUrl
例子

1.注解@Body

    @POST("/users/upload")
    Call<ResponseBody> uploadUserLog(@Body RequestBody body);

    //创建RequestBody
    RequestBody body = RequestBody.create(MediaType.parse("Content-Type: application/json;charset=UTF-8"), "Your json log");
    UserService#uploadUserLog(body);

2.注解@Filed@FiledMap

注解@Filed

   @POST("/users/query")
    @FormUrlEncoded
    Call<User> getUser(@Field("id") String id);

注解@FieldMap

    @POST("/users/query")
    @FormUrlEncoded
    Call<User> getUser(@FieldMap Map<String, String> filedMap);


    Map<String,String> filedMap = new HashMap<>();
    filedMap.put("id", "123");
    filedMap.put("age", "25");
    UserService#getUser(filedMap);

2.注解@Header@HeaderMap

注解@Header

    @POST("/users/query")
    @FormUrlEncoded
    Call<User> getUser(@Field("id") String id, @Header("Accept-Language") String lang);

注解@HeaderMap

    @POST("/users/query")
    @FormUrlEncoded
    Call<User> getUser(@Field("id") String id, @HeaderMap Map<String, String> headerMap);


    Map<String,String> headerMap = new HashMap<>();
    headerMap.put("Accept-Charset", "utf-8");
    headerMap.put("Accept", "text/plain");
    UserService#getUser("123", headerMap);

2.注解@Part@PartMap

注解@Part

    @POST("/users/update")
    @Multipart
    Call<ResponseBody> updateUserPortrait(@Part("id") int id, @Part(value = "image", encoding = "8-bit") RequestBody image);

注解@PartMap

    @POST("/users/upload/feedback")
    @Multipart
    Call<ResponseBody> uploadUserFeedback( @Part("file") RequestBody file, @PartMap Map<String, RequestBody> params);


    Map<String, RequestBody> partMap = new HashMap<>();
    partMap.put("deviceInfo", RequestBody.create(MediaType.parse("text/plain"), "deviceInfo"));
    partMap.put("content", RequestBody.create(MediaType.parse("text/plain"), "content"));
    File file = new File("test.png");
    RequestBody requestBody = RequestBody.create(MultipartBody.FORM, file);
    UserService#uploadUserFeedback(requestBody, partMap);

3.注解@Path

    @GET("/users/{name}")
    Call<List<User>> getUserByName(@Path("name") String name);

4.注解@Query@QueryMap

注解@Query

    @GET("/users/query/friends")
    Call<List<User>> getUserFriends(@Query("page") int page);

注解@QueryMap

    @GET("/users/query/friends/filter")
    Call<List<User>> getUserFriends(@QueryMap Map<String, String> params);

     Map<String, String> queryMap = new HashMap<>();
     queryMap.put("age", "23");
     queryMap.put("sex", "male");
     UserService#.getUserFriends(queryMap);

5.注解@QueryName

    @GET("/users/query/friends/filter")
    Call<List<User>> getUserFriendsByFileter(@QueryName String filter);

6.注解@Url

    @GET("/users/config")
    Call<ResponseBody> getUserConfig(@Url String url);

注解解析过程

注解的解析是在类ServiceMethod中的静态内部类Builder中的build方法中进行的,通过调用parseMethodAnnotation(annotation)方法解析方法注解,调用parseParameter方法解析参数注解。

关于具体解析步骤,我们先使用Retrofit创建一个服务接口:

        Retrofit retrofit = new Retrofit.Builder().baseUrl("https://api.example.com/").build();
        UserService service = retrofit.create(UserService.class);

服务接口的创建使用了 Java 动态代理,具体看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, @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);
          }
        });
  }

根据上一篇Java 动态代理 Proxy.newProxyInstance(),接口InvocationHandler中的回调方法invoke是在调用目标接口(UserService)的方法时候调用的,invoke方法的返回值就是目标接口(UserService)方法的返回值。如上面的例子注解@GET

    @GET("/users/query/all")
    Call<List<User>> getUsers();

    Call<List<User>> call =  service.getUsers();
    call.enqueue(callback);

在 UserService 调用getUsers()方法的时候,就会执行invoke方法,并返回Call<List<User>>

invoke方法中,首先判断执行的方法是不是在类Object中声明的方法:

            if (method.getDeclaringClass() == Object.class) {
              return method.invoke(this, args);
            }

hashCodeequals等都是Object里面的方法。其次是判断执行的方法是不是目标接口(UserService)中声明的默认方法(Java 8.0版本以上,可以在接口中声明默认方法):

            if (platform.isDefaultMethod(method)) {
              return platform.invokeDefaultMethod(method, service, proxy, args);
            }

Platform 主要区分是Android 还是 Java。最后创建ServiceMethodOkHttpCall对象,并通过CallAdapter对象中的adapt方法返回相应的接口方法返回值:

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

loadServiceMethod 方法中,先查看该method对应的ServiceMethod是否已经存在,如果存在就直接返回,如果没有就创建一个并放到缓存中:

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

ServiceMethod中的静态内部类Builder的构造方法接受RetrofitMethod对象,最后调用build()方法创建ServiceMethod对象:

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

    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];
        if (parameterAnnotations == null) {
          throw parameterError(p, "No Retrofit annotation found.");
        }

        parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations); //解析方法参数注解
      }

      ...省略部分代码

      return new ServiceMethod<>(this);
    }

parseMethodAnnotation(annotation) 方法里面主要解析方法注解:

    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);
      } else if (annotation instanceof Multipart) {
        if (isFormEncoded) {
          throw methodError("Only one encoding annotation is allowed.");
        }
        isMultipart = true;
      } else if (annotation instanceof FormUrlEncoded) {
        if (isMultipart) {
          throw methodError("Only one encoding annotation is allowed.");
        }
        isFormEncoded = true;
      }
    }

parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations) 方法里面主要解析方法参数注解:

    private ParameterHandler<?> parseParameter(
        int p, Type parameterType, Annotation[] annotations) {
      ParameterHandler<?> result = null;
      for (Annotation annotation : annotations) {
        ParameterHandler<?> annotationAction = parseParameterAnnotation(
            p, parameterType, annotations, annotation);

        if (annotationAction == null) {
          continue;
        }

        if (result != null) {
          throw parameterError(p, "Multiple Retrofit annotations found, only one allowed.");
        }

        result = annotationAction;
      }

      if (result == null) {
        throw parameterError(p, "No Retrofit annotation found.");
      }

      return result;
    }

    private ParameterHandler<?> parseParameterAnnotation(
        int p, Type type, Annotation[] annotations, Annotation annotation) {
      if (annotation instanceof Url) {
        ...省略部分代码

      } else if (annotation instanceof Path) {
        ...省略部分代码

      } else if (annotation instanceof QueryName) {
        ...省略部分代码
        
      } else if (annotation instanceof QueryMap) {
        ...省略部分代码
        
      } else if (annotation instanceof Header) {
        ...省略部分代码
       
      } else if (annotation instanceof HeaderMap) {
        ...省略部分代码
       
      } else if (annotation instanceof Field) {
        ...省略部分代码
         
      } else if (annotation instanceof FieldMap) {
        ...省略部分代码
        
      } else if (annotation instanceof Part) {
        ...省略部分代码
      
      } else if (annotation instanceof PartMap) {
        ...省略部分代码
        
      } else if (annotation instanceof Body) {
        ...省略部分代码
        
      }
    }

注解具体解析细节这里不再分析介绍,有兴趣可以仔细阅读这部分代码。


如何自定义接口方法注解

自定义接口方法注解有一个关键的问题就是:如何去解析以及在什么时候解析这个方法注解?

从上面注解解析过程,知道注解的解析是在类ServiceMethod中的静态内部类Builder中的build方法中进行的。再看在build方法中的第一行代码:

    callAdapter = createCallAdapter(); //创建一个 CallAdapter 对象

createCallAdapter() 方法创建的是一个 CallAdapter<T, R> 对象。 CallAdapter<R, T> 是一个接口,它的作用是将请求结果响应类型R转换为自定义的类型TCallAdapter 对象 是通过CallAdapter.Factory类来创建的。它是在创建Retrofit对象的时候,通过Retrofit.Builder
#addCallAdapterFactory(Factory)方法将它添加进来的:

        Retrofit retrofit = Retrofit.Builder()
                .baseUrl("https://api.example.com/")
                .client(okHttpClient)
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .addConverterFactory(GsonConverterFactory.create(createGsonConverter()))
                .build();
        UserService service = retrofit.create(UserService.class);

再看createCallAdapter方法:

  private CallAdapter<T, R> 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 {
        //noinspection unchecked
        return (CallAdapter<T, R>) 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);
      }
    }

在方法的最后,通过RetrofitcallAdapter方法返回一个CallAdapter<T, R>对象,这个callAdapter方法接受两个参数,它们分别是方法返回值类型Type returnType = method.getGenericReturnType(); 和 方法注解Annotation[] annotations = method.getAnnotations();如果要自定义注解,就要想办法拿到annotations这个参数。

再来看callAdapter方法:

  final List<CallAdapter.Factory> adapterFactories;
   
  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 = adapterFactories.indexOf(skipPast) + 1;//默认会添加一个`CallAdapter<?, ?>`对象
    for (int i = start, count = adapterFactories.size(); i < count; i++) {
      CallAdapter<?, ?> adapter = adapterFactories.get(i).get(returnType, annotations, this);
      if (adapter != null) {
        return adapter;
      }
    }

    ...省略部分代码
  }

nextCallAdapter方法中,通过遍历adapterFactories中的CallAdapter.Factoryget 方法得到CallAdapter<?, ?>对象:

 public abstract @Nullable CallAdapter<?, ?> get(Type returnType, Annotation[] annotations,
        Retrofit retrofit);

get 方法中有三个参数TypeAnnotation[]Retrofit对象,其中Annotation[]对像就是接口方法的方法注解,如果有自定义方法注解,那么这个自定义方法注解就在里面。所以自定义注解就是要实现CallAdapter.Factory接口,然后通过Retrofit.Builder
#addCallAdapterFactory(Factory)方法将它添加进来。

下面看下Retrofit框架提供的默认CallAdapter.Factory,它是在在创建Retrofit对象的时候,通过Retrofit.Builder#build方法添加的

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

再看下 platformdefaultCallAdapterFactory方法:

  CallAdapter.Factory defaultCallAdapterFactory(@Nullable Executor callbackExecutor) {
    if (callbackExecutor != null) {
      return new ExecutorCallAdapterFactory(callbackExecutor);
    }
    return DefaultCallAdapterFactory.INSTANCE;
  }

如果有设置Retrofit.Builder#build.callbackExecutor(Executor executor),就使用ExecutorCallAdapterFactory,否则就使用DefaultCallAdapterFactory

下面看看类DefaultCallAdapterFactory

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

根据上面代码,就知道如何创建一个CallAdapter.Factory,并在它的get方法中解析自定义的接口方法注解。

例子

自定义一个标识这个请求接口是不是需要加密的注解:

/**
 * 加密注解,在接口方法中添加了这个注解,就代表着这个请求接口传输的数据会进行加密
 */
public @interface Encryption {
    
}

自定义一个CallAdapter.Factory

public class EncryptionCallAdapterFactory extends CallAdapter.Factory {
    private static final CallAdapter.Factory INSTANCE = new EncryptionCallAdapterFactory();

    public static CallAdapter.Factory create() {
        return INSTANCE;
    }

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

        String baseUrl = retrofit.baseUrl().url().toString();
        String path = null;//请求路径
        boolean hasEncryption = false;//是否有加密注解Encryption
        for (Annotation annotation : annotations) {
            if (annotation instanceof GET) {
                path = ((GET) annotation).value();
            } else if (annotation instanceof POST) {
                path = ((POST) annotation).value();
            } else if (annotation instanceof Encryption) {
                hasEncryption = true;
            }
        }
        if (path != null && hasEncryption) {
            //do something
        }

        return new CallAdapter<Object, Call<?>>() {
            @Override
            public Type responseType() {
                return responseType;
            }

            @Override
            public Call<Object> adapt(Call<Object> call) {
                return call;
            }
        };
    }
}

get方法中,拿到自定义的接口方法注解,然后进行解析。上面对请求接口加密,在get方法中只能做到标识下这个请求接口的请求实体需要加密,接下来还需要在OKHttpClient 的 拦截器Interceptor中对请求实体进行加密处理。

添加EncryptionCallAdapterFactory

        Retrofit retrofit = Retrofit.Builder()
                .baseUrl("https://api.example.com/")
                .client(okHttpClient)
                .addCallAdapterFactory(EncryptionCallAdapterFactory.create())
                .addConverterFactory(GsonConverterFactory.create(createGsonConverter()))
                .build();
        UserService service = retrofit.create(UserService.class);

总结,自定义接口方法注解主要是利用了CallAdapter.Factory,并在它的get方法中拿到方法注解进行解析。注意,这里并不能自定义接口方法参数注解。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值