Retrofit统一处理响应结果以及注意事项

项目上使用Retrofit作为网络请求框架,最近有个需要对响应结果进行处理的需求,由于让各个模块自己处理响应结果不利于维护,所以需要统一处理,在处理的过程中遇到些问题,在此记录下来。

1.构建Retrofit
// 构建Retrofit代码如下:
OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder();
clientBuilder.addNetworkInterceptor(new NetworkInterceptor());
clientBuilder.connectTimeout(6, TimeUnit.SECONDS);
clientBuilder.writeTimeout(6, TimeUnit.SECONDS);
clientBuilder.readTimeout(6, TimeUnit.SECONDS);

Retrofit retrofit = new Retrofit.Builder()
    .client(clientBuilder.build())
    .addConverterFactory(GsonConverterFactory.create())
    .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
    .baseUrl("https:/xxxxxx")
    .build();

从构建Retrofit代码中可以看到GsonConverterFactory这个类就是把响应结果转变成我们想要的结果的类,所以如果需要统一处理响应结果的话,应该可以在这里面添加处理。

2.解析Converter
// GsonConverterFactory
public final class GsonConverterFactory extends Converter.Factory {
  public static GsonConverterFactory create() {
    return create(new Gson());
  }

  @SuppressWarnings("ConstantConditions")
  public static GsonConverterFactory create(Gson gson) {
    if (gson == null) throw new NullPointerException("gson == null");
    return new GsonConverterFactory(gson);
  }

  private final Gson gson;

  private GsonConverterFactory(Gson gson) {
    this.gson = gson;
  }

  @Override
  public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
      Retrofit retrofit) {
    TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
    return new GsonResponseBodyConverter<>(gson, adapter);
  }

  @Override
  public Converter<?, RequestBody> requestBodyConverter(Type type,
      Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
    TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
    return new GsonRequestBodyConverter<>(gson, adapter);
  }
}

GsonConverterFactory类有两个重要的方法responseBodyConverter()requestBodyConverter(),从这两个方法名称就可以看出,前者是用来处理响应的,后者是用来处理请求的。由于我们只需要处理响应结果,所以查看下GsonResponseBodyConverter代码。

// GsonResponseBodyConverter
final class GsonResponseBodyConverter<T> implements Converter<ResponseBody, T> {
  private final Gson gson;
  private final TypeAdapter<T> adapter;

  GsonResponseBodyConverter(Gson gson, TypeAdapter<T> adapter) {
    this.gson = gson;
    this.adapter = adapter;
  }

  @Override public T convert(ResponseBody value) throws IOException {
    JsonReader jsonReader = gson.newJsonReader(value.charStream());
    try {
      T result = adapter.read(jsonReader);
      if (jsonReader.peek() != JsonToken.END_DOCUMENT) {
        throw new JsonIOException("JSON document was not fully consumed.");
      }
      return result;
    } finally {
      value.close();
    }
  }
}

GsonResonseBodyConverter类除了构造方法,就只有一个convert()方法,从convert()方法的实现可以看出这里就是处理响应的地方,所以如果我们需要统一处理响应结果,在这个方法里添加即可。

3.重写Converter

想要重写Converter,只能通过复制代码然后新建类的方式来重写。

// 重写GsonConverterFactory
public final class CustomGsonConverterFactory extends Converter.Factory {
    public static CustomGsonConverterFactory create() {
        return create(new Gson());
    }

    @SuppressWarnings("ConstantConditions")
    public static CustomGsonConverterFactory create(Gson gson) {
        if (gson == null) throw new NullPointerException("gson == null");
        return new CustomGsonConverterFactory(gson);
    }

    private final Gson gson;

    private CustomGsonConverterFactory(Gson gson) {
        this.gson = gson;
    }

    @Override
    public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
                                                            Retrofit retrofit) {
        TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
        // 返回重写的GsonResponseConverter
        return new CustomGsonResponseBodyConverter<>(gson, adapter);
    }

    @Override
    public Converter<?, RequestBody> requestBodyConverter(Type type,
                                                          Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
        TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
        return new GsonRequestBodyConverter<>(gson, adapter);
    }
}

CustomGsonConverterFactoryresponseBodyConverter()方法中,我们返回了重写的GsonResponseConverter

// 重写GsonResponseConverter
final class CustomGsonResponseBodyConverter<T> implements Converter<ResponseBody, T> {
    private static final String TAG = "ResponseBodyConverter";
    private final Gson gson;
    private final TypeAdapter<T> adapter;

    CustomGsonResponseBodyConverter(Gson gson, TypeAdapter<T> adapter) {
        this.gson = gson;
        this.adapter = adapter;
    }

    @Override
    public T convert(ResponseBody value) throws IOException {
        String response = value.string();   // value只能读取一次,一次之后就会关闭,所以需要保存
        try {
            JSONObject jsonObject = new JSONObject(response);
            String code = jsonObject.getString("code");
            String description = jsonObject.getString("description");
            LogUtil.d(TAG, "convert: code = " + code + ", description = " + description);

            handleResponseCode(code);
        } catch (JSONException e) {
            e.printStackTrace();
        }

        MediaType contentType = value.contentType();
        Charset charset = contentType != null ? contentType.charset(UTF_8) : UTF_8;
        InputStream inputStream = new ByteArrayInputStream(response.getBytes());
        Reader reader = new InputStreamReader(inputStream, charset);
        JsonReader jsonReader = gson.newJsonReader(reader);
        try {
            T result = adapter.read(jsonReader);
            if (jsonReader.peek() != JsonToken.END_DOCUMENT) {
                throw new JsonIOException("JSON document was not fully consumed.");
            }
            return result;
        } finally {
            value.close();
        }
    }

