2012-04-30 5 views
0

Прежде всего, я не знаю, где, я не знаю, почему, я получаю ConcurrentModificationException. Мой Logcat не работает (или просто я не могу его использовать), но никогда не показывает никакой информации об исключениях (я прочитал много статей об этом, и ничего не помогло, может быть, не удалось правильно отладить мой телефон)Android API 7 (2.1), MapView, MultiThreading, ConcurrentModificationException

Во-вторых Сожалеем о моем замешательстве английском, я попытаюсь сформулировать так ясно, как я могу, коды могли бы помочь, пожалуйста, помогите мне

Таким образом, проблема является следующим:

я использую MAPview и 5 пользовательских CustomItemizedOverlay (Источник № 1). Из MapView я запускаю некоторые (1, 2, max 5) потоки в webservice (Источник 2), и после того, как я получаю результаты назад (это список), я рисую их в mapoverlay (источник 3)

так MapView (реализует 5 ResponseHandlerInterfaces) отправляет запросы в webservice через myActions (extends Thread), и когда действия получают ответы, они вызывают методы responseHandler.reportResponseList (List List). (MapView вернуть контроль прямо здесь)

and all of it causes ConcurrentModificationException sometimes 
(rarely ArrayIndexOutOfBoundsException) 

я получил опцию Активность установить необходимые списки и я также получил кнопку Обновить, чтобы получить списки. позвольте мне привести вас к одному примеру.

Я только что открыл MapView, он пуст. Мне нужен только один вид объектов. Я подключаюсь к обновлению, после сетевой связи, я получаю маркеры на моем карте. круто, он работает. Теперь я перейду в «Параметры», и я задаю больше объектов для запроса. Используйте Refresh снова в mapview, и иногда я получаю все виды объектов, иногда я получаю ConcurrentModificationException.

Источник No. 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; 
    } 
} 

Источник No. 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() должен быть включен в основной поток тоже) Большое спасибо.

ответ

0

Если my1Overlay уже входит в состав MapView к моменту выхода reportResponseList(), вы не должны изменять его на фоновом потоке. MapView будет использовать это Overlay.Вместо этого создайте новый Overlay в фоновом потоке, накладываете swap (удалите старый, добавьте новый) в основной поток приложения.

+0

Должен ли my1Overlay.clear() и синхронизированный цикл работать в основном потоке? – Victor

+0

@Victor: Это еще одна возможность, хотя вам больше не нужно, чтобы она была «синхронизирована», если все, кто обращается к ней, делают это в главном потоке приложения. – CommonsWare

+0

и smogло mapOverlays.add (my1Overlay); быть на основной теме? – Victor

Смежные вопросы