Android Canary 泄漏 - 计费和 ViewBinding
Android Canary leak - Billing and ViewBinding
我将 LeakCanary 导入到我的项目中,以了解我内部有哪些内存泄漏以及有多少内存泄漏,然后能够修复它们。我注意到这两个我无法修复。更好地说,我不知道如何用代码来解决它们。有人知道如何给我一些建议吗?
LeakCanary: 2 APPLICATION LEAKS
LeakCanary: References underlined with "~~~" are likely causes.
LeakCanary: Learn more at https://squ.re/leaks.
LeakCanary: 16523886 bytes retained by leaking objects
LeakCanary: Signature: 4594f3337285a2a3dd854a7bf9c944f5598ae18b
LeakCanary: ┬───
LeakCanary: │ GC Root: Global variable in native code
LeakCanary: │
LeakCanary: ├─ android.app.LoadedApk$ServiceDispatcher$DeathMonitor instance
LeakCanary: │ Leaking: UNKNOWN
LeakCanary: │ Retaining 144 bytes in 4 objects
LeakCanary: │ ↓ LoadedApk$ServiceDispatcher$DeathMonitor.this[=11=]
LeakCanary: │ ~~~~~~
LeakCanary: ├─ android.app.LoadedApk$ServiceDispatcher instance
LeakCanary: │ Leaking: UNKNOWN
LeakCanary: │ Retaining 622 bytes in 8 objects
LeakCanary: │ mContext instance of com.example.packagename.ApplicationContext
LeakCanary: │ ↓ LoadedApk$ServiceDispatcher.mConnection
LeakCanary: │ ~~~~~~~~~~~
LeakCanary: ├─ com.android.billingclient.api.BillingClientImpl$zza instance
LeakCanary: │ Leaking: UNKNOWN
LeakCanary: │ Retaining 41 bytes in 3 objects
LeakCanary: │ ↓ BillingClientImpl$zza.zzd
LeakCanary: │ ~~~
LeakCanary: ├─ com.example.packagename.fragments.ShopFragment$setupBillingClient instance
LeakCanary: │ Leaking: UNKNOWN
LeakCanary: │ Retaining 12 bytes in 1 objects
LeakCanary: │ Anonymous class implementing com.android.billingclient.api.BillingClientStateListener
LeakCanary: │ ↓ ShopFragment$setupBillingClient.this[=11=]
LeakCanary: │ ~~~~~~
LeakCanary: ╰→ com.example.packagename.fragments.ShopFragment instance
LeakCanary: Leaking: YES (ObjectWatcher was watching this because com.example.packagename.fragments.ShopFragment received
LeakCanary: Fragment#onDestroy() callback and Fragment#mFragmentManager is null)
LeakCanary: Retaining 16523886 bytes in 33489 objects
LeakCanary: key = b2ebfeff-4177-4587-b7f5-e4c89bc4afaa
LeakCanary: watchDurationMillis = 40425
LeakCanary: retainedDurationMillis = 35424
LeakCanary: 2804 bytes retained by leaking objects
LeakCanary: Signature: 71c80305d50d16354a6bec910e7f529c72e4296
LeakCanary: ┬───
LeakCanary: │ GC Root: Local variable in native code
LeakCanary: │
LeakCanary: ├─ android.os.HandlerThread instance
LeakCanary: │ Leaking: NO (PathClassLoader↓ is not leaking)
LeakCanary: │ Thread name: 'queued-work-looper'
LeakCanary: │ ↓ HandlerThread.contextClassLoader
LeakCanary: ├─ dalvik.system.PathClassLoader instance
LeakCanary: │ Leaking: NO (ViewDataBinding↓ is not leaking and A ClassLoader is never leaking)
LeakCanary: │ ↓ PathClassLoader.runtimeInternalObjects
LeakCanary: ├─ java.lang.Object[] array
LeakCanary: │ Leaking: NO (ViewDataBinding↓ is not leaking)
LeakCanary: │ ↓ Object[].[881]
LeakCanary: ├─ androidx.databinding.ViewDataBinding class
LeakCanary: │ Leaking: NO (a class is never leaking)
LeakCanary: │ ↓ static ViewDataBinding.sReferenceQueue
LeakCanary: │ ~~~~~~~~~~~~~~~
LeakCanary: ├─ java.lang.ref.ReferenceQueue instance
LeakCanary: │ Leaking: UNKNOWN
LeakCanary: │ Retaining 724 bytes in 30 objects
LeakCanary: │ ↓ ReferenceQueue.head
LeakCanary: │ ~~~~
LeakCanary: ├─ androidx.databinding.ViewDataBinding$WeakListener instance
LeakCanary: │ Leaking: UNKNOWN
LeakCanary: │ Retaining 52 bytes in 2 objects
LeakCanary: │ ↓ ViewDataBinding$WeakListener.mObservable
LeakCanary: │ ~~~~~~~~~~~
LeakCanary: ├─ androidx.databinding.ViewDataBinding$LiveDataListener instance
LeakCanary: │ Leaking: UNKNOWN
LeakCanary: │ Retaining 16 bytes in 1 objects
LeakCanary: │ ↓ ViewDataBinding$LiveDataListener.mLifecycleOwner
LeakCanary: │ ~~~~~~~~~~~~~~~
LeakCanary: ╰→ com.example.packagename.fragments.InventoryFragment instance
LeakCanary: Leaking: YES (ObjectWatcher was watching this because com.example.packagename.fragments.InventoryFragment
LeakCanary: received Fragment#onDestroy() callback and Fragment#mFragmentManager is null)
如您所见,主要有 2 个漏洞,但我不确定我是否知道如何修复它们。
第一个 leaktrace 显示 ShopFragment
在 ShopFragment$setupBillingClient
中设置了一个 BillingClientStateListener
,可能是通过调用 BillingClient#startConnection()
。匿名 class 中的 BillingClientStateListener
实现并引用其外部 class、ShopFragment
的实例。查看 Javadoc, it seems that you need to call BillingClient#endConnection()
once you're done, i.e. when the fragment is destroyed. The billing documentation 根本没有提到这一点,因此您可能想要提交文档错误。
第二个泄漏跟踪显示与被销毁的 InventoryFragment 关联的 ViewDataBinding.LiveDataListener
由 ViewDataBinding.sReferenceQueue
保存在内存中(参见 sources here。事实上 ViewDataBinding$WeakListener
在队列意味着 ViewDataBinding.WeakListener
不是强可达的。ViewDataBinding. processReferenceQueue()
负责这样做,所以看起来它已经有一段时间没有被调用了。如果没有堆转储,很难确定,但这看起来像是一个 DataBinding 库错误,您应该提交一个工单。
我将 LeakCanary 导入到我的项目中,以了解我内部有哪些内存泄漏以及有多少内存泄漏,然后能够修复它们。我注意到这两个我无法修复。更好地说,我不知道如何用代码来解决它们。有人知道如何给我一些建议吗?
LeakCanary: 2 APPLICATION LEAKS
LeakCanary: References underlined with "~~~" are likely causes.
LeakCanary: Learn more at https://squ.re/leaks.
LeakCanary: 16523886 bytes retained by leaking objects
LeakCanary: Signature: 4594f3337285a2a3dd854a7bf9c944f5598ae18b
LeakCanary: ┬───
LeakCanary: │ GC Root: Global variable in native code
LeakCanary: │
LeakCanary: ├─ android.app.LoadedApk$ServiceDispatcher$DeathMonitor instance
LeakCanary: │ Leaking: UNKNOWN
LeakCanary: │ Retaining 144 bytes in 4 objects
LeakCanary: │ ↓ LoadedApk$ServiceDispatcher$DeathMonitor.this[=11=]
LeakCanary: │ ~~~~~~
LeakCanary: ├─ android.app.LoadedApk$ServiceDispatcher instance
LeakCanary: │ Leaking: UNKNOWN
LeakCanary: │ Retaining 622 bytes in 8 objects
LeakCanary: │ mContext instance of com.example.packagename.ApplicationContext
LeakCanary: │ ↓ LoadedApk$ServiceDispatcher.mConnection
LeakCanary: │ ~~~~~~~~~~~
LeakCanary: ├─ com.android.billingclient.api.BillingClientImpl$zza instance
LeakCanary: │ Leaking: UNKNOWN
LeakCanary: │ Retaining 41 bytes in 3 objects
LeakCanary: │ ↓ BillingClientImpl$zza.zzd
LeakCanary: │ ~~~
LeakCanary: ├─ com.example.packagename.fragments.ShopFragment$setupBillingClient instance
LeakCanary: │ Leaking: UNKNOWN
LeakCanary: │ Retaining 12 bytes in 1 objects
LeakCanary: │ Anonymous class implementing com.android.billingclient.api.BillingClientStateListener
LeakCanary: │ ↓ ShopFragment$setupBillingClient.this[=11=]
LeakCanary: │ ~~~~~~
LeakCanary: ╰→ com.example.packagename.fragments.ShopFragment instance
LeakCanary: Leaking: YES (ObjectWatcher was watching this because com.example.packagename.fragments.ShopFragment received
LeakCanary: Fragment#onDestroy() callback and Fragment#mFragmentManager is null)
LeakCanary: Retaining 16523886 bytes in 33489 objects
LeakCanary: key = b2ebfeff-4177-4587-b7f5-e4c89bc4afaa
LeakCanary: watchDurationMillis = 40425
LeakCanary: retainedDurationMillis = 35424
LeakCanary: 2804 bytes retained by leaking objects
LeakCanary: Signature: 71c80305d50d16354a6bec910e7f529c72e4296
LeakCanary: ┬───
LeakCanary: │ GC Root: Local variable in native code
LeakCanary: │
LeakCanary: ├─ android.os.HandlerThread instance
LeakCanary: │ Leaking: NO (PathClassLoader↓ is not leaking)
LeakCanary: │ Thread name: 'queued-work-looper'
LeakCanary: │ ↓ HandlerThread.contextClassLoader
LeakCanary: ├─ dalvik.system.PathClassLoader instance
LeakCanary: │ Leaking: NO (ViewDataBinding↓ is not leaking and A ClassLoader is never leaking)
LeakCanary: │ ↓ PathClassLoader.runtimeInternalObjects
LeakCanary: ├─ java.lang.Object[] array
LeakCanary: │ Leaking: NO (ViewDataBinding↓ is not leaking)
LeakCanary: │ ↓ Object[].[881]
LeakCanary: ├─ androidx.databinding.ViewDataBinding class
LeakCanary: │ Leaking: NO (a class is never leaking)
LeakCanary: │ ↓ static ViewDataBinding.sReferenceQueue
LeakCanary: │ ~~~~~~~~~~~~~~~
LeakCanary: ├─ java.lang.ref.ReferenceQueue instance
LeakCanary: │ Leaking: UNKNOWN
LeakCanary: │ Retaining 724 bytes in 30 objects
LeakCanary: │ ↓ ReferenceQueue.head
LeakCanary: │ ~~~~
LeakCanary: ├─ androidx.databinding.ViewDataBinding$WeakListener instance
LeakCanary: │ Leaking: UNKNOWN
LeakCanary: │ Retaining 52 bytes in 2 objects
LeakCanary: │ ↓ ViewDataBinding$WeakListener.mObservable
LeakCanary: │ ~~~~~~~~~~~
LeakCanary: ├─ androidx.databinding.ViewDataBinding$LiveDataListener instance
LeakCanary: │ Leaking: UNKNOWN
LeakCanary: │ Retaining 16 bytes in 1 objects
LeakCanary: │ ↓ ViewDataBinding$LiveDataListener.mLifecycleOwner
LeakCanary: │ ~~~~~~~~~~~~~~~
LeakCanary: ╰→ com.example.packagename.fragments.InventoryFragment instance
LeakCanary: Leaking: YES (ObjectWatcher was watching this because com.example.packagename.fragments.InventoryFragment
LeakCanary: received Fragment#onDestroy() callback and Fragment#mFragmentManager is null)
如您所见,主要有 2 个漏洞,但我不确定我是否知道如何修复它们。
第一个 leaktrace 显示 ShopFragment
在 ShopFragment$setupBillingClient
中设置了一个 BillingClientStateListener
,可能是通过调用 BillingClient#startConnection()
。匿名 class 中的 BillingClientStateListener
实现并引用其外部 class、ShopFragment
的实例。查看 Javadoc, it seems that you need to call BillingClient#endConnection()
once you're done, i.e. when the fragment is destroyed. The billing documentation 根本没有提到这一点,因此您可能想要提交文档错误。
第二个泄漏跟踪显示与被销毁的 InventoryFragment 关联的 ViewDataBinding.LiveDataListener
由 ViewDataBinding.sReferenceQueue
保存在内存中(参见 sources here。事实上 ViewDataBinding$WeakListener
在队列意味着 ViewDataBinding.WeakListener
不是强可达的。ViewDataBinding. processReferenceQueue()
负责这样做,所以看起来它已经有一段时间没有被调用了。如果没有堆转储,很难确定,但这看起来像是一个 DataBinding 库错误,您应该提交一个工单。