We're trying to track down a memory leak happening on the GoogleMap in our Android app, which ends in an OOM after about 40-50 device rotations. The map gets set around 3500 markers.
The App has a minSDK of 9 and therefore using the SupportMapFragment from the V4 Support Library.
We've tried multiple things including:
Analyzing the memory dump in MAT shows that we accumulate lots of instances of
com.google.android.gms.location.internal.LocationClientHelper$ListenerTransport
which we have no clue where they are coming from.
Anyone has an idea on what could be the cause of this memory leak?
The following code has already all markes and listeners removed and still leaks. First the base class:
public abstract class BaseMapFragment extends Fragment {
public static final int MENU_ITEM_ID_SEARCH= 102;
public static final int MENU_ITEM_ID_SHOW_LIST= 100;
public static final int ZOOM_LEVEL_DEFAULT= 14;
private static final String SAVED_INSTANCE_LATITUDE= "savedLatitude";
private static final String SAVED_INSTANCE_LONGITUDE= "savedLongitutde";
private static final String SAVED_INSTANCE_ZOOM= "savedZoom";
protected static final String CLASSTAG= BaseMapFragment.class.getSimpleName();
private GoogleMap mMap;
private CameraUpdate mResumeCameraUpdate= null;
private double mSavedLatitude;
private double mSavedLongitude;
private float mSavedZoom;
private static View mView;
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (mMap != null) {
outState.putDouble(SAVED_INSTANCE_LATITUDE, mMap.getCameraPosition().target.latitude);
outState.putDouble(SAVED_INSTANCE_LONGITUDE, mMap.getCameraPosition().target.longitude);
outState.putFloat(SAVED_INSTANCE_ZOOM, mMap.getCameraPosition().zoom);
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
if (savedInstanceState != null) {
mSavedLatitude= savedInstanceState.getDouble(SAVED_INSTANCE_LATITUDE, Constants.EXTRA_VALUE_NONE);
mSavedLongitude= savedInstanceState.getDouble(SAVED_INSTANCE_LONGITUDE, Constants.EXTRA_VALUE_NONE);
mSavedZoom= savedInstanceState.getFloat(SAVED_INSTANCE_ZOOM, Constants.EXTRA_VALUE_NONE);
}
if (mView != null) {
ViewGroup parent= (ViewGroup) mView.getParent();
if (parent != null)
parent.removeView(mView);
}
try {
mView= inflater.inflate(R.layout.map_layout, container, false);
} catch (InflateException e) {
/* map is already there, just return view as it is */
}
return mView;
}
protected GoogleMap initializeMap() {
if (mMap != null) {
if (mSavedLatitude != Constants.EXTRA_VALUE_NONE && mSavedLatitude != 0.0) {
mResumeCameraUpdate= Context.getCamUpdate(mSavedZoom, mSavedLatitude, mSavedLongitude);
} else {
mResumeCameraUpdate= Context.getCamUpdate(mMap.getCameraPosition().zoom, mMap.getCameraPosition().target.latitude, mMap.getCameraPosition().target.longitude);
}
}
SupportMapFragment mapFragment= (SupportMapFragment) getActivity().getSupportFragmentManager().findFragmentById(R.id.map);
if (mapFragment == null) {
mapFragment= (SupportMapFragment) getChildFragmentManager().findFragmentById(R.id.map);
if (mapFragment == null) {
MapsInitializer.initialize(getActivity());
mapFragment= SupportMapFragment.newInstance();
mMap= mapFragment.getMap();
} else {
mMap= mapFragment.getMap();
}
} else {
mMap= mapFragment.getMap();
}
// check if map is created successfully or not
if (mMap == null) {
Toast.makeText(getActivity().getApplicationContext(), R.string.map_create_unable, Toast.LENGTH_SHORT).show();
} else {
mMap.setMyLocationEnabled(true);
mMap.setOnMyLocationButtonClickListener(new OnMyLocationButtonClickListener() {
@Override
public boolean onMyLocationButtonClick() {
if (mMap.getMyLocation() != null) {
CameraUpdate newLatLngZoom= Context.getCamUpdate(ZOOM_LEVEL_DEFAULT, mMap.getMyLocation());
mMap.animateCamera(newLatLngZoom);
} else {
Toast.makeText(getActivity().getApplicationContext(), R.string.map_location_services_disabled, Toast.LENGTH_SHORT).show();
}
return true;
}
});
}
return mMap;
}
}
Subclass
public class MySupportMapFragment extends BaseMapFragment {
private LinearLayout mStaoButtonsLayout;
private ToggleButton mStaoButton;
private ToggleButton mGasStaoButton;
private Boolean mInitialLocationChange;
private CameraUpdate mResumeCameraUpdate;
private GoogleMap mMap;
private double mBundleLatitude;
private double mBundleLongitude;
@Override
public void addRequiredModelClasses(LinkedHashSet<Class<? extends ComergeModel<?>>> set) {
set.add(AboModel.class);
set.add(StationModel.class);
super.addRequiredModelClasses(set);
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putDouble(BUNDLE_EXTRA_CENTER_LATITUDE, mBundleLatitude);
outState.putDouble(BUNDLE_EXTRA_CENTER_LONGITUDE, mBundleLongitude);
}
@Override
public void onActivityCreated(final Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
setHasOptionsMenu(showSearchButton());
final StationModel stationModel= getContext().getModel(StationModel.class);
mStaoButtonsLayout= (LinearLayout) getActivity().findViewById(R.id.mapStaoButtons);
mStaoButtonsLayout.setVisibility(View.VISIBLE);
mStaoButton= (ToggleButton) mStaoButtonsLayout.findViewById(R.id.staoButton);
mStaoButton.setChecked(stationModel.isStationButtonChecked());
mGasStaoButton= (ToggleButton) mStaoButtonsLayout.findViewById(R.id.gasStaoButton);
mGasStaoButton.setChecked(stationModel.isGasStationButtonChecked());
mMap= initializeMap();
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
addSearchButton(menu);
}
}
I had a similar problem before. I added following code to solve my problem:
@Override
public void onDestroy() {
if (mMap != null) {
mMap.setMyLocationEnabled(false);
}
}
It seems that LocationClientHelper$ListenerTransport is related to setMyLocationEnabled()
. I had to unregister some callbacks to prevent memory leak.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With