通过修改叠加层解决ConcurrentModificationException

时间:2012-12-17 03:01:38

标签: java android debugging concurrency

我一直在尝试修复导致间歇性ConcurrentModificationException的错误。发生了什么事情,我有大量的地理输出与ItemizedOverlay一起显示。但是,移动地图时,我正在尝试清除所有当前的叠加项(地理位置),并用适合新视图窗口的新点替换它们。

因此我有一个回调函数可以清除旧的叠加层并用新叠加层替换它们。我认为我的错误源于多个线程试图同时执行此操作。相关部分如下。我对叠加和这样的工作如何处于低水平的理解非常有限,所以我想知道是否有人可以证实(或反驳)这可能导致问题。

//first clear out the old overlays
List<Overlay> mapOverlays = mapView.getOverlays();
mapOverlays.clear();

//create the new overlays, each initialized with appropriate Drawables
MenuOverlay lowOverlay = new MenuOverlay(this, lowRisk);//(all valid Drawables)
MenuOverlay medOverlay = new MenuOverlay(this, medRisk);
MenuOverlay highOverlay = new MenuOverlay(this, highRisk);

//populate the overlays

//add the new overlays into the list of overlays
mapOverlays.add(lowOverlay);
mapOverlays.add(medOverlay);
mapOverlays.add(highOverlay);

//make the map refresh; this operation has to be pushed to another thread
Runnable runnable = new Runnable() {
    public void run() {
        mapView.invalidate();
    }
};
runOnUiThread(runnable);

我尝试制作此方法synchronized,但错误仍然存​​在。这可能是因为新的runnable在上一次runnable终止之前被推送到UI线程吗?我已经看到提到填充是一种比无效更好的方式,虽然我不完全确定它们是如何不同的。有任何想法如何解决这个问题?

1 个答案:

答案 0 :(得分:1)

应始终在UI线程上修改叠加集。 List返回的getOverlays()MapView所有,并且可以随时决定查看或操作列表。

由于您正在使用大量的地理位置,因此当MapView在UI线程上迭代它们时,您的后台线程可能正在清除(或添加)叠加层。这将触发ConcurrentModificationException,因为迭代器在其基础覆盖集更改时无效。有时,UI线程不会立即看到更改,因此崩溃是间歇性的。

设置叠加层通常是此类工作流程的缓慢部分。为避免同时修改,您可以在后台线程中设置叠加层,然后在clear()内拨打add()Runnable的所有电话。 (另一种选择是使用AsyncTask。)

例如:

// Slow setup steps
final MenuOverlay lowOverlay = new MenuOverlay(this, lowRisk);
final MenuOverlay medOverlay = new MenuOverlay(this, medRisk);
final MenuOverlay highOverlay = new MenuOverlay(this, highRisk);

Runnable runnable = new Runnable() {
    public void run() {
        // Anything that touches UI widgets (map, overlay set, views, etc.)
        List<Overlay> mapOverlays = mapView.getOverlays();
        mapOverlays.clear();

        mapOverlays.add(lowOverlay);
        mapOverlays.add(medOverlay);
        mapOverlays.add(highOverlay);

        mapView.invalidate();
    }
};

runOnUiThread(runnable);