interceptor cn.xxx.interceptor.XXXInterceptor@39d56dc returned null解决办法
问题描述
okttp3 + rxJava 十分经典,被大量运用到Android app。作为一款网络请求利器,设计上及其巧妙,性能优越,语义规范明确,该组合得到广大开发者的欢迎和认可。
不过,在使用拦截器处理请求预处理或者嵌套请求的时候,发现了一个java.lang.NullPointerException
。发生的代码逻辑是:当检查到异常就给请求框架返回一个null。然而,okttp3 并不会直接把这个null返回给上层框架处理,而是直接抛出运行时异常。
错误的信息如下:
java.lang.NullPointerException: interceptor cn.xxx.xxx.net.interceptor.RetryInterceptor@7911838 returned null
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:157)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
at cn.xxx.xxx.net.interceptor.TimeCalibrationInterceptor.intercept(TimeCalibrationInterceptor.java:28)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
at cn.dasyun.controlboardbusi.net.interceptor.TokenInterceptor.intercept(TokenInterceptor.java:62)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
at okhttp3.logging.HttpLoggingInterceptor.intercept(HttpLoggingInterceptor.java:212)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:254)
at okhttp3.RealCall.execute(RealCall.java:92)
at retrofit2.OkHttpCall.execute(OkHttpCall.java:188)
at retrofit2.DefaultCallAdapterFactory$ExecutorCallbackCall.execute(DefaultCallAdapterFactory.java:104)
at cn.xxx.xxx.service.ClientService.lambda$checkCamera$0$ClientService(ClientService.java:450)
at cn.xxx.xxx.service.-$$Lambda$ClientService$A3xHPJOiMiORStzuxUJBZ0MjDFU.run(lambda)
at java.lang.Thread.run(Thread.java:761)
问题分析
打开处理interceptor 的okhttp源码,我们找到抛出null异常的代码块进行分析。
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
RealConnection connection) throws IOException {
//...
// Call the next interceptor in the chain.
RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
writeTimeout);
Interceptor interceptor = interceptors.get(index);
Response response = interceptor.intercept(next);
// Confirm that the next interceptor made its required call to chain.proceed().
if (httpCodec != null && index + 1 < interceptors.size() && next.calls != 1) {
throw new IllegalStateException("network interceptor " + interceptor
+ " must call proceed() exactly once");
}
// Confirm that the intercepted response isn't null.
if (response == null) {
throw new NullPointerException("interceptor " + interceptor + " returned null");
}
if (response.body() == null) {
throw new IllegalStateException(
"interceptor " + interceptor + " returned a response with no body");
}
return response;
}
在代码中我们看到这一处代码:
// Confirm that the intercepted response isn't null.
if (response == null) {
throw new NullPointerException("interceptor " + interceptor + " returned null");
}
如果返回的response为null,则直接抛出NullPointerException
。至此为止,我们知道框架限制我们返回一个null给业务逻辑,那么如何理解这一行为呢?
刚好在前几天看到一个前辈的描述:外国人认为程序如果不能正常运行,则会使用异常机制来中断业务逻辑处理;而国内的人喜欢返回一个默认值或者null来让上一层业务进行处理,从而使业务能够进行下去。
前辈的话解答了在下的困惑,我们习惯返回一个默认值或者null给业务,业务针对各自的逻辑进行处理,而框架的制定者(外国友人)觉得这是一个异常,不应该流转到业务层再进行处理。
至于说,以上两种编程理念哪种更优,这是个仁者见仁智者见智的事情,各自按照心中的答案行事即可。
解决办法
那么针对这个异常应该如何解决呢?上文其实已经给出答案,那就是在自定义的interceptor 中使用异常来告诉框架我的处理流程已经中止,不需要进行额外的操作。
根据函数定义Response proceed(Request request) throws IOException
,我们可以在代码里面使用:
if (response == null) {
throw new IOException();
}