安卓应用接入Gson后偶发Random.nextInt抛IllegalArgumentException如何修复
认知正误确认
你认为「Gson解析出错应该抛出相关异常而非返回空列表」的认知不完全正确,以下两种场景Gson会返回空列表且不抛出任何异常:
- 待解析的JSON内容为合法空数组
[] - 配置了自定义容错类型适配器,对解析失败的数组类型静默返回空集合
如果解析过程中Gson确实抛出了JsonSyntaxException/JsonIOException等异常,但上层调用代码吞掉了所有Throwable,也会导致方法返回默认空值/空列表。
偶发空列表的可能原因
你本地无法复现的偶发空列表,大概率是以下几种场景导致的:
- 上层调用
getQuestionsFromJson的代码加了全局异常捕获,吞掉了资源读取、Gson解析过程中抛出的异常,静默给questionsFromJson赋了空默认值 - 极少数定制ROM、应用分身/平行空间沙箱环境下,raw资源读取异常返回空流,空流被Gson解析为空列表
- 多线程并发操作
questionsFromJson,列表被其他线程执行了clear/批量remove操作,刚好在你调用size()时长度为0 - 混淆规则偶发失效:Question类未加全
@SerializedName注解,部分极端系统环境下字段被混淆导致解析结果为空
排查修复方案
第一步:完善解析逻辑,加异常埋点和兜底
修改getQuestionsFromJson方法,补全异常捕获、埋点上报和默认值逻辑:
private ArrayList<Question> getQuestionsFromJson(int id, Context context) { ArrayList<Question> result = new ArrayList<>(); // 初始化默认空列表,避免返回null Reader reader = null; try { Gson gson = new GsonBuilder().setPrettyPrinting().create(); // 直接指定TypeToken为ArrayList<Question>,避免类型转换异常 Type dtoListType = new TypeToken<ArrayList<Question>>(){}.getType(); reader = new InputStreamReader(context.getResources().openRawResource(id)); List<Question> tempList = gson.fromJson(reader, dtoListType); if (tempList != null) { result.addAll(tempList); } // 埋点:如果解析结果为空,上报设备ROM版本、资源ID有效性、应用运行环境等信息 if (result.isEmpty()) { // 可接入自有埋点/第三方统计平台上报异常场景 } } catch (Throwable t) { // 捕获所有异常,上报堆栈信息用于排查 t.printStackTrace(); } finally { if (reader != null) { try { reader.close(); } catch (IOException e) { // 可忽略IO关闭异常 } } } return result; }
第二步:调用处加防御性判断,避免崩溃
在使用列表前加长度校验,极端场景下走兜底逻辑:
questionsFromJson = getQuestionsFromJson(R.raw.ru, context); // 列表为空时重试一次,或者直接加载预制的默认Question列表兜底 if (questionsFromJson.isEmpty()) { questionsFromJson = getQuestionsFromJson(R.raw.ru, context); if (questionsFromJson.isEmpty()) { // 走本地硬编码的兜底数据逻辑,保证列表至少有QUESTIONS_SIZE个元素 questionsFromJson = getDefaultFallbackQuestions(); } } for (int i = 0; i < QUESTIONS_SIZE; i++){ // 极端情况再做一层判断,避免崩溃 if (questionsFromJson.isEmpty()) { break; } int index = random.nextInt(questionsFromJson.size()); int imageId = context.getResources().getIdentifier(questionsFromJson.get(index).getImage().replace(".png", "") , "drawable", packageName); questionsFromJson.get(index).setImageId(imageId); questions.add(questionsFromJson.remove(index)); }
第三步:排查潜在风险
- 确认
questionsFromJson是否为多线程共享变量,如果是需要给操作加同步锁,或者使用CopyOnWriteArrayList等线程安全实现 - 给Question类加
@Keep注解,所有字段加@SerializedName注解,避免混淆规则偶发失效
内容的提问来源于stack exchange,提问作者Riggel
相关产品推荐
相关产品推荐

