Retrofit2:如何正确检查response.body()的有效性?

时间:2017-08-01 09:58:04

标签: java android retrofit retrofit2

retrofit2.retrofit:2.3.0版以来,即使在NullPointerException之前检查正文时,我response.body()也会收到null警告:

Image

Method invocation 'getCar()' may produce 'java.lang.NullPointerException'

2.3.0的更改日志中,有一个与空检查相关的条目:

  

Retrofit现在使用@Nullable来注释所有可能为空的值。 [...]我们使用@ParametersAreNonnullByDefault,除非明确注释@Nullable,否则所有参数和返回类型都不会为空。

这是预期的行为?在我看来response.body()应该是不可变的,因此我的图片1中的检查不应该显示警告。

这不是关于NullPointerExceptions的问题 - 它是关于如何正确处理Retrofit2的响应的方式。为了没有警告,我必须做这样的事情:

if(response != null) {
    CarResponseBody body = response.body();
    if (body != null && body.getCar() != null){
        CarResponse car = body.getCar();
    }
}

很多代码只是检查是否有有效的响应......

3 个答案:

答案 0 :(得分:16)

  

这是预期的行为吗?

如果您检查Response<T>的JavaDoc,您可以阅读

  

@Nullable public T body()   成功响应的反序列化响应主体   Javadoc: Response

根据建议,如果回复成功,body()将不会是null。要检查是否成功,您需要isSuccessful()

  

如果code()在[200..300)范围内,则返回true。

因此,@Nullable是一个有效的选择,因为在任何不成功的情况下,响应可以是null,例如没有网络,无效请求或其他错误。

IDE中的提示是一个lint警告,它会检查源代码是否存在常见错误或错误的可能来源。

这是为什么 body()可能是null以及为什么lint首先将此报告为警告。

  

在我的观点中,response.body()应该是不可变的,所以我在图1中的检查不应该显示警告。

理论上你是对的。你和我都知道body()不可变,但问题是这个 lint 检查无法知道。

T res1 = response.body(); // could be null
T res2 = response.body(); // ...maybe still null?

Lint是一个静态源和字节码分析器,有助于防止常见的错误和错误,其中一个lint检查试图阻止NPE。如果您注释方法@Nullable,则所有检查都知道返回的值可能null,如果您尝试操作调用结果,它将发出警告直接

// does response return the same value twice? who knows?
response.body() != null && response.body().getCar() != null

处理响应的方式实际上是摆脱lint警告的唯一方法 - 除了抑制或禁用它。

通过将其分配给局部变量,您可以确保某个值在某个时刻不是null并且将来不会变为null,并且lint也能够看到了。

CarResponseBody body = response.body(); // assign body() to a local variable
if (body != null && body.getCar() != null) { // variable is not null
    CarResponse car = body.getCar(); // safe to access body, car is also not null
    // everything is fine!
}
  

是预期的行为?

是。 @Nullable是提示方法可能返回null的好方法,如果您在某些路径上返回null,也应该在自己的代码中使用它,因为 lint可以警告可能的NullPointerExceptions。

如果方法可能会返回null,则必须将其分配给本地字段并检查字段中的null值,否则您可能会更改该值。

Car car = response.getBody(); // car could be null
if(car != null) {
  // car will never be null
}

不同的选项/方法

我看到你还似乎还将你的响应对象包装在一个附加层中。

CarResponseBody body = response.body();
Car car = body.getCar()

如果要从代码中删除复杂性,应该先了解如何在早期阶段删除此包装*ResponseBody。您可以通过注册自己的转换器并在那里添加其他处理来实现。您可以在此Retrofit talk by Jake Wharton

上查看有关此内容的更多信息

另一种完全不同的方法是将RxJava与Retrofit一起使用,这样就无需自行检查响应。您将获得成功或错误,您可以处理Rx方式。

答案 1 :(得分:1)

我通常在WhateverResponse类中创建一个isValid()或hasResults()方法

boolean isValid(){
return getCar() != null && getWheel() != null && somethingElse
}

使用

if(response.isValid()){
//do your other if's
}

不需要检查空响应。 errorBody()将不为null或在此之前将调用retrofit2.Callback onFailure()方法

答案 2 :(得分:0)

试试这个

export function updateSettings(id, item) {
  return dispatch => {
    dispatch({ type: 'SETTINGS_IS_LOADING' })
    console.log(dispatch)
    console.log('FÖRE', item)
    axios
      .put(`${settings.hostname}/locks/${id}`, item)
      .then(() => {
        console.log(item)
        dispatch({
          type: 'SETTINGS_UPDATED',
          payload: {
            item,
            id
          }
        })
        console.log('EFTER', item) // It's still the same
      })
      .catch(err => console.log(err))
  }
}