Crash-fix-2:org.springframework.http.converter.HttpMessageNotReadableException
最近开始对APP上的Crash进行对应,发现有好多常见的问题,同一个问题在多个APP都类似的出现了,这里记录下这些常见的错误。
crash Log:
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崩溃。
项目背景:
项目是使用AA框架开发的,Api请求使用的是SpringRestTemplate,使用Gson进行json与Bean的转换
为了解决Gson在Android6.0上的bug,自定义了一个GsonConverter,继承自GsonHttpMessageConverter。在数据转换时添加了log,主要代码如下:
@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出数据,并没有业务逻辑
mClient.setRestErrorHandler(handler);
@Override
public void onRestClientExceptionThrown(RestClientException e) {
LogUtil.e(e);
}
根据CrashLog,定位到问题是Api返回的数据转换成Bean出错导致的,代码定位到了GsonConverter.readInternal方法,通常来说方法上已经声明了错误类型了,按照业务逻辑抛出指定的错误类型不应该导致App崩溃,应该是回调RestErrorHandler的方法才对的。但是根据实际测试下来和猜想的还是有很大的区别。
然后抽取一个Api,代码如下:
ResponseEntity<CheckVersionResponse> entity = apiHelper.checkVersion();
if (null == entity || !entity.hasBody()) {
return;
}
如果在GsonConverter.readInternal中抛出异常,则App崩溃。如果在以上代码中添加TryCatch,则可以捕获到异常。这个就好奇了,怎么是直接抛出异常,而不会回调异常处理接口。如果是这么修改的话,整个系统几十个接口都需要修改,工程量太大而且太傻。
解决办法:
既然抛出异常会导致崩溃,那么当Api转换错误时,数据返回null不就可以了。修改后的代码:
@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类型才会回调异常回调接口,其他的错误只会直接抛出。
然而HttpMessageNotReadableException不是RestClientException类型的,所以异常就直接抛出,没有被捕获当然就导致APP崩溃了。
最新文章
- React的使用与JSX的转换
- 【2016-10-20】【坚持学习】【Day10】【反射2】
- 将字符串转换成JSON对象
- 红字差评系列2.dwarf
- Android Broadcast Receiver 使用入门
- Gym 100650H	 Two Ends DFS+记忆化搜索
- spring-boot支持双数据源mysql+mongo
- 【Android 界面效果36】Fragment管理
- JS代码大全 (都是网上看到 自己整理的)
- Bootstrap Paginator分页插件的使用
- python学习之成员信息增删改查
- c# 集合的交集、并集、差集
- AJAX基础_AJAX获取PHP数据
- 压力测试工具ab - Apache HTTP server benchmarking tool
- keepalived介绍
- jQuery源码框架fn解读
- AX_List
- 【BZOJ1925】[SDOI2010]地精部落(动态规划)
- Eclipse构建项目时";An internal error occurred during: ";Building workspace";. Java heap space";
- 多线程中的信号机制--signwait()函数【转】
热门文章
- [PHP] 调用微博API 发微博OAuth2.0
- 尤雨溪在直播中讲到的Vue3.0 Beta的那些特性,快记笔记了
- 敏捷与OKR实践(如何让OKR与敏捷计划共存)
- tp5.1 模型 where多条件查询 like 查询
- PHP $_FILES的应用
- 第九次-DFA最小化,语法分析初步
- 2019-2020-1 20199310《Linux内核原理与分析》第七周作业
- Redis持久化存储(三)
- dockerfile简介及书写规则
- Codeforces Round #623 (Div. 2, based on VK Cup 2019-2020 - Elimination Round, Engine) A Dead Pixel