2

I have added Map to Fragment like this

public class SomeFragment extends Fragment {

MapView mapView;
GoogleMap map;

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View v = inflater.inflate(R.layout.some_layout, container, false);

    // Gets the MapView from the XML layout and creates it
    mapView = (MapView) v.findViewById(R.id.mapview);
    mapView.onCreate(savedInstanceState);

    // Gets to GoogleMap from the MapView and does initialization stuff
    map = mapView.getMap();
    map.getUiSettings().setMyLocationButtonEnabled(false);
    map.setMyLocationEnabled(true);

    // Needs to call MapsInitializer before doing any CameraUpdateFactory calls
    try {
        MapsInitializer.initialize(this.getActivity());
    } catch (GooglePlayServicesNotAvailableException e) {
        e.printStackTrace();
    }

    // Updates the location and zoom of the MapView
    CameraUpdate cameraUpdate = CameraUpdateFactory.newLatLngZoom(new LatLng(43.1, -87.9), 10);
    map.animateCamera(cameraUpdate);

    return v;
}

@Override
public void onResume() {
    mapView.onResume();
    super.onResume();
}

@Override
public void onDestroy() {
    super.onDestroy();
    mapView.onDestroy();
}

@Override
public void onLowMemory() {
    super.onLowMemory();
    mapView.onLowMemory();
}

}

I got the idea from this post

https://stackoverflow.com/a/26174573/1393695

And it is working, but the problem i am having is memory leak. I have made second Fragment (replacing (replacing not adding) it with addToBackStack(null)) with just view and a button with manager.popBackStackImmediate() and i was testing it with Canary Leak (didn't catch anything) and Android Profiler. Every time i go to that fragment and return to map fragment memory rose by around 10MB.

For the sake of testing i removed MapView and the swapping fragments didn't increase in memory when coming back, so i can say that the problem is with MapView.

I also tried to put mapView.onDestroy() in onDestroyView() (also added mapView = null here) as someone said in the comments of that post, that did some help and memory was not getting filled when swapping (it still was, but it wasnt as bad 1 - 5 MB everytime i did swap). But problem with this was when i swapped Fragments 20+ times app crashed and i got error

java.lang.NullPointerException: Attempt to get length of null array at java.nio.DirectByteBuffer.put(DirectByteBuffer.java:385)

So i went to search what is causing this and i found couple of posts about this (will only post one)

Why google map v2 suddenly crashed by java.lang.NullPointerException: Attempt to get length of null array?

i tried the solution there and it didn't work (im still getting this error). But as i see some people are still experiencing this. I also read other posts but didn't find anything for my case. So now for my questions.

  1. What am i missing here? How should i swap fragments without "bloating" (keeping references to MapView) memory.

  2. Could i maybe create MapView in Activity and reference it in this Fragment (so that it wont recreate everytime), since everything in my app revolves around it

  3. Put map in one activity and rest of Fragments in other Activity ( All fragments are communicating with map in some way that is why i done it like this for start)

  4. Maybe i am doing this wrong, and there might be a better way? (not sure)

MePo
  • 1,044
  • 2
  • 23
  • 48

2 Answers2

14

Ok, i figured out what was the problem (i think i did) as i am not seeing leaks on the map anymore.

I added this to the fragment onDestroyView

@Override
public void onDestroyView() {
    googleMap.clear()
    mMapView?.onDestroy()
    mMapView = null
    super.onDestroyView()
}

I have read in one of the post that googleMap.clear() and MapView onDestroy() should be in onDestroyView() and not in onDestroy() method because that callback is not called. That got rid of the error but the memory problem in replacing fragments still existed. Once i added mMapView = null there from what i could tell there where not any leaks(i will still better debug this and if i find anything new i will post here).

MePo
  • 1,044
  • 2
  • 23
  • 48
  • 2
    Has similar code inside `onDestroy()`, moved it to `onDestroyView()`. Now, when swapping two map fragments, memory usages drops of about 50% for a split second and then goes back to a similar value as it was before fragment swapping, so I assume it's solved. – fillobotto Mar 07 '18 at 18:03
1

The Best way to swap fragments is to attach detach it

 public void replaceFragment(Fragment fragment)
    {
        fm = FragmentManager;
        ft = fm.BeginTransaction();
        if (fragment == MapsFragment)
        {
            if (MapsFragment == null)
            {
                MapsFragment = new PoSLocationMapView(this);
                ft.Add(Resource.Id.frame_container, MapsFragment, "MapsFragment");

            }
            else
            {
                ft.Detach(FragmentManager.FindFragmentByTag("ListFragment"));
                ft.Attach(MapsFragment);
            }
        }
        else if (fragment == ListFragment)
        {
            if (ListFragment == null)
            {
                ListFragment = new ServicePointDHLList(this);
                ft.Add(Resource.Id.frame_container, ListFragment, "ListFragment");
            }
            else
            {
                ft.Detach(FragmentManager.FindFragmentByTag("MapsFragment"));
                ft.Attach(ListFragment);
            }
        }
        ft.SetTransition(FragmentTransit.FragmentOpen);
        ft.Commit();

    }
    private void setCurrentTabFragment(int tabPosition)
    {
        switch (tabPosition)
        {
            case 0:
                replaceFragment(MapsFragment);
                break;
            case 1:
                replaceFragment(ListFragment);
                break;
        }
    }

This is how i did it in Xamarin android recently this code is inside an activity i needed two tabs so i used setCurrentTabFragment() to swap between the Two You can easily convert the same to Java Android.

And for the Memory Leak Problem use the java system garbage collector in onLowMemory() Also Clear your googlemaps object using GoogleMapsObject.Clear() in onDestroy() of your Fragment But see to it that you call this clear() method where ever you don't need the maps anymore So that the cache of bitmap that is created thanks to all the markers is also cleared with It

GoodLuck!

FreakyAli
  • 13,349
  • 3
  • 23
  • 63