内容简介:今天在做网络接口的时候, 一个返回结果应该是根据错误信息, 进入代码可得:此处代码, 说明服务器返回的成功,
每日一问-Android-20181031
为什么 HTTP Code 204 会导致 Retrofit 出现 NullPointerException?
答:
今天在做网络接口的时候, 一个返回结果应该是 Map
的接口出现了异常:
java.lang.NullPointerException: The mapper function returned a null value. at io.reactivex.internal.functions.ObjectHelper.requireNonNull(ObjectHelper.java:39) at io.reactivex.internal.operators.observable.ObservableMap$MapObserver.onNext(ObservableMap.java:59) at retrofit2.adapter.rxjava2.CallExecuteObservable.subscribeActual(CallExecuteObservable.java:44) at io.reactivex.Observable.subscribe(Observable.java:12030) at io.reactivex.internal.operators.observable.ObservableMap.subscribeActual(ObservableMap.java:33) at io.reactivex.Observable.subscribe(Observable.java:12030) at io.reactivex.internal.operators.observable.ObservableSubscribeOn$SubscribeTask.run(ObservableSubscribeOn.java:96) at io.reactivex.Scheduler$DisposeTask.run(Scheduler.java:579) at io.reactivex.internal.schedulers.ScheduledRunnable.run(ScheduledRunnable.java:66) at io.reactivex.internal.schedulers.ScheduledRunnable.call(ScheduledRunnable.java:57) at java.util.concurrent.FutureTask.run(FutureTask.java:266)
根据错误信息, 进入代码可得:
static final class MapObserver<T, U> extends BasicFuseableObserver<T, U> { final Function<? super T, ? extends U> mapper; //省略代码 @Override public void onNext(T t) { if (done) { return; } if (sourceMode != NONE) { actual.onNext(null); return; } U v; try { v = ObjectHelper.requireNonNull(mapper.apply(t), "The mapper function returned a null value."); } catch (Throwable ex) { fail(ex); return; } actual.onNext(v); } //省略代码 }
此处代码, 说明服务器返回的成功, 已经进入 onNext 回调了, 但是其 value 是 null
, 我们在往上看调用信息, 在实际的 subscribeActual
方法中将 Response 进行传递.
//retrofit2.adapter.rxjava2.CallExecuteObservable#subscribeActual @Override protected void subscribeActual(Observer<? super Response<T>> observer) { // Since Call is a one-shot type, clone it for each new observer. Call<T> call = originalCall.clone(); CallDisposable disposable = new CallDisposable(call); observer.onSubscribe(disposable); boolean terminated = false; try { Response<T> response = call.execute(); if (!disposable.isDisposed()) { observer.onNext(response); } if (!disposable.isDisposed()) { terminated = true; observer.onComplete(); } } catch (Throwable t) { //省略代码 } }
这里的 Response 是 retrofit 内置的 Resonse 不是 Okhttp 里面的 Response, 部分代码如下:
// /** An HTTP response. */ public final class Response<T> { //省略代码 /** * Create a successful response from {@code rawResponse} with {@code body} as the deserialized * body. */ public static <T> Response<T> success(@Nullable T body, okhttp3.Response rawResponse) { checkNotNull(rawResponse, "rawResponse == null"); if (!rawResponse.isSuccessful()) { throw new IllegalArgumentException("rawResponse must be successful response"); } return new Response<>(rawResponse, body, null); } //省略代码 }
这个 Response 怎么来的呢?看 subscribeActual
方法这个代码块:
Response<T> response = call.execute(); if (!disposable.isDisposed()) { observer.onNext(response); }
是 Okhttp 的 Call 的执行结果, 这里的实现类是 OkHttpCall<T>
:
@Override public Response<T> execute() throws IOException { okhttp3.Call call; //省略代码 return parseResponse(call.execute()); }
Call 执行以后对结果进行解析处理:
Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException { ResponseBody rawBody = rawResponse.body(); // Remove the body's source (the only stateful object) so we can pass the response along. rawResponse = rawResponse.newBuilder() .body(new NoContentResponseBody(rawBody.contentType(), rawBody.contentLength())) .build(); int code = rawResponse.code(); if (code < 200 || code >= 300) { try { // Buffer the entire body to avoid future I/O. ResponseBody bufferedBody = Utils.buffer(rawBody); return Response.error(bufferedBody, rawResponse); } finally { rawBody.close(); } } if (code == 204 || code == 205) { rawBody.close(); return Response.success(null, rawResponse); } ExceptionCatchingRequestBody catchingBody = new ExceptionCatchingRequestBody(rawBody); try { T body = serviceMethod.toResponse(catchingBody); return Response.success(body, rawResponse); } catch (RuntimeException e) { // If the underlying source threw an exception, propagate that rather than indicating it was // a runtime exception. catchingBody.throwIfCaught(); throw e; } }
重点来了:在这个解析的代码中, 如果服务器返回的 HTTP Code 是 204 或者 205, 那么其 body 会置为 null, 所以会出现 NullPointException
那么, HTTP Code 204 是什么意思呢? 这里引用 Mozilla 的说明:
HTTP协议中 204 No Content 成功状态响应码表示目前请求成功,但客户端不需要更新其现有页面。204 响应默认是可以被缓存的。在响应中需要包含头信息 ETag。
其实就是, 请求服务器成功了, 但是没有数据返回给你.
实际的网络请求是什么样呢(接口已做马赛克处理)?
D/OkHttp: <-- 204 No Content https://xxxxxxxx (2241ms) D/OkHttp: Server: nginx D/OkHttp: Date: Wed, 31 Oct 2018 13:50:15 GMT D/OkHttp: Connection: keep-alive D/OkHttp: X-Application-Context: dating:publicCloud D/OkHttp: Access-Control-Allow-Headers: DNT,X-FROM-APP,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range D/OkHttp: Access-Control-Expose-Headers: DNT,X-FROM-APP,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range D/OkHttp: Access-Control-Allow-Origin: * D/OkHttp: Access-Control-Allow-Methods: GET,POST,HEAD,OPTIONS,PUT,DELETE D/OkHttp: Access-Control-Max-Age: 1728000 D/OkHttp: <-- END HTTP
从日志上上看,服务器确实返回了 204, 那么我们怎么处理这个异常呢?
从实际的现象来看, 这个异常只是打印了异常信息, 而不会引发程序的崩溃. 个人认为可以这样处理:
- 不做任何处理
- 将此 204 使用 Observable 的 map 转换分发到 onException 分支
如果服务器返回的数据格式类似下面这样:
{ "code" : 10000, "desc" : "success", "data" : "", }
那就在 结果返回时添加 Function 转换, 返回一个data 为 null 的 JavaBean.
操作过程:
1.将 rest service 的接口返回值改为: Observable<Response<BaseResult<Map<String, String>>>>
2.创建新的 Function类:
public class ResponseFun<T> implements Function<Response<BaseResult<T>>, BaseResult<T>> { @Override public BaseResult<T> apply(Response<BaseResult<T>> response) { if (response.isSuccessful()) { if (response.code() == HttpURLConnection.HTTP_NO_CONTENT || response.code() == HttpURLConnection.HTTP_RESET || response.body() == null) { BaseResult<T> result = new BaseResult<>(); result.setCode(10000); result.setDesc("success"); return result; } else { return response.body(); } } throw new HttpException(response); } }
3.调用处在处理数据的时候要做判 null 处理.
以上所述就是小编给大家介绍的《为什么 HTTP Code 204 会导致 Retrofit 出现 NullPointerException?》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- 微软修改开源代码,导致 Windows Defender 出现漏洞
- 值拷贝导致使用container/list出现的诡异问题分析
- 云计算技术的出现或导致IT运维体系产生重大变革
- JavaScript运算出现很多小数导致运算不精确的问题,用toFixed解决
- Oracle 10.2.0.4 sql关联查询语句中含有 connect by 导致报错出现ORA-00600
- sqlserver还原数据库的时候出现提示无法打开备份设备的解决方法(设备出现错误或设备脱)
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Head First Servlets & JSP(中文版)
(美)巴萨姆、(美)塞若、(美)贝茨 / 苏钰函、林剑 / 中国电力出版社 / 2006-10 / 98.00元
《Head First Servlets·JSP》(中文版)结合SCWCD考试大纲讲述了关于如何编写servlets和JSP代码,如何使用JSP表达式语言,如何部署Web应用,如何开发定制标记,以及会话状态、包装器、过滤器、企业设计模式等方面的知识,以一种轻松、幽默而又形象的方式让你了解、掌握servlets和JSP,并将其运用到你的项目中去。《Head First Servlets·JSP》(中......一起来看看 《Head First Servlets & JSP(中文版)》 这本书的介绍吧!