Retrofit让我们更优雅的发送HTTP请求

424 阅读4分钟

Retrofit

一、背景

Retrofit 可以将Http请求转换为Java 接口,这样我们在调用Http接口请求就跟调RPC接口一样方便。

eg:

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

二、使用方法:

QuickStart

  1. 引入依赖

    <dependency>
      <groupId>com.squareup.retrofit2</groupId>
      <artifactId>retrofit</artifactId>
      <version>2.9.0</version>
    </dependency>
    
  2. 创建Interface

    public interface GitHubService {
      @GET("users/{user}/repos")
      Call<List<Repo>> listRepos(@Path("user") String user);
    }
    
  3. Retrofit 生成实例对象

    Retrofit retrofit = new Retrofit.Builder()
        .baseUrl("https://api.github.com/")
        .build();
    
    GitHubService service = retrofit.create(GitHubService.class);
    
  4. 调用

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

API 介绍

Retrofit 给我们提供了非常丰富的注解,本小结主要是从使用层面介绍各个注解的用法,以及使用场景

Retrofit2注解.png

上图::左半部分是用于发送请求的注解(对应HTTP/1.1 协议规定的HTTP请求的方法),接口方法上必有其中注解之一,右半部分是在特定Http请求场景下使用的修饰增强方法——配合左半部分一起使用。

@GET

作用:发送GET 请求

使用范围:Method

使用案例:

@GET("users/list")

// 还可以指定查询参数
@GET("users/list?sort=desc")

@Path

作用:有时候我们在请求是需要将参数作为请求路径的一部分时使用。

使用范围:Parameter

使用案例:

@GET("group/{id}/users")
Call<List<User>> groupList(@Path("id") int groupId);

@Query

作用:动态指定Url参数

使用范围:Parameter

使用案例:

@GET("group/{id}/users")
Call<List<User>> groupList(@Path("id") int groupId, @Query("sort") String sort);

@QueryMap

作用:动态指定Url参数

使用范围:Parameter

使用案例:

@GET("group/{id}/users")
Call<List<User>> groupList(@Path("id") int groupId, @QueryMap Map<String, String> options);

@POST、@Body

作用:@POST-发送POST请求;@Body - 讲参数转换为RequestBody 体进行传输

使用范围:@POST-Method;@Body-Parameter

使用案例:

@POST("users/new")
Call<User> createUser(@Body User user);

注意: @Body 修饰的对象在经过传输之前会使用Retrofit 指定的Converter进行转换,如果不指定Converter 智能转换RequestBody 对象

@FormUrlEncoded、@Field

作用:我们都知道Http的Post 请求,数据必须放在消息头(entity-body)中,但是协议并没有规定编码格式。服务端解析数据是根于请求头的Content-Type 标记编码格式来进行解析。@FormUrlEncoded 对应 (Content-Type: application/x-www-form-urlencoded;charset=utf-8)

使用范围:@FormUrlEncoded-Method;@@Field-Parameter

使用案例:

@FormUrlEncoded
@POST("user/edit")
Call<User> updateUser(@Field("first_name") String first, @Field("last_name") String last);

对应Post伪代码:

POST http://www.example.com HTTP/1.1
Content-Type: application/x-www-form-urlencoded;charset=utf-8

title=test&sub%5B%5D=1&sub%5B%5D=2&sub%5B%5D=3

@Multipart、@Part、@PUT

作用:对应Http的Post 请求上传文件

使用范围:@Multipart-Method;@Part-Parameter;@PUT-Method

使用案例:

@Multipart
@PUT("user/photo")
Call<User> updateUser(@Part("photo") RequestBody photo, @Part("description") RequestBody description);

@Headers

作用:静态指定请求头

使用范围:Method

使用案例:

@Headers({
    "Accept: application/vnd.github.v3.full+json",
    "User-Agent: Retrofit-Sample-App"
})
@GET("users/{username}")
Call<User> getUser(@Path("username") String username);

