You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android应用绘制航点时触发ConcurrentModificationException问题求助

解决Android地图航点绘制时的ConcurrentModificationException问题

这个问题我碰到过好多次了,本质是ArrayList的快速失败迭代器机制在搞事情——哪怕你没在迭代循环里直接改列表,只要另一个线程(比如处理触摸事件的UI线程)在你迭代的时候修改了列表的结构(比如添加、删除元素,或者调整元素位置导致列表 resize),迭代器检测到内部的修改计数不一致,就会抛出这个异常。

下面给你几个靠谱的解决办法,你可以根据自己的场景选:

1. 改用线程安全的CopyOnWriteArrayList

这是最省心的方案,适合读多写少的场景(比如航点绘制的频率比用户移动航点的频率高)。CopyOnWriteArrayList每次修改操作(add/remove/set)都会复制一份底层数组,迭代器是基于修改前的旧数组创建的,所以完全不会因为后续的并发修改抛出异常。

替换起来也简单,把原来的ArrayList<Waypoint>声明改成:

List<Waypoint> waypoints = new CopyOnWriteArrayList<>();

之后不管是绘制迭代还是触摸修改,都不需要额外加锁,集合本身会处理线程安全问题。

2. 给所有列表操作加同步锁

如果不想换集合类,那就用同步锁保证同一时间只有一个线程能访问航点列表。先定义一个全局的锁对象:

private final Object waypointLock = new Object();

然后在绘制航点的迭代代码外面包上同步块:

synchronized(waypointLock) {
    for (Waypoint wp : waypoints) {
        // 你的航点绘制逻辑
        drawWaypoint(wp);
    }
}

同时,在用户触摸移动航点修改列表的代码里,也要用同一个锁包裹:

synchronized(waypointLock) {
    // 比如移除旧位置的航点,添加到新位置
    waypoints.remove(selectedWaypointIndex);
    waypoints.add(newWaypointIndex, updatedWaypoint);
}

这样就能彻底避免并发访问的冲突,缺点是锁会带来一点性能开销,但对于航点这种量级的数据来说基本可以忽略。

3. 迭代时使用列表的快照副本

如果对实时性要求不是特别高(比如用户移动航点后,延迟一帧绘制最新状态没问题),可以每次绘制前先复制一份航点列表的快照,然后迭代这个副本:

// 复制出一个快照,原列表修改不影响这个副本
List<Waypoint> waypointSnapshot = new ArrayList<>(waypoints);
for (Waypoint wp : waypointSnapshot) {
    drawWaypoint(wp);
}

这种方式不需要加锁,也不用换集合,缺点是绘制的可能不是最新的航点状态,适合修改频率低的场景。

额外注意点

如果你的移动航点操作只是修改航点对象的属性(比如更新坐标),而不是修改列表的结构(add/remove),那其实不会触发这个异常。这时候你要检查是不是代码里不小心做了列表结构的修改(比如为了调整航点顺序做了remove再add),或者要确保航点对象的属性修改是线程安全的(比如用volatile修饰坐标字段,或者修改属性时加锁)。

内容的提问来源于stack exchange,提问作者juergen d

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 09:02:17