安卓轮子之Retrofit的使用

表单请求

1. 普通表单

@FormUrlEncoded:表示请求体是一个 Form 表单

Content-Type:application/x-www-form-urlencoded

采用 @Field 注解:

@FormUrlEncoded
@POST("login/login")
Call<ResponseBody> login(@Field("mail") String mail, @Field("password") String password);

采用 @FieldMap 注解:

@FormUrlEncoded
@POST("login/login")
Call<ResponseBody> login(@FieldMap Map<String, String> map);

2. Mutipart 表单

@Multipart:表示请求体是一个支持文件上传的 Form 表单

Content-Type:multipart/form-data

@Part 注解支持的类型有:

  • @Part MultipartBody.Part
  • @Part("name") RequestBody name
  • @PartMap Map<String, RequestBody>

除了 okhttp3.MultipartBody.Part 之外,其他类型都必须带上表单字段,因为 okhttp3.MultipartBody.Part 中已经包含了表单字段的信息。

@Multipart
@POST("FileServlet?style=UploadFiles")
Call<ResponseBody> uploadFiles(@Part MultipartBody.Part filePart);
// @Part("name") RequestBody requestBody

上传示例:

File file = new File("D:\\test.txt");
String partName = "file";
String fileName = file.getName();
final MediaType MEDIA_OBJECT_STREAM =
        MediaType.parse("application/octet-stream; charset=UTF-8");
RequestBody fileBody = RequestBody.create(MEDIA_OBJECT_STREAM, file);
MultipartBody.Part filePart = MultipartBody.Part.createFormData(partName, fileName, fileBody);
Call<ResponseBody> uploadFile = inteCastService.uploadFiles(filePart);

GET 方式请求 JSON 数据并解析得到 GSON 对象

// Repo 是由 Android Studio 插件 GsonFormat 从 JSON 字符串自动生成的类

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

private void connect() {
    Retrofit retrofit = new Retrofit.Builder()
            .baseUrl("https://api.github.com/")
            .addConverterFactory(GsonConverterFactory.create())
            .build();
    GithubService githubService = retrofit.create(GithubService.class);
    Call<List<Repo>> octocat = githubService.listRepos("xiangaoole");

    octocat.enqueue(mCallback);
}

private Callback<List<Repo>> mCallback = new Callback<List<Repo>>() {
    @Override
    public void onResponse(Call<List<Repo>> call, Response<List<Repo>> response) {
        List<Repo> body = response.body();
        for (int i = 0; i < body.size(); i++) {
            Repo repo = body.get(i);
            System.out.println(repo.getName());
        }
    }

    @Override
    public void onFailure(Call<List<Repo>> call, Throwable t) {

    }
};

自定义 Converter

Converter 类的作用是从 HTTP 响应体中解析生成特定类型的对象,反之亦然。

public interface Converter<F, T> {
  // 实现从 F(rom)到 T(o)的转换
  T convert(F value) throws IOException;

  // 提供 Converter 的工厂类
  abstract class Factory {
    // 返回一个将 HTTP 响应体解析为 type 类型对象的 Converter。
    public @Nullable Converter<ResponseBody, ?> responseBodyConverter(Type type,
        Annotation[] annotations, Retrofit retrofit) {
      return null;
    }

    // 返回一个将 type 类型对象转化为 HTTP 请求体的 Converter。
    public @Nullable Converter<?, RequestBody> requestBodyConverter(Type type,
        Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
      return null;
    }

    // 返回一个将 type 类型对象转化为 String 的 Converter。
    public @Nullable Converter<?, String> stringConverter(Type type, Annotation[] annotations,
        Retrofit retrofit) {
      return null;
    }

    // 提取出 type 类型中第 index 个参数的泛型上界
    // 比如 Map<String, ? extends Runnable> 的第 1 个参数的泛型上界是 Runnable
    protected static Type getParameterUpperBound(int index, ParameterizedType type) {
      return Utils.getParameterUpperBound(index, type);
    }

    // 提取出 type 类型的原始类
    // 比如 List<? extends Runnable> 返回 List.class
    protected static Class<?> getRawType(Type type) {
      return Utils.getRawType(type);
    }
  }
}
  • responseBodyConverter :主要用于处理响应体
  • requestBodyConverter :主要用于处理 Part、PartMap、Body 注解
  • stringConverter :主要用于处理 Field、FieldMap、Header、Path、Query、QueryMap 注解(默认调用 toString 方法)

下面我们演示一个自定义 ResponseBody 到 String 的自定义 Converter。

第一步:实现 Converter

public class StringConverter implements Converter<ResponseBody, String> {
    public static final StringConverter INSTANCE = new StringConverter();

    @Override
    public String convert(@Nullable ResponseBody value) throws IOException {
        return value != null ? value.string() : null;
    }
}

第二步:创建 Converter.Factory 子类

我们通过 Factory 的 create 方法来获得 Factory 实例,因为我们只处理 Call ,所以只用重写 responseBodyConverter 方法。

public static class StringConverterFactory extends Converter.Factory {
    public static final StringConverterFactory INSTANCE = new StringConverterFactory();

