0

My application works fine with Android 1.6, but when I run it on Android 2.2 I get concurrentModificationException in some places. After wrapping my head around it for days, I've come up with a theory: I guess that multi-threading (or something similar) has been introduced since 1.6. Could this be it? If so, is it any way to force the application to be run without the multi-threading? I've tried to set the target to 1.6, but no luck.. In advance, I'd like to thank you for your time.

Exception:

04-05 11:47:12.812: ERROR/AndroidRuntime(5328): FATAL EXCEPTION: main
04-05 11:47:12.812: ERROR/AndroidRuntime(5328): java.lang.RuntimeException: Failure delivering result ResultInfo{who=null, request=1, result=-1, data=Intent { cmp=ntnu.client/com.google.android.maps.MapView (has extras) }} to activity {ntnu.client/ntnu.client.MapClient}: java.util.ConcurrentModificationException
04-05 11:47:12.812: ERROR/AndroidRuntime(5328):     at android.app.ActivityThread.deliverResults(ActivityThread.java:3808)
04-05 11:47:12.812: ERROR/AndroidRuntime(5328):     at android.app.ActivityThread.handleSendResult(ActivityThread.java:3850)
04-05 11:47:12.812: ERROR/AndroidRuntime(5328):     at android.app.ActivityThread.access$2800(ActivityThread.java:136)
04-05 11:47:12.812: ERROR/AndroidRuntime(5328):     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2209)
04-05 11:47:12.812: ERROR/AndroidRuntime(5328):     at android.os.Handler.dispatchMessage(Handler.java:99)
04-05 11:47:12.812: ERROR/AndroidRuntime(5328):     at android.os.Looper.loop(Looper.java:143)
04-05 11:47:12.812: ERROR/AndroidRuntime(5328):     at android.app.ActivityThread.main(ActivityThread.java:5068)
04-05 11:47:12.812: ERROR/AndroidRuntime(5328):     at java.lang.reflect.Method.invokeNative(Native Method)
04-05 11:47:12.812: ERROR/AndroidRuntime(5328):     at java.lang.reflect.Method.invoke(Method.java:521)
04-05 11:47:12.812: ERROR/AndroidRuntime(5328):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:868)
04-05 11:47:12.812: ERROR/AndroidRuntime(5328):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:626)
04-05 11:47:12.812: ERROR/AndroidRuntime(5328):     at dalvik.system.NativeStart.main(Native Method)
04-05 11:47:12.812: ERROR/AndroidRuntime(5328): Caused by: java.util.ConcurrentModificationException
04-05 11:47:12.812: ERROR/AndroidRuntime(5328):     at java.util.ArrayList$ArrayListIterator.next(ArrayList.java:573)
04-05 11:47:12.812: ERROR/AndroidRuntime(5328):     at ntnu.client.MapClient.handleResult(MapClient.java:599)
04-05 11:47:12.812: ERROR/AndroidRuntime(5328):     at ntnu.client.MapClient.onActivityResult(MapClient.java:881)
04-05 11:47:12.812: ERROR/AndroidRuntime(5328):     at android.app.Activity.dispatchActivityResult(Activity.java:3988)
04-05 11:47:12.812: ERROR/AndroidRuntime(5328):     at android.app.ActivityThread.deliverResults(ActivityThread.java:3804)
04-05 11:47:12.812: ERROR/AndroidRuntime(5328):     ... 11 more

Code with handleresult-code is provided below.

  public synchronized void handleResult(boolean notify)
  { 

      if(!citynodes.equals(null) && citynodes.size()>0 )
      {
          noteBaloon.setVisibility(0x00000008);

          Drawable drawable = this.getResources().getDrawable(R.drawable.up);
          Context myContext = this;

          itemizedoverlay = new CitynodeItemizedOverlay(drawable,myContext);
          itemizedoverlay.setThumbsUp(BitmapFactory.decodeResource(
                  getResources(), R.drawable.vote_yes3));  

          itemizedoverlay.setThubmsDown(BitmapFactory.decodeResource(
                  getResources(), R.drawable.vote_no3));  
          itemizedoverlay.addObserver(this);    

          //itemizedoverlay.setDoAnimtation(true);

          RecommendationNotificationOverlay overlay = new RecommendationNotificationOverlay(); 


          for (Recommendation n : citynodes )
          {

              CitynodeOverlayItem cn= n.getNode().getOverlayItem();
              Drawable marker =  this.iconmanager.changeBackground(this.iconmanager.findIcon(n.getNode()),Integer.parseInt(n.getSystemRating()),n.isPersonalized()); 
              //marker.setAlpha(100);
              marker.setBounds(0, 0, marker.getIntrinsicWidth(), marker.getIntrinsicHeight());

              ShapeDrawable l; 

              cn.setMarker(marker); 
              cn.setNode(n);
              itemizedoverlay.addOverlay(cn);

              LayoutInflater inflater = getLayoutInflater();
          }



          for(Overlay i : getMapView().getOverlays() )
          { 
              if((i instanceof ItemizedOverlay)) //|| (i instanceof RecommendationNotificationOverlay) )
                  this.mapView.getOverlays().remove(i); 
          }



          for(Overlay i : getMapView().getOverlays() )
          { 
              if((i instanceof RecommendationNotificationOverlay) )
                  this.mapView.getOverlays().remove(i); 
          }
          List <Recommendation> proactive = new ArrayList<Recommendation>(); 

          for(Recommendation potpro : this.citynodes)
          {
              if(potpro.isProactive())
              {
                  proactive.add(potpro); 

              }

          }

          overlay.setNotifications(proactive); 

          mapOverlays.add(overlay);



          mapOverlays.add(itemizedoverlay);



          mapView.invalidate();

      }

  }  

