Codename One parseJSON模拟器正常手机端空指针异常咨询
问题描述
以下基于Java的Codename One代码可在Simulator中成功运行,但在实体手机上运行失败,代码已添加行号:
58 Map<String, Object> pageMap = null; 59 ConnectionRequest connectionRequest = new ConnectionRequest(); 60 connectionRequest.setUrl("https://api.beta.tab.com.au/v1/tab-info-service/racing/dates/today/meetings"); 61 connectionRequest.setPost(false); 62 connectionRequest.addArgument("jurisdiction", "QLD"); 63 NetworkManager.getInstance().addToQueueAndWait(connectionRequest); 64 try { 65 Dialog.show("Trace", "pre parseJSON", "OK", ""); 66 pageMap = new JSONParser().parseJSON(new InputStreamReader(new ByteArrayInputStream(connectionRequest.getResponseData()), "UTF-8")); 67 Dialog.show("Trace", "post parseJSON", "OK", ""); 68 if (pageMap == null) { 69 Dialog.show("Trace", "pageMap is null!", "OK", ""); 70 } else { 71 Dialog.show("Trace", "pageMap is NOT null,pageMap," + pageMap.toString().substring(0, 200), "OK", ""); 72 } 73 } catch (Exception e) { 74 Dialog.show("Trace", "getPageMap,exception," + e, "OK", ""); 75 }
- 代码中的Trace弹窗是用于获取手机端运行状态的调试手段。
- Simulator环境下运行时,pageMap可正常赋值输出,示例内容为
{meetings=[{meetingName=DOOMBEN, location=QLD...}。 - 实体手机端运行时,第65行
pre parseJSON、第67行post parseJSON弹窗均可正常弹出,执行第68行判断逻辑时触发异常,第74行弹窗展示的异常信息为:java.lang.NullPointerException: Attempt to read from field 'java.util.LinkedHashMap$LinkedHashMapEntry java.util.LinkedHashMap.head' on a null object reference。 - 该段代码在2022年4月16日的历史构建版本中可正常运行,提问者怀疑相关API存在变更,请求协助排查。
问题原因与解决方案
该问题不是公开API变更导致,核心是旧版Codename One JSON解析逻辑在移动端的兼容bug,以及写法上的跨平台隐患:
post parseJSON弹窗正常弹出仅代表parseJSON方法执行完成没有抛出同步异常,不代表返回值是合法初始化的Map实例。Android端ART虚拟机和桌面Simulator用的JVM类加载、对象初始化逻辑存在差异,旧版JSONParser在通过多层流嵌套解析响应时,会出现对象返回但内部LinkedHashMap结构未完成初始化的情况,后续访问Map内容(包括toString()、key遍历、隐式结构检查)时就会抛出你看到的head字段空指针。- 嵌套
ByteArrayInputStream+InputStreamReader的解析写法在移动端部分版本存在编码转换兼容问题,会导致解析结果异常,Simulator因为用的是桌面JVM的流实现,不会触发该问题。
按以下步骤修改即可修复:
- 第一步,解析前先把响应数据转成UTF-8字符串,校验数据完整性,同时规避流嵌套的兼容问题:
byte[] respData = connectionRequest.getResponseData(); // 先判断响应数据本身是否为空 if (respData == null || respData.length == 0) { Dialog.show("Error", "Network response is empty", "OK", ""); return; } String respStr = new String(respData, "UTF-8");
- 第二步,用StringReader做JSON解析,解析后先做类型校验再使用:
JSONParser parser = new JSONParser(); Object parseResult = parser.parseJSON(new StringReader(respStr)); // 先判断类型和空值,不要直接强转接Map if (!(parseResult instanceof Map)) { Dialog.show("Trace", "Parse result is not valid Map, type: " + (parseResult == null ? "null" : parseResult.getClass().getName()), "OK", ""); return; } pageMap = (Map<String, Object>) parseResult; // 后续再做key存在性校验、业务逻辑处理 if (pageMap.containsKey("meetings")) { // 正常业务逻辑 }
- 额外注意:
addToQueueAndWait会阻塞调用线程,如果这段代码不是写在EDT事件线程上,阻塞等待可能导致网络响应数据读取不完整,建议优先使用带回调的addToQueue方法异步处理网络请求,规避线程阻塞带来的隐性异常。
内容的提问来源于stack exchange,提问作者stylsy
相关产品推荐
相关产品推荐

