Android Maps V2 LocationClientHelper Memory Leak Vulnerability

We are trying to track the memory leak in GoogleMap in our Android application, which ends in OOM after about 40-50 revolutions of the device. The card will be installed around 3,500 markers.

The application has a minSDK of 9 and therefore uses the SupportMapFragment from the V4 support library.

We tried several things, including:

  • LatLng Caching
  • CameraUpdates Caching
  • Removing Markers from a Map
  • Removing listeners from the card
  • Removing all listeners, markers, etc., so that we have a simple map.
  • Google Play Services Library Update
  • Support Library Update

Analysis of the memory dump in MAT shows that we accumulate many instances of com.google.android.gms.location.internal.LocationClientHelper$ListenerTransport about which we do not know where they came from.

Does anyone have an idea what could be causing this memory leak?

In the following code, all tags and listeners are already deleted and are still leaking. First 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); } } 
+5
source share
1 answer

I had a similar problem. I added the following code to solve my problem:

 @Override public void onDestroy() { if (mMap != null) { mMap.setMyLocationEnabled(false); } } 

LocationClientHelper $ ListenerTransport seems to be associated with setMyLocationEnabled() . I had to unregister some callbacks to prevent a memory leak.

+9
source

All Articles