    private void handleResponseCode(String code) {
        // todo 添加处理逻辑
    }
}

CustomGsonResponseBodyConverter中的convert()方法实现与GsonResponseBodyConverter中的convert()方法实现有很大不同,因为我们要通过传进来的参数valuestring()方法来获取完整的响应结果,但是通过value.string()获取响应结果后,会把该数据源给关闭,这时候如果还是按照之前的方式value.charStream()获取数据,是会出现报错closed的,所以需要先保存结果,然后再通过IO流的方式把结果传给JsonReader,这一点是特别需要注意的地方。

4.注意事项及总结

统一处理响应结果需要注意的地方就在GsonResponseBodyConverterconvert()方法里面和ResponseBody类的使用。

// ResponseBody部分实现代码
...
	 /**
   * Returns the response as a string decoded with the charset of the Content-Type header. If that
   * header is either absent or lacks a charset, this will attempt to decode the response body as
   * UTF-8.
   */
  public final String string() throws IOException {
    return new String(bytes(), charset().name());
  }
  
  public final byte[] bytes() throws IOException {
    long contentLength = contentLength();
    if (contentLength > Integer.MAX_VALUE) {
      throw new IOException("Cannot buffer entire body for content length: " + contentLength);
    }

    BufferedSource source = source();
    byte[] bytes;
    try {
      bytes = source.readByteArray();
    } finally {
      Util.closeQuietly(source);	// 注意这里调用了close
    }
    if (contentLength != -1 && contentLength != bytes.length) {
      throw new IOException("Content-Length and stream length disagree");
    }
    return bytes;
  }

  /**
   * Returns the response as a character stream decoded with the charset of the Content-Type header.
   * If that header is either absent or lacks a charset, this will attempt to decode the response
   * body as UTF-8.
   */
  public final Reader charStream() {
    Reader r = reader;
    return r != null ? r : (reader = new InputStreamReader(byteStream(), charset()));
  }

  public final InputStream byteStream() {
    return source().inputStream();
  }
...

// Util部分实现代码
...
    /**
   * Closes {@code closeable}, ignoring any checked exceptions. Does nothing if {@code closeable} is
   * null.
   */
  public static void closeQuietly(Closeable closeable) {
    if (closeable != null) {
      try {
        closeable.close();
      } catch (RuntimeException rethrown) {
        throw rethrown;
      } catch (Exception ignored) {
      }
    }
  }
...

ResponseBody代码中可以看出,string()方法和charStream()方法最终都需要调用到source()方法,但是如果先调用string()方法,在bytes()方法里面就会把source给关闭,导致后面调用charStream()方法时候会报closed

这个地方是统一处理响应结果最容易忽略、也是新手容易踩的坑。

总的来说,统一处理响应结果的流程就是重写GsonConverterFactory类和GsonResponseBodyConverter类,需要重写的两个方法是GsonConverterFactory类中的responseBodyConverter()方法和GsonResponseBodyConverter类中的convert()方法。需要注意的就是ResponseBody中的数据读取。

以上是使用Retrofit统一处理响应结果的过程和遇到的一些问题,有错误的地方欢迎指正,希望能够帮助到大家,感谢你的阅读!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Retrofit 可以与响应式编程框架 RxJava 结合使用,这样可以更加方便地进行异步操作和数据流处理。使用 Retrofit 和 RxJava 结合的步骤如下: 1. 添加依赖 在 build.gradle 文件中添加以下依赖: ```groovy implementation 'com.squareup.retrofit2:retrofit:2.9.0' implementation 'com.squareup.retrofit2:adapter-rxjava2:2.9.0' implementation 'io.reactivex.rxjava2:rxjava:2.2.21' implementation 'io.reactivex.rxjava2:rxandroid:2.1.1' ``` 2. 创建 Retrofit 实例 同样需要创建 Retrofit 实例,示例代码如下: ```java Retrofit retrofit = new Retrofit.Builder() .baseUrl("https://api.example.com/") .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .addConverterFactory(GsonConverterFactory.create()) .build(); ``` 需要注意的是,这里需要添加 RxJava2CallAdapterFactory 适配器。 3. 创建 API 接口 同样需要创建 API 接口,示例代码如下: ```java public interface ApiService { @GET("users") Observable<List<User>> getUsers(); } ``` 需要注意的是,这里返回的是一个 Observable 对象,而不是 Call 对象。 4. 调用 API 接口 使用 API 接口的实现调用 API 接口方法,示例代码如下: ```java ApiService service = retrofit.create(ApiService.class); service.getUsers() .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Observer<List<User>>() { @Override public void onSubscribe(Disposable d) { // TODO: 订阅开始 } @Override public void onNext(List<User> users) { // TODO: 处理返回结果 } @Override public void onError(Throwable e) { // TODO: 处理请求失败 } @Override public void onComplete() { // TODO: 订阅结束 } }); ``` 这里使用了 subscribeOn 和 observeOn 操作符,使得网络请求在后台线程中执行,而结果回调在主线程中执行。 通过这种方式,我们可以使用 Retrofit 和 RxJava 结合进行网络请求,将返回结果转换成数据流进行处理,实现更加方便和灵活的异步编程。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值