Crash-fix-2:org.springframework.http.converter.HttpMessageNotReadableException

最近开始对APP上的Crash进行对应,发现有好多常见的问题,同一个问题在多个APP都相似的出现了,这里记录下这些常见的错误。
crash Log:javascript

org.springframework.http.converter.HttpMessageNotReadableException: Could not read JSON: com.google.gson.stream.MalformedJsonException: Unterminated string at line 1 column 425727 path $.shops[631].lineup; nested exception is com.google.gson.ad: com.google.gson.stream.MalformedJsonException: Unterminated string at line 1 column 425727 path $.shops[631].lineup
    at 包名.k.readInternal(SourceFile:75)
    at org.springframework.http.converter.AbstractHttpMessageConverter.read(SourceFile:147)
    at org.springframework.web.client.HttpMessageConverterExtractor.extractData(SourceFile:76)
    at org.springframework.web.client.RestTemplate$ResponseEntityResponseExtractor.extractData(SourceFile:655)
    at org.springframework.web.client.RestTemplate$ResponseEntityResponseExtractor.extractData(SourceFile:641)
    at org.springframework.web.client.RestTemplate.doExecute(SourceFile:484)
    at org.springframework.web.client.RestTemplate.execute(SourceFile:439)
    at org.springframework.web.client.RestTemplate.exchange(SourceFile:415)

根据错误log的意思,应该是服务器(php开发)返回了非正常的json格式错误信息致使app崩溃。php

项目背景:
项目是使用AA框架开发的,Api请求使用的是SpringRestTemplate,使用Gson进行json与Bean的转换css

为了解决Gson在Android6.0上的bug,自定义了一个GsonConverter,继承自GsonHttpMessageConverter。在数据转换时添加了log,主要代码以下:java

@Override
    protected Object readInternal(Class<?> clazz, HttpInputMessage inputMessage)
            throws IOException, HttpMessageNotReadableException {
        String str = FileCopyUtils.copyToString(new InputStreamReader(inputMessage.getBody(), getCharset(inputMessage.getHeaders())));
        LogUtil.d("in =" + str);
        try {
            Type typeOfT = getType();

            if (typeOfT != null) {
                return this.gson.fromJson(str, typeOfT);
            } else {
                return this.gson.fromJson(str, clazz);
            }
        } catch (JsonSyntaxException ex) {
            throw new HttpMessageNotReadableException("Could not read JSON: " + ex.getMessage(), ex);
        } catch (JsonIOException ex) {
            throw new HttpMessageNotReadableException("Could not read JSON: " + ex.getMessage(), ex);
        } catch (Exception ex) {
            throw new HttpMessageNotReadableException("Could not read JSON: " + ex.getMessage(), ex);
        }
    }

而后就是AA中Rest的配置了,将自定义的GsonConverter配置到Rest上。
在每一个请求中都设置了RestErrorHandler,单纯的log出数据,并无业务逻辑web

mClient.setRestErrorHandler(handler);
  @Override
    public void onRestClientExceptionThrown(RestClientException e) {
        LogUtil.e(e);
    }

根据CrashLog,定位到问题是Api返回的数据转换成Bean出错致使的,代码定位到了GsonConverter.readInternal方法,一般来讲方法上已经声明了错误类型了,按照业务逻辑抛出指定的错误类型不该该致使App崩溃,应该是回调RestErrorHandler的方法才对的。可是根据实际测试下来和猜测的仍是有很大的区别。spring

而后抽取一个Api,代码以下:json

ResponseEntity<CheckVersionResponse> entity = apiHelper.checkVersion();

        if (null == entity || !entity.hasBody()) {
            return;
        }

若是在GsonConverter.readInternal中抛出异常,则App崩溃。若是在以上代码中添加TryCatch,则能够捕获到异常。这个就好奇了,怎么是直接抛出异常,而不会回调异常处理接口。若是是这么修改的话,整个系统几十个接口都须要修改,工程量太大并且太傻。
解决办法:
既然抛出异常会致使崩溃,那么当Api转换错误时,数据返回null不就能够了。修改后的代码:api

@Override
    protected Object readInternal(Class<?> clazz, HttpInputMessage inputMessage)
            throws IOException, HttpMessageNotReadableException {
        String str = FileCopyUtils.copyToString(new InputStreamReader(inputMessage.getBody(), getCharset(inputMessage.getHeaders())));
        LogUtil.d("in =" + str);
        try {
            Type typeOfT = getType();

            if (typeOfT != null) {
                return this.gson.fromJson(str, typeOfT);
            } else {
                return this.gson.fromJson(str, clazz);
            }
        } catch (JsonSyntaxException ex) {
            LogUtil.e("Could not read JSON: " + ex.getMessage(), ex);
//            throw new HttpMessageNotReadableException("Could not read JSON: " + ex.getMessage(), ex);
        } catch (JsonIOException ex) {
            LogUtil.e("Could not read JSON: " + ex.getMessage(), ex);
//            throw new HttpMessageNotReadableException("Could not read JSON: " + ex.getMessage(), ex);
        } catch (Exception ex) {
            LogUtil.e("Could not read JSON: " + ex.getMessage(), ex);
//            throw new HttpMessageNotReadableException("Could not read JSON: " + ex.getMessage(), ex);
        }
        return null;
    }

缘由分析:
解决办法找到了,深究下定位个缘由,AA框架自动生成的ApiClient源码:服务器

@Override
    public ResponseEntity<CheckVersionResponse> checkVersion(Map<String, Object> params) {
        HttpEntity<Map<String, Object>> requestEntity = new HttpEntity<Map<String, Object>>(params);
        try {
            return restTemplate.exchange(rootUrl.concat("/checkVersion/"), HttpMethod.POST, requestEntity, CheckVersionResponse.class);
        } catch (RestClientException e) {
            if (restErrorHandler!= null) {
                restErrorHandler.onRestClientExceptionThrown(e);
                return null;
            } else {
                throw e;
            }
        }
    }

从这里能够看出,只有RestClientException类型才会回调异常回调接口,其余的错误只会直接抛出。markdown

然而HttpMessageNotReadableException不是RestClientException类型的,因此异常就直接抛出,没有被捕获固然就致使APP崩溃了。

相关文章
相关标签/搜索
本站公众号
   欢迎关注本站公众号,获取更多信息