6

I'm using the play services v9.8.0 (without location service permissions) and am still facing a leak when I use a MapView in a dialog fragment. I'm using it like in my code example and I use it to display a location only and I DON'T have setMyLocationEnabled (as I don't even have the permissions for this setting).

Does anyone see a problem in my code? I'm getting a leak like the one here: MapView v2 keeping Context around. I do following:

  • create a dialog
  • replace a view in my layout with a MapView (because I allow to use static maps as well, so my default view is a ImageView in my layout, that will be replaced with a MapView)

Then it happens that my fragments leaks MapView.mContext...

Code - Dialog Fragment

public class DialogMediaDetails extends DialogFragment
{
    private GoogleMap mGoogleMap = null;
    private MapView mMapView = null;

    @Override
    public final Dialog onCreateDialog(Bundle savedInstanceState)
    {
        Dialog dlg = ...; // create dialog
        View view = ...; // get view from dialog
        Location location = ...; // defined location
        initGoogleMap();
        return dlg;
    }

    private void initGoogleMap(Location location)
    {
        mMapView = new MapView(getActivity());
        MapsInitializer.initialize(getActivity());
        // Updates the location and zoom of the MapView
        mMapView.onCreate(null);
        mMapView.getMapAsync(new OnMapReadyCallback()
        {
            @Override
            public void onMapReady(GoogleMap googleMap)
            {
                LatLng coordinates = new LatLng(location.getLatitude(), location.getLongitude());
                googleMap.addMarker(new MarkerOptions().position(coordinates));
                googleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(coordinates, 15));
                mGoogleMap = googleMap;
                mMapView.onResume();
            }
        });
        mMapView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener()
        {
            @Override
            public void onGlobalLayout()
            {
                mMapView.getViewTreeObserver().removeOnGlobalLayoutListener(this);

                // MapView is scrollable, so we disable dragging
                CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams) appBarLayout.getLayoutParams();
                AppBarLayout.Behavior behavior = (AppBarLayout.Behavior) params.getBehavior();
                behavior.setDragCallback(new AppBarLayout.Behavior.DragCallback() {
                    @Override
                    public boolean canDrag(AppBarLayout appBarLayout) {
                        return false;
                    }
                });
            }
        });

        replaceHeader(mMapView);
    }

    private void replaceHeader(View view)
    {
        ViewGroup parent = (ViewGroup) pbHeader.getParent();
        int index = parent.indexOfChild(pbHeader);
        ViewGroup.LayoutParams lp = pbHeader.getLayoutParams();
        parent.removeView(pbHeader);
        parent.addView(view, index, lp);
    }

    // ----------------------------------------
    // forward all lifecycle events to MapView
    // ----------------------------------------

    @Override
    public void onResume() {
        super.onResume();
        if (mMapView != null)
            mMapView.onResume();
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        if (mMapView != null)
            mMapView.onSaveInstanceState(outState);
    }

    @Override
    public void onPause() {
        super.onPause();
        if (mMapView != null)
            mMapView.onPause();
    }

    @Override
    public void onLowMemory() {
        super.onLowMemory();
        if (mMapView != null)
            mMapView.onLowMemory();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        if (mMapView != null)
            mMapView.onDestroy();
        mMapView = null;
    }
}
Community
  • 1
  • 1
prom85
  • 16,896
  • 17
  • 122
  • 242

2 Answers2

1

There are some issues reported about leaks with MapView. You may try calling googleMap.setMyLocationEnabled(false); in onDestroy to prevent the leak from happening. Failure to call either MapView.onDestroy or GoogleMap.setMyLocationEnabled(false) will cause a leak. Here's a related thread which might help.

Community
  • 1
  • 1
abielita
  • 13,147
  • 2
  • 17
  • 59
  • Calling `setLocationEnabled` needs location permission and my app does not have it so I can't enable it nor disable it... so this is no solution for now for me... – prom85 Nov 17 '16 at 16:08
-1

I had the same issue, and realized in my heap reference tree that the bug is coming from the lite mode part of the library. Try not using lite mode and adding all the corresponding life cycles for mapview.

sufeiz
  • 1
  • 1
  • 1
    Can you explain what you mean with lite mode? In my example I'm adding all life cycle events already I think. Or am I missing something? – prom85 Mar 10 '17 at 20:40
  • @prom85 [Lite mode doc](https://developers.google.com/maps/documentation/android-api/lite) It's a feature that displays just a bitmap on your fragment of the location you specify. Check your fragment xml if it has map:liteMode="true" – sufeiz Mar 13 '17 at 21:19