内容简介:今天在做网络接口的时候, 一个返回结果应该是根据错误信息, 进入代码可得:此处代码, 说明服务器返回的成功,
每日一问-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还原数据库的时候出现提示无法打开备份设备的解决方法(设备出现错误或设备脱)
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Algorithms + Data Structures = Programs
Niklaus Wirth / Prentice Hall / 1975-11-11 / GBP 84.95
It might seem completely dated with all its examples written in the now outmoded Pascal programming language (well, unless you are one of those Delphi zealot trying to resist to the Java/.NET dominanc......一起来看看 《Algorithms + Data Structures = Programs》 这本书的介绍吧!