The code this Exception refers to is the first for-loop:

  for(Overlay i : getMapView().getOverlays() )

The code also throws the same Exception on this line (processing another action):

  if(!citynodes.equals(null) && citynodes.size()>0 )
pecka85
  • 752
  • 7
  • 21
  • can you provide the log of these errors? since upgrading to 2.2 from 1.6 shouldn't be a problem – Aman Alam Apr 05 '11 at 09:45
  • Error is not due to thread.did you getting error in your programs? – Ajay Singh Apr 05 '11 at 10:01
  • Android's always had multithreading, but 2.2 introduced JIT which gives some speed improvements, and may have caused the timings between your threads to change where previously you were lucky enough that it didn't show its head. – Nick Apr 05 '11 at 11:59
  • @Nick Thanks for the heads up. However, I tried to disable JIT (as suggested on this page http://developer.android.com/sdk/android-2.2.html), given that it is correct to use it as this: ` `, but it did not work. I wasn't sure whether it should be 'true' or 'false' so I tried both but none worked. – pecka85 Apr 05 '11 at 13:47
  • I'm not sure why, but by putting the crashing line (`for(Overlay i : getMapView().getOverlays()` ) in try-catch-blocks, everything works fine. That code is used in the code for using Google Maps in the application, maybe some changes there from earlier versions..? – pecka85 Apr 05 '11 at 19:38

3 Answers3

1

Ah, at least one of your problems is nothing to do with multithreading:

for(Overlay i : getMapView().getOverlays() )
{ 
    if (i instanceof ItemizedOverlay)
        this.mapView.getOverlays().remove(i); 
}

You can't modify a collection while you're iterating over it with this style of for-loop. Sometimes the collection implementation won't notice and you'll get away with it, but I suspect the implementation of collections could have been changed at some point in Android which would catch this sort of thing. If you want to remove items while looping, you'll need to get an Iterator and use that to loop, calling remove on the Iterator.

for(Iterator<Overlay> it=getMapView().getOverlays().iterator(); it.hasNext(); )
{
    Overlay i = it.next(); 
    if (i instanceof ItemizedOverlay)
        it.remove(); 
}

Or something to that effect.

Nick
  • 11,475
  • 1
  • 36
  • 47
  • I should have spotted this earlier really - `ConcurrentModificationException` generally means something like this has happened. When two threads modify a collection at the same time you tend to get more confusing exceptions. – Nick Apr 06 '11 at 09:30
0

This is the portion of the error message you are interested in:

04-05 11:47:12.812: ERROR/AndroidRuntime(5328): Caused by: java.util.ConcurrentModificationException
04-05 11:47:12.812: ERROR/AndroidRuntime(5328):     at java.util.ArrayList$ArrayListIterator.next(ArrayList.java:573)
04-05 11:47:12.812: ERROR/AndroidRuntime(5328):     at ntnu.client.MapClient.handleResult(MapClient.java:599)
04-05 11:47:12.812: ERROR/AndroidRuntime(5328):     at ntnu.client.MapClient.onActivityResult(MapClient.java:881)

The error occurs at line 599 of the MapClient class in the handleResult methd. Could you post the code from your handleResult method?

The exception is caused because more than one thread is attempting to modify the ArrayList at the same time.

Joseph Earl
  • 23,351
  • 11
  • 76
  • 89
  • I've looked at that code. It didn't get med anywhere.. But then how is that the application works on 1.6 but not 2.2? It fails everytime in 2.2, and never in 1.6.. – pecka85 Apr 05 '11 at 11:54
0

wrap sychronized([ArrayList]){ } around the areas where you modify the Array content. That way the threads are both able to change the Array content

Mark Mooibroek
  • 7,636
  • 3
  • 32
  • 53
  • I'll try that this afternoon. The method that throws the exception is synchronized (see code above), shouldn't that have taken care of that in such a case? – pecka85 Apr 05 '11 at 11:52
  • @pecka85 - are any of these lists shared between instances of this class? `synchronized` on a method only synchronises on that object instance, so if they share the same data they could still be doing unsafe stuff. – Nick Apr 05 '11 at 11:56
  • @Nick There is only one instance of that class (activity). However, a quite similar class(activity) exist but even without starting this other activity, the main activity crashes.. – pecka85 Apr 05 '11 at 13:42