Android 如何解决框架布局中的内存泄漏问题?
我在android应用程序中使用leakcanary,它检测到FrameLayout泄漏。但我找不到或解决不了这个问题 我怎样才能修补这个漏洞?参考我的泄漏报告。 框架布局泄漏Android 如何解决框架布局中的内存泄漏问题?,android,google-maps,google-maps-android-api-2,leakcanary,Android,Google Maps,Google Maps Android Api 2,Leakcanary,我在android应用程序中使用leakcanary,它检测到FrameLayout泄漏。但我找不到或解决不了这个问题 我怎样才能修补这个漏洞?参考我的泄漏报告。 框架布局泄漏 ┬─── │ GC Root: System class │ ├─ leakcanary.internal.InternalLeakCanary class │ Leaking: NO (MainActivity↓ is not leaking and a class is nev
┬───
│ 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
- 制造商:三星
- 金丝雀版本:2.7
- 统计:LruCache[maxSize=3000,命中率=5750,未命中率=80700,命中率=6%] 随机访问[字节=4089892,读取=80700,行程=6089221355,范围=18101017,大小=22 288421]
- 堆转储原因:用户请求
- 分析持续时间:29922毫秒
导入androidx.fragment.app.fragment
导入android.os.Bundle
导入android.view.LayoutInflater
导入android.view.view
导入android.view.ViewGroup
导入com.google.android.gms.maps.CameraUpdateFactory
导入com.google.android.gms.maps.OnMapReadyCallback
导入com.google.android.gms.maps.SupportMapFragment
导入com.google.android.gms.maps.model.LatLng
导入com.google.android.gms.maps.model.MarkerOptions
类MapsFragment:Fragment(){
private val callback=OnMapReadyCallback{googleMap->
val sydney=LatLng(-34.0151.0)
googleMap.addMarker(MarkerOptions().position(sydney.title)(“悉尼的标记”))
谷歌地图。移动摄像机(CameraUpdateFactory。纽拉廷(悉尼))
}
override fun onCreateView(充气机:布局充气机,
容器:视图组?,
savedInstanceState:捆绑?:查看{
返回充气机。充气(R.layout.fragment_图,容器,假)
}
覆盖已创建的视图(视图:视图,保存状态:捆绑?){
super.onViewCreated(视图,savedInstanceState)
val mapFragment=childFragmentManager.findFragmentById(R.id.map)是否作为SupportMapFragment?
mapFragment?.getMapAsync(回调)
}
}
首先,移除@suppressint(“RestrictedApi”)
并修复隐藏在那里的内容
然后尝试将private lateinit var mMap:GoogleMap
设置为局部变量…不需要保留,因为您将在MapReadyCallback上获得实例
最后尝试调用super.onDestroyView()
,因为FrameLayout
既不是层次结构的一部分,也不是附加的。。。并且附加计数为1,因此它以前必须已附加。您很有可能尝试处理已由super.onDestroyView()
处理的内容,而整个覆盖可能毫无意义
我猜你必须看看这个android.widget.FrameLayout是什么(后来的API有一个布局检查器-不过在API 25上,仍然可以将渲染的布局转储到文件中)。在某些情况下,内存泄漏是不可避免的,例如,在这种情况下,谷歌地图-你总是会有一个内存泄漏谷歌地图片段-这就是它的工作原理。不过,这比以前好多了——在2013年之前,谷歌地图覆盖了整个地图缓存,这太可怕了
不管怎样,这都是一个问题,至今仍未解决。如果泄漏发生在谷歌提供的库中,我建议不要麻烦。通常,它们都是已知的,如果它们是严重的-固定的,或者如果它们是轻微的-很高兴地被遗忘。尝试使用而不是/。MapView扩展了FrameLayout,并针对不仅需要在设备屏幕上显示地图,还需要在设备屏幕上显示其他控件的情况进行定向,并针对现代平台进行设计,还可以以其他方式实现
此外,如果您只需要为用户显示地图,您可以尝试或。onDestroyView
尝试通过使字段为空来分配mapFragment=null
,并使用WeakReference
作为mMap fieldmapFragment=null(不工作)的类型,请与我分享如何使用“WeakReference”这可能是:WeakReference
,但我不认为可空性是问题所在。mapsFragment.onDestroy()-在我筛选RotateSame问题时仍然会重复泄漏,看起来谷歌已经确认这是一个bug。本期由八位开发者主演,因此您可能想就此发表评论,询问是否有人(包括谷歌)有解决问题的方法等待修复。已尝试使用局部变量、已移除受限注释和已移除onDestroyView-仍会发生内存泄漏
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" />