片段内存泄漏中的MapView

时间:2017-10-12 10:44:03

标签: android google-maps android-fragments memory-leaks

我已经像这样添加了Map to Fragment

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();
}

}

我从这篇文章中得到了这个想法

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

它正在工作,但我遇到的问题是内存泄漏。我已经制作了第二个片段(用addToBackStack(null)取代(替换不添加))只有视图和一个带manager.popBackStackImmediate()的按钮,我正在用Canary Leak测试它(没有捕获任何东西)和Android Profiler 。每次我去那个片段并返回地图片段内存上升大约10MB。

为了测试我移除了MapView并且交换片段在返回时没有增加内存,所以我可以说问题出在MapView上。

我还尝试将mapView.onDestroy()放入onDestroyView()(此处也添加了mapView = null),就像有人在该帖子的评论中所说的那样,这确实有所帮助,而且记忆力没有得到满足交换(它仍然是,但它每次我交换时它不是1 - 5 MB)。但问题是当我交换碎片20次以上应用程序崩溃并且我收到错误

  

java.lang.NullPointerException:尝试获取null数组的长度   java.nio.DirectByteBuffer.put(DirectByteBuffer.java:385)

所以我去搜索造成这种情况的原因,我发现了几个关于此的帖子(只会发布一个)

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

我在那里尝试了解决方案但它没有工作(我仍然得到这个错误)。但正如我所看到的,有些人仍在经历这一点。我也读过其他帖子,但没有找到我的案例。现在我的问题。

  1. 我在这里缺少什么?我应该如何在没有“膨胀”(保持对MapView的引用)内存的情况下交换片段。

  2. 我可以在Activity中创建MapView并在此Fragment中引用它(这样它不会每次都重新创建),因为我的应用程序中的所有内容都围绕着它

  3. 将地图放在一个活动中,将其余部分的碎片放在其他活动中(所有碎片都以某种方式与地图进行通信,这就是为什么我这样做的原因)

  4. 也许我做错了,可能有更好的方法吗? (不确定)

2 个答案:

答案 0 :(得分:6)

好的,我想出了问题是什么(我想我做了)因为我不再在地图上看到泄漏了。

我将此添加到片段onDestroyView

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

我在帖子中读到googleMap.clear()和MapView onDestroy()应该在onDestroyView()而不是在onDestroy()方法中。摆脱了错误,但替换片段的内存问题仍然存在。一旦我添加mMapView = null从我可以告诉哪里没有任何泄漏(我仍然会更好地调试这个,如果我发现任何新的,我将在这里发布)。

答案 1 :(得分:1)

交换片段的最佳方法是附加分离

 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;
        }
    }

这是我最近在Xamarin android中做到的 这段代码在一个活动里面,我需要两个标签,所以我用setCurrentTabFragment()在两者之间交换 您可以轻松地将其转换为Java Android。

以及内存泄漏问题 在onLowMemory()中使用java系统垃圾收集器同样在你的片段的onDestroy()中使用GoogleMapsObject.Clear()清除你的googlemaps对象但是要确保你调用这个clear()方法,你不需要不再映射因此,所有标记创建的位图缓存也会被它清除

好运!