0

I have a map with custom overlay and a bunch of markers. Because I'm doing markers grouping when zoom change happens I have to clear overlay, regroup items for new zoom and lay then on map again. I'm doing this in backround thread to get things smooth. So you click to zoom, markers disappear, busy indicator shows in corner and new markers appear on map. all is smooth as it should however I get concurent modification exception when you zoom twice in row or zoom in and zoom out immediatelly. I've read plenty about this but none solutions are ok for me yet. For example moving map moification to onPostExecute which is on UI does prevent this but freezes map for a time markers are drawn and it looses its smoothness. I tried to make markders list on itemized overlay synchronized list but did not help. How can I prevent this?

This is what I call on zoom change:

private void drawItems() {
  final MyMapView mw = this.mapView;

  //remove existing first
  //mw.getOverlays().remove(stuffOverlay);
  stuffOverlay.clearAll();
  //mw.invalidate();

  new AsyncTask<String, Integer, String>() {
    @Override
    protected void onPreExecute() {
      super.onPreExecute();
      ((ProgressBar)findViewById(R.id.mapProgressBar)).setVisibility(View.VISIBLE);
    }

    @Override
    protected String doInBackground(String... params) {
      //get items on map
      HashMap<String, JSONArray> groupedItems = getItemsForZoom(mw.getZoomLevel());

      if (groupedItems==null)
        return "done";

      //create a copy of it sometimes it was throwing concurentmodificationexception
      HashMap<String, JSONArray> groupedItemsCopy = new HashMap<String, JSONArray> (groupedItems);

      Iterator it = groupedItemsCopy.entrySet().iterator();
      while (it.hasNext()) { //loop over markers
        try {
          //get item 
          HashMap.Entry<String, Object> pair = (HashMap.Entry)it.next();
          JSONArray itemsInMarker = (JSONArray) pair.getValue();
          JSONObject itemData = (JSONObject) itemsInMarker.get(0);

          //get truncated coordinates
          String coordsKey = pair.getKey();
          String[] coordsSplitted = coordsKey.split("_");

          //add first marker to map
          GeoPoint gp = new GeoPoint((int)(Double.parseDouble(coordsSplitted[0])*1E6), (int)(Double.parseDouble(coordsSplitted[1])*1E6));
          OverlayItem markerOverlay = new OverlayItem(gp, "marker", "snipper text");
          Drawable markerPic = ServerCall.drawableFromUrl(((JSONObject)((JSONObject)itemData.get("picture")).get("1")).get("picture_full")+"_0.png");
          //markerPic.setBounds(0, 0, markerPic.getIntrinsicWidth(), markerPic.getIntrinsicHeight());
          markerPic.setBounds(-25, -25, 50, 50);
          markerOverlay.setMarker(markerPic);
          stuffOverlay.addOverlay(markerOverlay, itemData);

          if (stuffOverlay.size() > 0) {
            mapView.getOverlays().add(stuffOverlay);
          }
        } catch (JSONException e) {
          e.printStackTrace();
        } catch (IOException e) {
          e.printStackTrace();
        }
      }
      return "done";
    }

    @Override
    protected void onPostExecute(String result) {
      super.onPostExecute(result);
      ((ProgressBar)findViewById(R.id.mapProgressBar)).setVisibility(View.GONE);
      mapView.invalidate();
    }
  }.execute();
}

This is my itemized overlay:

private class StuffOverlay extends ItemizedOverlay {
  private List<OverlayItem> mOverlays = Collections.synchronizedList(new ArrayList<OverlayItem>());
  private ArrayList<JSONObject> mStuff = new ArrayList<JSONObject>();
  public int currentItem;
  Map mMap;

  public StuffOverlay(Drawable defaultMarker, Map map) {
    super(boundCenterBottom(defaultMarker));
    mMap = map;
    populate(); //keep here
  }

  public void addOverlay(OverlayItem overlay, JSONObject stuffData) {
    synchronized (mOverlays) {
      mOverlays.add(overlay);
    }

    mStuff.add(stuffData);
    setLastFocusedIndex(-1);  //keep here
    populate();
  }

  public void clearAll() {
    synchronized (mOverlays) {
      mOverlays.clear();
    }

    setLastFocusedIndex(-1); //keep here
    populate();
  }

  @Override
  protected OverlayItem createItem(int i) {
    synchronized (mOverlays) {
      return mOverlays.get(i);
    }
  }

  @Override
  public int size() {
    synchronized (mOverlays) {
      return mOverlays.size();
    }
  }

  public void draw(Canvas canvas, MapView mapView, boolean shadow) {
    if (!shadow) {
      super.draw(canvas, mapView, false);
    }
  }
}

And this is what I'm getting ;o(

05-30 20:45:35.485: W/dalvikvm(1139): threadid=3: thread exiting with uncaught exception (group=0x4001b188)
05-30 20:45:35.485: E/AndroidRuntime(1139): Uncaught handler: thread main exiting due to uncaught exception
05-30 20:45:35.495: E/AndroidRuntime(1139): java.util.ConcurrentModificationException
05-30 20:45:35.495: E/AndroidRuntime(1139):     at java.util.AbstractList$SimpleListIterator.next(AbstractList.java:64)
05-30 20:45:35.495: E/AndroidRuntime(1139):     at com.google.android.maps.OverlayBundle.draw(OverlayBundle.java:44)
05-30 20:45:35.495: E/AndroidRuntime(1139):     at com.google.android.maps.MapView.onDraw(MapView.java:476)
05-30 20:45:35.495: E/AndroidRuntime(1139):     at android.view.View.draw(View.java:6535)
05-30 20:45:35.495: E/AndroidRuntime(1139):     at android.view.ViewGroup.drawChild(ViewGroup.java:1531)
05-30 20:45:35.495: E/AndroidRuntime(1139):     at android.view.ViewGroup.dispatchDraw(ViewGroup.java:1258)
4

1 回答 1

1

我认为问题可能与这里的这些行有关:

      if (stuffOverlay.size() > 0) {
        mapView.getOverlays().add(stuffOverlay);
      }

您只需将对象添加stuffOverlay到地图叠加数组一次,您甚至可以在 stuffOverlay 中包含任何项目之前执行此操作。我会转到mapView.getOverlays().add(stuffOverlay);stuffOverlay 被实例化的地方。

于 2012-05-31T03:15:11.163 回答