0

I have a thread that receives map points which are sent from a server, and these points are added to the MapView. This works fine but if I interact with the MapView (eg. zoom in, pan, etc) I get a ConcurrentModificationException, so how can I allow access to the user while adding points to the MapView?

02-20 01:44:26.566: E/AndroidRuntime(9448):     FATAL EXCEPTION: main
02-20 01:44:26.566: E/AndroidRuntime(9448):     java.util.ConcurrentModificationException
02-20 01:44:26.566: E/AndroidRuntime(9448):     at java.util.ArrayList$ArrayListIterator.next(ArrayList.java:569)
02-20 01:44:26.566: E/AndroidRuntime(9448):     at com.google.android.maps.OverlayBundle.draw(OverlayBundle.java:41)
02-20 01:44:26.566: E/AndroidRuntime(9448):     at com.google.android.maps.MapView.onDraw(MapView.java:532)
02-20 01:44:26.566: E/AndroidRuntime(9448):     at android.view.View.draw(View.java:13707)
02-20 01:44:26.566: E/AndroidRuntime(9448):     at android.view.View.draw(View.java:13591)
02-20 01:44:26.566: E/AndroidRuntime(9448):     at android.view.ViewGroup.drawChild(ViewGroup.java:2928)
02-20 01:44:26.566: E/AndroidRuntime(9448):     at android.view.ViewGroup.dispatchDraw(ViewGroup.java:2797)
02-20 01:44:26.566: E/AndroidRuntime(9448):     at android.view.View.draw(View.java:13589)
02-20 01:44:26.566: E/AndroidRuntime(9448):     at android.view.ViewGroup.drawChild(ViewGroup.java:2928)
02-20 01:44:26.566: E/AndroidRuntime(9448):     at android.view.ViewGroup.dispatchDraw(ViewGroup.java:2797)
02-20 01:44:26.566: E/AndroidRuntime(9448):     at android.view.View.draw(View.java:13589)
02-20 01:44:26.566: E/AndroidRuntime(9448):     at android.view.ViewGroup.drawChild(ViewGroup.java:2928)
02-20 01:44:26.566: E/AndroidRuntime(9448):     at android.view.ViewGroup.dispatchDraw(ViewGroup.java:2797)
02-20 01:44:26.566: E/AndroidRuntime(9448):     at android.view.View.draw(View.java:13589)
02-20 01:44:26.566: E/AndroidRuntime(9448):     at android.view.ViewGroup.drawChild(ViewGroup.java:2928)
02-20 01:44:26.566: E/AndroidRuntime(9448):     at android.view.ViewGroup.dispatchDraw(ViewGroup.java:2797)
02-20 01:44:26.566: E/AndroidRuntime(9448):     at android.view.View.draw(View.java:13710)
02-20 01:44:26.566: E/AndroidRuntime(9448):     at android.widget.FrameLayout.draw(FrameLayout.java:467)
02-20 01:44:26.566: E/AndroidRuntime(9448):     at com.android.internal.policy.impl.PhoneWindow$DecorView.draw(PhoneWindow.java:2211)
02-20 01:44:26.566: E/AndroidRuntime(9448):     at android.view.ViewRootImpl.drawSoftware(ViewRootImpl.java:2281)
02-20 01:44:26.566: E/AndroidRuntime(9448):     at android.view.ViewRootImpl.draw(ViewRootImpl.java:2177)
02-20 01:44:26.566: E/AndroidRuntime(9448):     at android.view.ViewRootImpl.performDraw(ViewRootImpl.java:2045)
02-20 01:44:26.566: E/AndroidRuntime(9448):     at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1854)
02-20 01:44:26.566: E/AndroidRuntime(9448):     at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:989)
02-20 01:44:26.566: E/AndroidRuntime(9448):     at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:4351)
02-20 01:44:26.566: E/AndroidRuntime(9448):     at android.view.Choreographer$CallbackRecord.run(Choreographer.java:749)
02-20 01:44:26.566: E/AndroidRuntime(9448):     at android.view.Choreographer.doCallbacks(Choreographer.java:562)
02-20 01:44:26.566: E/AndroidRuntime(9448):     at android.view.Choreographer.doFrame(Choreographer.java:532)
02-20 01:44:26.566: E/AndroidRuntime(9448):     at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:735)
02-20 01:44:26.566: E/AndroidRuntime(9448):     at android.os.Handler.handleCallback(Handler.java:725)
02-20 01:44:26.566: E/AndroidRuntime(9448):     at android.os.Handler.dispatchMessage(Handler.java:92)
02-20 01:44:26.566: E/AndroidRuntime(9448):     at android.os.Looper.loop(Looper.java:137)
02-20 01:44:26.566: E/AndroidRuntime(9448):     at android.app.ActivityThread.main(ActivityThread.java:5039)
02-20 01:44:26.566: E/AndroidRuntime(9448):     at java.lang.reflect.Method.invokeNative(Native Method)
02-20 01:44:26.566: E/AndroidRuntime(9448):     at java.lang.reflect.Method.invoke(Method.java:511)
02-20 01:44:26.566: E/AndroidRuntime(9448):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793)
02-20 01:44:26.566: E/AndroidRuntime(9448):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:560)
02-20 01:44:26.566: E/AndroidRuntime(9448):     at dalvik.system.NativeStart.main(Native Method)

3 Answers3

1

Have you tried using the new maps api? Personally I found it much easier to use and easy to change from the old API to the new one.

Link: https://developers.google.com/maps/documentation/android/

One thing that I found particularly improved is the drawing on the map. No more messing with lots of overflows and so on. Example of adding pins using the new API is:

    final GoogleMap map = this.getMap();
    final MarkerOptions marker = new MarkerOptions();
    marker.position(latlng);
    marker.draggable(false);
    marker.icon(BitmapDescriptorFactory.fromResource(drawable));
    map.addMarker(marker);
Corey Scott
  • 2,430
  • 3
  • 28
  • 33
0

While it's appropriate to receive the points from the server in a background thread, you should actually add/update the points in the UI thread. This should prevent any concurrent modification issues. And as long as the addition/updating process isn't too heavy, it shouldn't interfere with the user experience significantly.

Halogen
  • 551
  • 6
  • 11
  • Thanks, I had tried this before but was doing network stuff in there as well, worked once I was just doing UI stuff. – John Wiliger Feb 20 '13 at 02:52
0

I have tried to extract and sanitize some of my old code that uses the old api. The highlights are:

  1. Create an overlay that is not yet on the map
  2. Drawing and otherwise preparing the overlay
  3. Adding the overlay to the map
  4. Forcing the map to redraw

Add this method to your activity

// check if overlay we are adding was already added
final List<Overlay> listOfOverlays = this.mMapView.getOverlays();
if ((this.mPinsOverlay != null) && listOfOverlays.contains(this.mPinsOverlay))
{
    listOfOverlays.remove(this.mPickupOverlay);
}

// create overlay
this.mPinsOverlay = new PinsOverlay();

// add overlay to map
listOfOverlays.add(this.mPinsOverlay);

// force map to redraw
this.mMapView.postInvalidate();

Custom Overlay class, not needed but keeps things neat.

public class PinsOverlay extends Overlay
{
    public PinsOverlay()
    {
        super();
    }

    @Override
    public boolean draw(final Canvas canvas, final MapView mapView, final boolean shadow, final long when)
    {
        super.draw(canvas, mapView, shadow);

        // draw your pins here

        return true;
    }
}
Corey Scott
  • 2,430
  • 3
  • 28
  • 33