Reputation: 135
So i am porting an App i made for iOS a couple of months back to Android. This app has a database of a few hundred (341) points that can be shown on a map. When doing this on iOS i experienced no preformance issues when adding these points to my map. Shure if the user zoomed out so that all the points where visible at once you could notice some slowdown but nothing major. On Android on the other hand this is excrutiatingly slow to the point of freezing. It is also slower on the AVD than on the phone(HTC Hero).
I would like to point out that i am a newcommer to java but i have experience in C/C++/OBJ-C. I have fidled around a bit with my code and used some simple preformance measurments and come to some surprising conclusions about my code.
This is my function in my MapActivity for adding Overlays to my map i have left some commented out code in there wich i will explain.
private void addOverlaysToMapFromManagedInterestPoints() {
this.mapOverlays = myMap.getOverlays();
GeoPoint point;
OverlayItem overlayitem;
for( managed_interest_point mip : managedObjectManager.interestPoints )
{
TypeOfPoint type = getEnumForTypeOfPoint(mip.ZTYPEOFPOINT);
if(type!=null)
{
switch (type) {
case kAnimalHospital:
point = new GeoPoint(degreesToMicrodegreesConversion(mip.ZLATITUDE),degreesToMicrodegreesConversion(mip.ZLONGITUDE));
overlayitem = new OverlayItem(point, mip.ZTITLE, mip.ZSUBTITLE);
//this.animalHospitalOverlay.addOverlay(overlayitem);
AnnotationsOverlay testOverlay = new AnnotationsOverlay(this.animalHospitalPin,this);
testOverlay.addOverlay(overlayitem);
Log.d("MainActivity","added animalHospital");
break;
case kStore:
point = new GeoPoint(degreesToMicrodegreesConversion(mip.ZLATITUDE),degreesToMicrodegreesConversion(mip.ZLONGITUDE));
overlayitem = new OverlayItem(point, mip.ZTITLE, mip.ZSUBTITLE);
//this.storesOverlay.addOverlay(overlayitem);
AnnotationsOverlay testOverlay2 = new AnnotationsOverlay(storePin,this);
testOverlay2.addOverlay(overlayitem);
this.mapOverlays.add(testOverlay2);
Log.d("MainActivity","added Store point");
break;
case kVeterinary:
point = new GeoPoint(degreesToMicrodegreesConversion(mip.ZLATITUDE),degreesToMicrodegreesConversion(mip.ZLONGITUDE));
overlayitem = new OverlayItem(point, mip.ZTITLE, mip.ZSUBTITLE);
//this.storesOverlay.addOverlay(overlayitem);
AnnotationsOverlay testOverlay3 = new AnnotationsOverlay(this.veterinaryPin,this);
testOverlay3.addOverlay(overlayitem);
this.mapOverlays.add(testOverlay3);
Log.d("MainActivity","added veterinary point");
break;
default:
Log.d("MainActivity", "unknown enum");
break;
}//end switch
}//end if
}//end foreach
//this.mapOverlays.add(this.storesOverlay);
//this.mapOverlays.add(this.veterinariesOverlay);
//this.mapOverlays.add(this.animalHospitalOverlay);
Log.d("MainActivity","end of foreach in add overlays to map ");
}
And this is the code for the AnnotationsOverlay my subclass for ItemizedOverlay.
public class AnnotationsOverlay extends ItemizedOverlay {
private ArrayList<OverlayItem> mOverlays = new ArrayList<OverlayItem>();
Context mContext;
public AnnotationsOverlay(Drawable defaultMarker) {
super(boundCenterBottom(defaultMarker));
}
public AnnotationsOverlay(Drawable defaultMarker,Context context)
{
super(boundCenterBottom(defaultMarker));
mContext=context;
}
@Override
protected OverlayItem createItem(int i) {
return mOverlays.get(i);
}
@Override
public int size() {
return mOverlays.size();
}
//show calloutAssesory view tapped för oss iPhöne människor
@Override
protected boolean onTap(int index) {
OverlayItem item = mOverlays.get(index);
AlertDialog.Builder dialog = new AlertDialog.Builder(mContext);
dialog.setTitle(item.getTitle());
dialog.setMessage(item.getSnippet());
dialog.show();
return true;
}
public void addOverlay(OverlayItem overlay)
{
mOverlays.add(overlay);
populate();
}
}
My first instinct was that allocating the AnnotationsOverlay inside the loop would be a bad thing since the seem to work as a collection of some sort to which yo should add multiple items.
This assumtion proved wrong since each subsequent call to add() took longer and longer time. I put this allocation inside the loop and now this function executes in acceptable time. All the point show up on the map but it is super slow (2 fps possibly less). I think it is weird that each call to add also calls the populate(); function. I have tried removing that and writing a public method that wraps populate that i call only after adding all the items, but that just does not feel rigth. Writing public methods to wrap private ones is generally not a good idea. Also it gives me strange behavior and all the Overlays get the same image if i do this.
So what am i doing wrong , or is it just a fact that Google Maps API on Android is a lot less flexible than on iOS?
Upvotes: 3
Views: 3284
Reputation: 6968
Adding
mapOverlays.clear();
Straight after
mapOverlays.add(itemizedOverlay);
Sped the performance of my app up quite a bit.
Upvotes: 0
Reputation: 135
I am making some progress here that might be useful to some of you reading this question. 1. I have now moved my populate method out from my add method and wrapped it in public method so that I can call this only once after I have populated the entire array of OverlayItems. Also I tried running the app without the debugger ie just running the app on the phone and it works smoothly then. This is not the only time that I have noticed the debugger slowing things down by a factor of 10-20 or more. I had similar problems in the past where running in debug mode slows down some operations by extreme amounts of time.
The new code looks like this:
private void addOverlaysToMapFromManagedInterestPoints() {
this.mapOverlays = myMap.getOverlays();
GeoPoint point;
OverlayItem overlayitem;
for( managed_interest_point mip : managedObjectManager.interestPoints )
{
if(mip==null)
{
Log.d("MainActivity","this should not happen!");
}
TypeOfPoint type = getEnumForTypeOfPoint(mip.ZTYPEOFPOINT);
if(type!=null)
{
switch (type) {
case kAnimalHospital:
point = new GeoPoint(degreesToMicrodegreesConversion(mip.ZLATITUDE),degreesToMicrodegreesConversion(mip.ZLONGITUDE));
overlayitem = new OverlayItem(point, mip.ZTITLE, mip.ZSUBTITLE);
this.animalHospitalOverlay.addOverlay(overlayitem);
Log.d("MainActivity","added animalHospital");
break;
case kStore:
point = new GeoPoint(degreesToMicrodegreesConversion(mip.ZLATITUDE),degreesToMicrodegreesConversion(mip.ZLONGITUDE));
overlayitem = new OverlayItem(point, mip.ZTITLE, mip.ZSUBTITLE);
this.storesOverlay.addOverlay(overlayitem);
Log.d("MainActivity","added Store point");
break;
case kVeterinary:
point = new GeoPoint(degreesToMicrodegreesConversion(mip.ZLATITUDE),degreesToMicrodegreesConversion(mip.ZLONGITUDE));
overlayitem = new OverlayItem(point, mip.ZTITLE, mip.ZSUBTITLE);
this.veterinariesOverlay.addOverlay(overlayitem);
Log.d("MainActivity","added veterinary point");
break;
default:
Log.d("MainActivity", "unknown enum");
break;
}//end switch
}//end if
}//end foreach
this.storesOverlay.callToPopulate();
this.veterinariesOverlay.callToPopulate();
this.animalHospitalOverlay.callToPopulate();
this.mapOverlays.add(this.storesOverlay);
this.mapOverlays.add(this.veterinariesOverlay);
this.mapOverlays.add(this.animalHospitalOverlay);
Log.d("MainActivity","end of foreach in add overlays to map ");
}
public class AnnotationsOverlay extends ItemizedOverlay {
private ArrayList<OverlayItem> mOverlays = new ArrayList<OverlayItem>();
Context mContext;
public AnnotationsOverlay(Drawable defaultMarker) {
super(boundCenterBottom(defaultMarker));
}
public AnnotationsOverlay(Drawable defaultMarker,Context context)
{
super(boundCenterBottom(defaultMarker));
mContext=context;
}
@Override
protected OverlayItem createItem(int i) {
return mOverlays.get(i);
}
@Override
public int size() {
return mOverlays.size();
}
//show calloutAssesory view tapped för oss iPhöne människor
@Override
protected boolean onTap(int index) {
OverlayItem item = mOverlays.get(index);
AlertDialog.Builder dialog = new AlertDialog.Builder(mContext);
dialog.setTitle(item.getTitle());
dialog.setMessage(item.getSnippet());
dialog.show();
return true;
}
public void addOverlay(OverlayItem overlay)
{
mOverlays.add(overlay);
//populate();
}
public void callToPopulate()
{
populate();
}
}
Upvotes: 3