https://bugs.kde.org/show_bug.cgi?id=520075
Bug ID: 520075
Summary: DeviceFragment leaked via duplicate callback
registration in Device.kt
Classification: Applications
Product: kdeconnect
Version First unspecified
Reported In:
Platform: Android
OS: Android 12.x
Status: REPORTED
Severity: normal
Priority: NOR
Component: android-application
Assignee: [email protected]
Reporter: [email protected]
CC: [email protected]
Target Milestone: ---
Created attachment 192227
--> https://bugs.kde.org/attachment.cgi?id=192227&action=edit
Heap dump: DeviceFragment leak warning + References tab evidence
DESCRIPTION
DeviceFragment is retained in memory after onDestroyView() due to duplicate
registration of pairingCallback and pluginsChangedListener in the underlying
CopyOnWriteArrayList in Device.kt.
When onViewCreated() is invoked more than once on the same Fragment instance
(which can happen during certain fragment transactions, e.g., navigating to
a device, back, then to the same device again), calls to:
device.addPairingCallback(pairingCallback)
device.addPluginsChangedListener(pluginsChangedListener)
append a second entry to the underlying CopyOnWriteArrayList. The matching
remove calls in onDestroyView() only remove the first occurrence (per the
documented semantics of CopyOnWriteArrayList.remove(Object)), leaving the
Fragment retained via the duplicate entry.
STEPS TO REPRODUCE
1. Launch the app with at least one paired device.
2. Tap on the paired device in the navigation drawer to open DeviceFragment.
3. Tap back to return to the device list.
4. Tap on the same device again.
5. Open Android Studio Profiler and capture a heap dump on the running process.
6. Filter the heap dump by "DeviceFragment".
OBSERVED RESULT
Two live instances of DeviceFragment are present in the heap:
- One active instance (with mView and _binding populated).
- One orphaned instance (with mView and _binding null, but with
mSavedViewState populated, confirming onDestroyView was called).
The orphaned instance has 15+ inbound references that prevent garbage
collection, including:
- Multiple synthetic lambda objects (DeviceFragment$$ExternalSyntheticLambda
9 with multiple occurrences) that capture the fragment as f$0 / f$1.
- "Index 0 in Object[]" entries corresponding to the CopyOnWriteArrayList
backing array inside Device (the duplicate callback registration).
- Inner classes (DeviceFragment$pairingCallback$1,
DeviceFragment$menuProvider$1)
via this$0 references.
EXPECTED RESULT
After onDestroyView() completes, only the current active DeviceFragment
instance should remain reachable. Previous instances should be eligible for
garbage collection.
SOFTWARE/OS VERSIONS
KDE Connect Android version: 1.35.2 (from Google Play Store).
Repository revision tested: master branch as of (fecha en que clonaste el
repo).
Device: Xiaomi POCO M6 Pro (model 21061119AL).
Android version: 12 (API level 31).
Build type tested: Debug (org.kde.kdeconnect_tp.debug).
ADDITIONAL INFORMATION
Root cause analysis: CopyOnWriteArrayList.add(E) does not check for
duplicates. CopyOnWriteArrayList.remove(Object) only removes the first
matching entry. The code in DeviceFragment.onViewCreated (lines 222-223 in
src/main/java/org/kde/kdeconnect/ui/DeviceFragment.kt) registers two
callbacks via add(); the corresponding onDestroyView() calls remove(). If
onViewCreated runs twice on the same Fragment instance without an
intermediate onDestroyView, the second add() creates a duplicate that the
single remove() cannot fully clean up.
Proposed fix: defensively call remove() before add() in onViewCreated, so
that a second invocation of onViewCreated does not accumulate duplicate
entries. The fix is local to DeviceFragment.kt; Device.kt does not need to
change. A merge request is being prepared.
See also bug 452410 ("Duplicate notifications using Android version"), which
may be a downstream symptom of the same class of problem (duplicate listener
registration on re-attachment). The reporter of 506728 (duplicate of 452410)
notes that force-stop is required to reproduce, suggesting state-persistence
or re-attachment paths may also be involved. Whether the fix proposed here
also resolves 452410 requires separate investigation in NotificationsPlugin.
Empirical evidence: heap dump captured via Android Studio Profiler
(Analyze Memory Usage task). Screenshots attached showing:
1. The leak warning indicator on DeviceFragment in the class listing.
2. The References tab on the orphaned instance, listing the inbound
references retaining the Fragment.
--
You are receiving this mail because:
You are watching all bug changes.