问题背景:

- 我的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?

Logo

脑启社区是一个专注类脑智能领域的开发者社区。欢迎加入社区,共建类脑智能生态。社区为开发者提供了丰富的开源类脑工具软件、类脑算法模型及数据集、类脑知识库、类脑技术培训课程以及类脑应用案例等资源。

更多推荐