如何修复帧布局中的内存泄漏问题



我在android应用程序中使用leakanary,它检测到FrameLayout Leaked。但我找不到或无法解决这个问题,

我该如何修复这个漏洞?参考我的泄漏报告。帧布局泄漏

┬───
│ GC Root: System class
│
├─ leakcanary.internal.InternalLeakCanary class
│    Leaking: NO (MainActivity↓ is not leaking and a class is never leaking)
│    ↓ static InternalLeakCanary.resumedActivity
├─ com.android.zigmaster.MainActivity instance
│    Leaking: NO (MapViewFragment↓ is not leaking and Activity#mDestroyed
│    is false)
│    mApplication instance of com.android.zigmaster.MyApplication
│    mBase instance of androidx.appcompat.view.ContextThemeWrapper
│    ↓ ComponentActivity.mActivityResultRegistry
├─ androidx.activity.ComponentActivity$2 instance
│    Leaking: NO (MapViewFragment↓ is not leaking)
│    Anonymous subclass of androidx.activity.result.ActivityResultRegistry
│    this$0 instance of com.android.zigmaster.MainActivity with mDestroyed =
│    false
│    ↓ ActivityResultRegistry.mKeyToCallback
├─ java.util.HashMap instance
│    Leaking: NO (MapViewFragment↓ is not leaking)
│    ↓ HashMap.table
├─ java.util.HashMap$HashMapEntry[] array
│    Leaking: NO (MapViewFragment↓ is not leaking)
│    ↓ HashMap$HashMapEntry[].[4]
├─ java.util.HashMap$HashMapEntry instance
│    Leaking: NO (MapViewFragment↓ is not leaking)
│    ↓ HashMap$HashMapEntry.value
├─ androidx.activity.result.ActivityResultRegistry$CallbackAndContract instance
│    Leaking: NO (MapViewFragment↓ is not leaking)
│    ↓ ActivityResultRegistry$CallbackAndContract.mCallback
├─ androidx.fragment.app.FragmentManager$10 instance
│    Leaking: NO (MapViewFragment↓ is not leaking)
│    Anonymous class implementing androidx.activity.result.
│    ActivityResultCallback
│    ↓ FragmentManager$10.this$0
├─ androidx.fragment.app.FragmentManagerImpl instance
│    Leaking: NO (MapViewFragment↓ is not leaking)
│    ↓ FragmentManager.mParent
├─ com.android.zigmaster.ui.trips.FragmentTripPlanner instance
│    Leaking: NO (Fragment#mFragmentManager is not null)
│    ↓ FragmentTripPlanner.mMap
│                          ~~~~
├─ com.google.android.gms.maps.GoogleMap instance
│    Leaking: UNKNOWN
│    Retaining 765.4 kB in 10828 objects
│    ↓ GoogleMap.zzg
│                ~~~
├─ com.google.android.gms.maps.internal.zzg instance
│    Leaking: UNKNOWN
│    Retaining 142 B in 2 objects
│    ↓ zza.zza
│          ~~~
├─ com.google.maps.api.android.lib6.impl.bn instance
│    Leaking: UNKNOWN
│    Retaining 765.0 kB in 10821 objects
│    A instance of com.android.zigmaster.MyApplication
│    ↓ bn.w
│         ~
├─ android.widget.FrameLayout instance
│    Leaking: UNKNOWN
│    Retaining 2.9 kB in 65 objects
│    View not part of a window view hierarchy
│    View.mAttachInfo is null (view detached)
│    View.mWindowAttachCount = 1
│    mContext instance of com.android.zigmaster.MyApplication
│    ↓ View.mParent
│           ~~~~~~~
╰→ android.widget.FrameLayout instance
​     Leaking: YES (ObjectWatcher was watching this because com.google.android.
​     gms.maps.SupportMapFragment received Fragment#onDestroyView() callback
​     (references to its views should be cleared to prevent leaks))
​     Retaining 1.9 kB in 48 objects
​     key = 265fafb3-b2a3-4462-a4e7-d5cc2afbc6fe
​     watchDurationMillis = 57439
​     retainedDurationMillis = 52422
​     View not part of a window view hierarchy
​     View.mAttachInfo is null (view detached)
​     View.mWindowAttachCount = 1
​     mContext instance of com.android.zigmaster.MainActivity with
​     mDestroyed = false


这是我的xml代码:XML:

<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.trips.FragmentTripPlanner">

<androidx.fragment.app.FragmentContainerView
android:id="@+id/map"
android:name="com.google.android.gms.maps.SupportMapFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>

这是我的碎片片段:

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import com.google.android.gms.maps.CameraUpdate
import com.google.android.gms.maps.CameraUpdateFactory
import com.google.android.gms.maps.GoogleMap
import com.google.android.gms.maps.SupportMapFragment
import com.google.android.gms.maps.model.LatLng

class MapViewFragment : Fragment() {
private lateinit var mMap: GoogleMap
private var userLatitude     = 38.2500486
private var userLongitude    = -85.7647484
private lateinit var mapFragment : SupportMapFragment
private var binding : MapViewFragmentBinding ?=null
private fun zoomingLocation(): CameraUpdate {
return CameraUpdateFactory.newLatLngZoom(LatLng(userLatitude, userLongitude), 14f)
}
private fun configActivityMaps(googleMap: GoogleMap): GoogleMap {
// set map type
googleMap.mapType = GoogleMap.MAP_TYPE_NORMAL
// Enable / Disable zooming controls
googleMap.uiSettings.isZoomControlsEnabled = false
// Enable / Disable Compass icon
googleMap.uiSettings.isCompassEnabled = true
// Enable / Disable Rotate gesture
googleMap.uiSettings.isRotateGesturesEnabled = true
// Enable / Disable zooming functionality
googleMap.uiSettings.isZoomGesturesEnabled = true
googleMap.uiSettings.isScrollGesturesEnabled = true
googleMap.uiSettings.isMapToolbarEnabled = true
return googleMap
}

override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
binding = MapViewFragmentBinding.inflate(inflater)
mapFragment = (childFragmentManager.findFragmentById(R.map.id) as SupportMapFragment?)!!
mapFragment.getMapAsync { googleMap ->
mMap = configActivityMaps(googleMap)
mMap.moveCamera(zoomingLocation())
}
return binding!!.root
}
override fun onDestroyView() {
super.onDestroyView()

mapFragment =null
binding=null
}
}

  • Build.VERSION.SDK_INT:25

  • 制造商:三星

  • LeakCanary版本:2.7

  • 统计数据:LruCache[maxSize=3000,命中率=5750,未命中率=80700,命中率=6%]RandomAccess[字节=4089892,读取=80700,行程=60892211355,范围=18101017,大小=22288421]

  • 堆转储原因:用户请求

  • 分析持续时间:29922毫秒

