Android API 7(2.1),MapView,MultiThreading,ConcurrentModificationException

时间:2012-04-30 12:15:21

标签: android multithreading android-mapview

首先,我不知道在哪里,我不知道为什么,我得到ConcurrentModificationException。我的Logcat乱序(或者我不能使用它)但是从不显示有关异常的任何信息(我阅读了很多关于它的文章,没有任何帮助,也许无法正确调试我的手机)

其次抱歉我的语言很混乱,我尽可能清楚地表达,代码可以提供帮助,请帮帮我

所以,问题是下一个:

我使用Mapview和5个自定义CustomItemizedOverlay(来源编号1)。 从MapView我开始一些(1,2,最多5)线程到webservice(来源2),然后我得到结果(它的列表)我把它们绘制成mapoverlay(来源3)

因此MapView(实现5个ResponseHandlerInterfaces)通过myActions(扩展线程)向webservice发送请求,当动作获得响应时,它们调用responseHandler.reportResponseList(List list)方法。 (MapView在这里取回控件)

and all of it causes ConcurrentModificationException sometimes
(rarely ArrayIndexOutOfBoundsException)

我有选项活动来设置所需的列表,我也有刷新按钮,以获取列表。让我带领你完成一个例子。

我刚打开MapView,它是空的。我只需要一种物体。我点击刷新,在网络通信后,我在我的mapview上得到了标记。很酷,它正在工作。现在我要去选项,我设置了更多的对象来请求。在mapview中再次使用Refresh,有时我会得到各种对象,有时我会得到ConcurrentModificationException。

来源1号

public class CustomItemizedOverlay<T> extends ItemizedOverlay<T>{

private Context mContext;
private Object lock = new Object();
private CopyOnWriteArrayList<T> overlays = new CopyOnWriteArrayList<T>();

public CustomItemizedOverlay(Drawable marker, Context context) {
    super(boundCenterBottom(marker));
    this.mContext = context;
    populate();
}
@Override
protected boolean onTap(int index){
 // doesn't matter
}
public void clear(){
    synchronized (lock) {
        overlays.clear();
    }
}
public void addOverlay(T overlay){
    synchronized (lock) {
        overlays.add(overlay);
        setLastFocusedIndex(-1);
        populate();
    }
}
public void removeOverlay(int selected){
    synchronized (lock) {
        overlays.remove(selected);
        populate();
        setLastFocusedIndex(-1);
    }
}
@Override
protected T createItem(int i) {
    synchronized (lock) {
        return overlays.get(i);         
    }
}
@Override
public int size() {
    synchronized (lock) {
        return overlays.size();
    }
}
public void setLock(Object o){
        this.lock = o;
    }
}

来源2

MapView:

public class MyMap extends MapActivity implements LocationListener, RestResPonseHandler { // there are 5 type of responsehandlers, one for each overlay 

private MapView mapView;
private MyLocationOverlay myLocationOverlay;

private Object lock = new Object();

private CustomItemizedOverlay<CustomOverlayItem<MyObject1>> my1Overlay;
private CustomItemizedOverlay<CustomOverlayItem<MyObject2>> my2Overlay;
private CustomItemizedOverlay<CustomOverlayItem<MyObject3>> my3Overlay;
private CustomItemizedOverlay<CustomOverlayItem<MyObject4>> my4Overlay;
private CustomItemizedOverlay<CustomOverlayItem<MyObject5>> my5Overlay;

public void getObject1List(){ // there are 5 getList methods
    new RestAction(this).start(); // 'this' is the object which implements required RestResponseHandler interface. in every case it will be 'this'. MyMap implements all kind of required RestResponseHandler interfaces
    }

来源3(非主线程)//这是每个'CustomItemizedOverlay填充方法'的模式。在操作报告结果(对象列表)后,mapview将使用OverlayItems填充实际叠加层

@Override
public void reportResponseList(List<MyObject1> objects) {
    if (my1Overlay == null){
        List<Overlay> mapOverlays = mapView.getOverlays();
        Drawable marker = this.getResources().getDrawable(R.drawable.icon);
        my1Overlay = new CustomItemizedOverlay<CustomOverlayItem<MyObject1>>(marker, this);
        my1Overlay.setLock(lock); // MyMap has lock object, look at source 2 (also CustomItemizedOverlay (source 1) )
        mapOverlays.add(my1Overlay);
    } else {
        my1Overlay.clear(); 
    }   
    synchronized (lock) {
        for(int i=0;i<objects.size();++i){
            MyObject1 object = objects.get(i);
            CustomOverlayItem<MyObject1> item = new CustomOverlayItem<CustomBuilding>(object.getPositionId(), object);
            my1Overlay.addOverlay(item);
        }
    refreshView();
    }
}

其中refreshView帖子可以运行到主线程以更新mapView。

public void refreshView(){
    new Thread(new Runnable(){
        @Override
        public void run(){
            mapView.post(new Runnable(){
                @Override
                public void run(){
                    mapView.invalidate();
                }
            }); 
        }
    }).start();
}

解决方案: 在CommonsWare的回答之后,我将我的源代码修改为:

@Override
public void reportResponseList(final List<MyObject1> objects) {     
    if (my1Overlay == null){
        List<Overlay> mapOverlays = mapView.getOverlays();
        Drawable marker = this.getResources().getDrawable(R.drawable.icon);
        my1Overlay = new CustomItemizedOverlay<CustomOverlayItem<MyObject1>>(marker, this);
        my1Overlay.setLock(lock);
        mapOverlays.add(my1Overlay);
    } else {
        runOnUiThread(new Runnable(){
            @Override
            public void run(){
                my1Overlay.clear();
            }
        }); 
    }
    runOnUiThread(new Runnable(){
        @Override
        public void run(){
            for(int i=0;i<objects.size();++i){
                MyObject1 object = objects.get(i);
                CustomOverlayItem<MyObject1> item = new CustomOverlayItem<MyObject1>(object.getPositionId(), object);
                my1Overlay.addOverlay(item);
            }
            refreshView();  
        }
    });
}

现在此刻似乎有效。我不知道它有多漂亮,但似乎有效。 (也许mapOverlays.add()方法也应该在主线程上)非常感谢。

1 个答案:

答案 0 :(得分:0)

如果在调用my1Overlay之前MapView已经是reportResponseList()的一部分,则不应在后台线程上对其进行修改。 MapView将使用Overlay。相反,在后台线程中创建一个新的Overlay,在主应用程序线程上交换覆盖(删除旧的,添加新的)。