BottomSheetDialogFragment 内存泄漏排查
·
问题背景:
- 我的Android项目中有一个负责账单编辑的BottomSheetDialogFragment,用户关闭后LeakCanary持续报告内存泄漏。
ViewUI_insets setControl -> ITYPE_STATUS_BAR on com.ftc.testdemoone/leakcanary.internal.activity.LeakActivity ViewUI_insets setControl mType:0, requestedVisible:true, getLeash:Surface(name=Surface(name=396dfa9 StatusBar)/@0xe3e0f92 - animation-leash of insets_animation)/@0x63c0177, needAnimation:false, mIsAnimationPending:false, getRootViewTitle:com.ftc.testdemoone/leakcanary.internal.activity.LeakActivity ViewUI_insets setControl -> ITYPE_IME on com.ftc.testdemoone/leakcanary.internal.activity.LeakActivity ==================================== HEAP ANALYSIS RESULT ==================================== 1 APPLICATION LEAKS References underlined with "~~~" are likely causes. Learn more at https://squ.re/leaks. 2167 bytes retained by leaking objects Signature: 9e25c8983912b18543e8dabae60b670dc1bbec7f ┬─── │ GC Root: Global variable in native code │ ├─ android.database.ContentObserver$Transport instance │ Leaking: UNKNOWN │ Retaining 39.5 kB in 718 objects │ ↓ ContentObserver$Transport.mContentObserver │ ~~~~~~~~~~~~~~~~ ├─ android.app.Dialog$3 instance │ Leaking: UNKNOWN │ Retaining 39.0 kB in 717 objects │ Anonymous subclass of android.database.ContentObserver │ ↓ Dialog$3.this$0 │ ~~~~~~ ├─ com.google.android.material.bottomsheet.BottomSheetDialog instance │ Leaking: UNKNOWN │ Retaining 38.9 kB in 714 objects │ mContext instance of android.view.ContextThemeWrapper, wrapping activity com.ftc.testdemoone.MainActivity with │ mDestroyed = false │ mOwnerActivity instance of com.ftc.testdemoone.MainActivity with mDestroyed = false │ Dialog#mDecor is null │ ↓ Dialog.mCancelMessage │ ~~~~~~~~~~~~~~ ├─ android.os.Message instance │ Leaking: UNKNOWN │ Retaining 88 B in 2 objects │ Message.what = 68 │ Message.when = 0 (461737480 ms before heap dump) │ Message.obj = instance @329333240 of androidx.fragment.app.DialogFragment$2 │ Message.callback = null │ Message.target = instance @329409856 of android.app.Dialog$ListenersHandler │ ↓ Message.obj │ ~~~ ├─ androidx.fragment.app.DialogFragment$2 instance │ Leaking: UNKNOWN │ Retaining 12 B in 1 objects │ Anonymous class implementing android.content.DialogInterface$OnCancelListener │ ↓ DialogFragment$2.this$0 │ ~~~~~~ ╰→ com.ftc.testdemoone.dialog.MyBSDialogFragment instance Leaking: YES (ObjectWatcher was watching this because com.ftc.testdemoone.dialog.MyBSDialogFragment received Fragment#onDestroy() callback. Conflicts with Fragment.mLifecycleRegistry.state is INITIALIZED) Retaining 2.2 kB in 77 objects key = ab08c795-f182-4ec8-b042-a7312e366aba watchDurationMillis = 11665 retainedDurationMillis = 6665 ==================================== 0 LIBRARY LEAKS A Library Leak is a leak caused by a known bug in 3rd party code that you do not have control over. See https://square.github.io/leakcanary/fundamentals-how-leakcanary-works/#4-categorizing-leaks ==================================== 3 UNREACHABLE OBJECTS An unreachable object is still in memory but LeakCanary could not find a strong reference path from GC roots. androidx.lifecycle.SavedStateHandlesVM instance Leaking: YES (ObjectWatcher was watching this because androidx.lifecycle.SavedStateHandlesVM received ViewModel#onCleared() callback) key = e518484a-bd2d-4a25-aabe-939ace0d7720 watchDurationMillis = 11667 retainedDurationMillis = 6667 leakcanary.internal.ViewModelClearedWatcher instance Leaking: YES (ObjectWatcher was watching this because leakcanary.internal.ViewModelClearedWatcher received ViewModel#onCleared() callback) key = 642b0f1e-a006-426c-837d-b87849349774 watchDurationMillis = 11667 retainedDurationMillis = 6666 androidx.loader.app.LoaderManagerImpl$LoaderViewModel instance Leaking: YES (ObjectWatcher was watching this because androidx.loader.app.LoaderManagerImpl$LoaderViewModel received ViewModel#onCleared() callback) key = bcd2fcd1-a7f9-4ec4-8bc5-c533d1c3f287 watchDurationMillis = 11666 retainedDurationMillis = 6666 ==================================== METADATA Please include this in bug reports and Stack Overflow questions. Build.VERSION.SDK_INT: 31 Build.MANUFACTURER: HUAWEI LeakCanary version: 2.14 App process name: com.ftc.testdemoone Class count: 24604 Instance count: 133623 Primitive array count: 108494 Object array count: 20291 Thread count: 24 Heap total bytes: 22713271 Bitmap count: 1 Bitmap total bytes: 51193 Large bitmap count: 0 Large bitmap total bytes: 0 Stats: LruCache[maxSize=3000,hits=41317,misses=86094,hitRate=32%] RandomAccess[bytes=4230551,reads=86094,travel=72296035371,range=27195979,size=34289438] Analysis duration: 4672 ms Heap dump file path: /storage/emulated/0/Download/leakcanary-com.ftc.testdemoone/2026-06-20_18-02-54_987.hprof Heap dump timestamp: 1781949782904 Heap dump duration: Unknown ====================================
尝试的修复:
- 简单过了一边代码,以为自己onDestroyView做的不完全或外部使用了强引用。
- 修正、删掉部分业务代码,仍然泄漏。
- 创建一个什么都不做的空白BottomSheetDialogFragment,仍然泄露。
- 中间对各个回调进行监听、尝试不同阶段调用dismiss()\dismissAllowingStateLoss(),都不起作用。
- 测试时偶然发现在AS内虚拟机测试不发生内存泄漏。
线下机器:
| 项目型号 | HUAWEI ABR-AL00 |
| SoC | Snapdragon 888 (SM8350) |
| Android | 12 (API 31), 安全补丁 2022-08-01 |
| 系统 | EMUI 14.2.0, 内核 5.4.86 |
| ROM | ABR-AL00 104.2.0.186C00 |
求助方向:
1. 是否有绕过 Dialog.dismissDialog() guard 的方法?
2. fragment-ktx / material 更高版本是否解决了此问题?
3. 除放弃 BottomSheetDialogFragment 外,还有什么应用层 workaround?
更多推荐



所有评论(0)