如何修复框架布局中的内存泄漏问题?

fai*_*iry 6 android google-maps google-maps-android-api-2 leakcanary

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

我该如何修复这个泄漏?参考我的泄漏金丝雀报告。 FrameLayout 泄露

 ????
    ? 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
    
   
Run Code Online (Sandbox Code Playgroud)

这是我的 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>
Run Code Online (Sandbox Code Playgroud)

这是我的片段 片段:

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
    }
}
Run Code Online (Sandbox Code Playgroud)

………………

  • Build.VERSION.SDK_INT:25

  • Build.MANUFACTURER: 三星

  • LeakCanary 版本:2.7

  • 统计数据:LruCache[maxSize=3000,hits=5750,misses=80700,hitRate=6%] RandomAccess[bytes=4089892,reads=80700,travel=60892211355,range=18101017,84212]

  • 堆转储原因:用户请求

  • 分析持续时间: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" />
Run Code Online (Sandbox Code Playgroud)

Mar*_*ler 1

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

然后尝试创建private lateinit var mMap: GoogleMap一个局部变量...
不需要保留,因为将获得实例OnMapReadyCallback

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

我想人们必须看看这android.widget.FrameLayout到底是什么(后来的 API 提供了一个布局检查器 - 然而在 API 25 上,仍然可以将渲染的布局转储到文件中)。