已尝试:相同问题

import androidx.fragment.app.Fragment
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.google.android.gms.maps.CameraUpdateFactory
import com.google.android.gms.maps.OnMapReadyCallback
import com.google.android.gms.maps.SupportMapFragment
import com.google.android.gms.maps.model.LatLng
import com.google.android.gms.maps.model.MarkerOptions
class MapsFragment : Fragment() {
private val callback = OnMapReadyCallback { googleMap ->
val sydney = LatLng(-34.0, 151.0)
googleMap.addMarker(MarkerOptions().position(sydney).title("Marker in Sydney"))
googleMap.moveCamera(CameraUpdateFactory.newLatLng(sydney))
}
override fun onCreateView(inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_maps, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val mapFragment = childFragmentManager.findFragmentById(R.id.map) as SupportMapFragment?
mapFragment?.getMapAsync(callback)
}
}
<?xml version="1.0" encoding="utf-8"?>
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/map"
android:name="com.google.android.gms.maps.SupportMapFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.pref.MapsFragment" />

首先,删除@SuppressLint("RestrictedApi")并修复隐藏在那里的内容。

然后尝试使private lateinit var mMap: GoogleMap成为局部变量
不需要保持,因为会得到实例OnMapReadyCallback

也许最后尝试调用super.onDestroyView(),因为FrameLayout既不是层次结构的一部分,也不是附加的。。。并且附加计数为1,因此它必须先前已附加。您很有可能尝试处理super.onDestroyView()已经处理的内容,而整个覆盖可能毫无意义。

我猜你必须看看这个android.widget.FrameLayout到底是什么(后来API提供了一个布局检查器——尽管如此,在API 25上,仍然可以将渲染的布局转储到文件中(。

在某些情况下,内存泄漏是不可避免的——例如,在GoogleMaps的这种情况下——你总是会有来自GoogleMaps片段的内存泄漏——这就是它的工作原理。不过,它比以前好多了——在2013年之前,谷歌地图泄露了整个地图缓存,这太可怕了。

无论哪种方式,这都是一个已知的问题,但仍未解决。如果泄漏发生在谷歌提供的图书馆内,我建议不要麻烦。通常,它们都是已知的,如果它们是严重的——固定的,或者是轻微的——很高兴被遗忘。

尝试使用MapView而不是MapFragment/SupportMapFragment。MapView扩展了FrameLayout,并针对不仅需要在设备屏幕上显示地图,还需要在其他控件上显示的情况进行定向,并为现代平台设计,还可以以其他方式实现。

此外,如果您只需要为用户显示地图,您可以尝试LiteMode或静态地图API。

最新更新