注意:所有的Headers不会被重写,只是原封不动的加进请求中

@Header、@HeaderMap

作用:动态指定请求头

使用范围:Parameter

使用案例:

@GET("user")
Call<User> getUser(@Header("Authorization") String authorization)

@GET("user")
Call<User> getUser(@HeaderMap Map<String, String> headers)

Retrofit 配置

CONVERTER

默认情况下Retrofit 只能反序列化http请求Body。(翻译成大白话就是@Body 只能修饰RequestBody)

但是Retrofit 给我们提供了其他类型的Converter 供我们使用,用于支持其他类型的转换

  • Gsoncom.squareup.retrofit2:converter-gson
  • Jacksoncom.squareup.retrofit2:converter-jackson
  • Moshicom.squareup.retrofit2:converter-moshi
  • Protobufcom.squareup.retrofit2:converter-protobuf
  • Wirecom.squareup.retrofit2:converter-wire
  • Simple XMLcom.squareup.retrofit2:converter-simplexml
  • JAXBcom.squareup.retrofit2:converter-jaxb
  • Scalars (primitives, boxed, and String): com.squareup.retrofit2:converter-scalars

使用方式:

假设我们现在指定Gson序列化

Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com/")
    .addConverterFactory(GsonConverterFactory.create()) //指定
    .build();

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

自定义CONVERTER

新建一个类继承Converter.Factory 类型,覆写其方法

abstract class Factory {
    /**
     * Returns a {@link Converter} for converting an HTTP response body to {@code type}, or null if
     * {@code type} cannot be handled by this factory. This is used to create converters for
     * response types such as {@code SimpleResponse} from a {@code Call<SimpleResponse>}
     * declaration.
     */
    public @Nullable Converter<ResponseBody, ?> responseBodyConverter(Type type,
        Annotation[] annotations, Retrofit retrofit) {
      return null;
    }

    /**
     * Returns a {@link Converter} for converting {@code type} to an HTTP request body, or null if
     * {@code type} cannot be handled by this factory. This is used to create converters for types
     * specified by {@link Body @Body}, {@link Part @Part}, and {@link PartMap @PartMap}
     * values.
     */
    public @Nullable Converter<?, RequestBody> requestBodyConverter(Type type,
        Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
      return null;
    }

    /**
     * Returns a {@link Converter} for converting {@code type} to a {@link String}, or null if
     * {@code type} cannot be handled by this factory. This is used to create converters for types
     * specified by {@link Field @Field}, {@link FieldMap @FieldMap} values,
     * {@link Header @Header}, {@link HeaderMap @HeaderMap}, {@link Path @Path},
     * {@link Query @Query}, and {@link QueryMap @QueryMap} values.
     */
    public @Nullable Converter<?, String> stringConverter(Type type, Annotation[] annotations,
        Retrofit retrofit) {
      return null;
    }

    /**
     * Extract the upper bound of the generic parameter at {@code index} from {@code type}. For
     * example, index 1 of {@code Map<String, ? extends Runnable>} returns {@code Runnable}.
     */
    protected static Type getParameterUpperBound(int index, ParameterizedType type) {
      return Utils.getParameterUpperBound(index, type);
    }

    /**
     * Extract the raw class type from {@code type}. For example, the type representing
     * {@code List<? extends Runnable>} returns {@code List.class}.
     */
    protected static Class<?> getRawType(Type type) {
      return Utils.getRawType(type);
    }
  }

注意事项

  1. Retrofit 配置 baseUrl 必须以“/”结束;

  2. Retrofit 配置 baseUrl 如果域名后配置路径,实际发请求是截取到域名结束部分;

    eg:

    如果配置 baseUrl : www.baidu.com/pic/xxx/eee…

    实际使用是 发送请求的 baseUrl :www.baidu.com/

  3. POST 、GET 等方法不允许赋值为空,如果不赋值可以使用 ”.“ 或者”/“ 代替;

    eg: @POST(”.“) 或者@POST(”/“)