    public static StringConverterFactory create() {
        return INSTANCE;
    }

    @Nullable
    @Override
    public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
        if (type == String.class)
            return StringConverter.INSTANCE;
        else
            return null;
    }
}

第三步:注册 Converter.Factory

addConverterFactory 是有先后顺序的,如果有多个 ConverterFactory 都支持同一种类型,那么只有第一个才会被使用。但是要注意的是, GsonConverterFactory 支持所有类型,所以要放在最后。

Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("http://localhost:8080/InteCAST/")
                .addConverterFactory(StringConverter.StringConverterFactory.create())
                .build();

自定义 CallAdapter

Converter 的作用是将 Call 转化为 Call,而CallAdapter 则可以将 Call 转化为其他类型。比如在 Retrofit 中使用 RxJava 时,可以将 Call 转化为 Observable,只需要向 Retrofit 中注册相应的 CallAdapterFactory。

Retrofit retrofit = new Retrofit.Builder()
      .baseUrl("http://localhost:8080/")
      .addConverterFactory(GsonConverterFactory.create())
      .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
      // 针对rxjava2.x
      .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
      .build();

CallAdapter 接口的定义如下:

public interface CallAdapter<R, T> {
  // 返回将 Call<Repo> 中的 Repo
  // 这个 T 会作为 Converter.Factory.responseBodyConverter 的第一个参数
  Type responseType();

  // 返回 Call<R> 的委托实例 T
  /* 比如下面的方法将 Call<R> 转化为 Async<R>对象,可以调用 call 方法来执行
  @Override
  public <R> Async<R> adapt(final Call<R> call) {
    return Async.create(new Callable<Response<R>>() {
      @Override
      public Response<R> call() throws Exception {
        return call.execute();
      }
    });
  }
  */
  T adapt(Call<R> call);

  // 提供 CallAdapter 的工厂类
  abstract class Factory {
    // 返回一个能返回 returnType 的 CallAdapter
    public abstract @Nullable CallAdapter<?, ?> get(Type returnType, Annotation[] annotations,
        Retrofit retrofit);

    protected static Type getParameterUpperBound(int index, ParameterizedType type) {
      return Utils.getParameterUpperBound(index, type);
    }

    protected static Class<?> getRawType(Type type) {
      return Utils.getRawType(type);
    }
  }
}

下面演示自定义一个 MyCallAdapter 将 Call 转化为 MyCall。

第一步:定义 MyCall

这里的 MyCall 采用代理模式,封装了一个 Call 实例,提供一个同步获取数据的 get 方法。

public class MyCall<R> {
    public final Call<R> call;

    public MyCall(Call<R> call) {
        this.call = call;
    }

    public R get() throws IOException {
        return call.execute().body();
    }
}

第二步:实现 CallAdapter

public class MyCallAdapter implements CallAdapter<R, MyCall<R>> {

    private final Type responseType;

    MyCallAdapter(Type responseType) {
        this.responseType = responseType;
    }

    @Override
    public Type responseType() {
        return responseType;
    }

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

}

第三步:定义 CallAdapter.Factory 子类

重写 get 方法,MyCall 的 raw type 为 MyCall.class,类型匹配之后,getParameterUpperBound 得到 Call 的第 0 个参数的泛型上界是 String,然后新建 MyCallAdapter 返回。

public class MyCallAdapter implements CallAdapter<R, MyCall<R>> {

    ...

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

        @Nullable
        @Override
        public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
            Class<?> rawType = getRawType(returnType);
            if (rawType == MyCall.class && returnType instanceof ParameterizedType) {
                Type callReturnType = getParameterUpperBound(0, (ParameterizedType) returnType);
                return new MyCallAdapter(callReturnType);
            }
            return null;
        }
    }

}

最后一步:注册 CallAdapter.Factory

这里接着自定义 StringConverter 的内容,我们实现了 Call -> Call -> MyCall 的转化。

Retrofit retrofit = new Retrofit.Builder()
        .baseUrl("http://localhost:8080/InteCAST/")
        .addConverterFactory(StringConverter.StringConverterFactory.create())
        .addConverterFactory(GsonConverterFactory.create())
        .addCallAdapterFactory(MyCallAdapter.Factory.INSTANCE)
        .build();

Retrofit 中 Url 组合规则

baseUrl 和 url 有关的注解中的值 最终结果
http://localhost:8080/path/ /post http://localhost:8080/post
http://localhost:8080/path/ post http://localhost:8080/path/post
http://localhost:8080/path/ http://baidu.com/ http://baidu.com/

从上面可以看出如下规则:

  • 注解中 url 为完整的 url,则以此为最终的 url
  • 注解中 url 不完整,且以 / 开头,则最终请求的 url 为 baseUrl 的主机部分 + 有关的注解中的值
  • 注解中 url 不完整,且不以 / 开头,则最终请求的 url 为 baseUrl + 有关的注解中的值

参考资料

  1. 你真的会用Retrofit2吗?Retrofit2完全教程

猜你喜欢

转载自blog.csdn.net/weixin_40255793/article/details/81589939