[Bug][Android][4.0.1] Flattened <image> lifecycle: destroyImage/clearNodeIndexImageMap remove managers without calling LynxImageManager.destroy()
System Info
System:
- Platform: Android
- Device used for observation: Samsung SM-A9080
Binaries / packages:
- org.lynxsdk.lynx:lynx:4.0.1 (release)
- org.lynxsdk.lynx:lynx:4.0.1-dev (debug)
- org.lynxsdk.lynx:lynx-base:4.0.1 / 4.0.1-dev
- org.lynxsdk.lynx:lynx-jssdk:4.0.1
- org.lynxsdk.lynx:lynx-service-image:4.0.1
Integration notes:
- Native Android integration using LynxView.
- We inspected the resolved 4.0.1 AAR bytecode and added local diagnostic/lifecycle instrumentation for investigation.
Details
Summary
While investigating Native Heap growth in a long-running Android Lynx course page, we found two image-manager lifecycle paths in the resolved Lynx 4.0.1 AAR that remove image managers without explicitly calling LynxImageManager.destroy().
We would like to confirm whether this is expected behavior or an Android-side lifecycle bug, and whether it has already been fixed in Lynx 4.1.x.
This report is specifically about deterministic release of image-manager resources. It does not claim that this is the only source of our overall Native Heap growth; a separate root-layout loop is still under investigation.
Observed paths in 4.0.1
1. PlatformRendererContext.destroyImage(int)
The method obtains/removes a LynxImageManager from the root UIBodyView through:
rootView.obtainImageAccordingToNodeIndex(imageKey)
but the original 4.0.1 implementation does not subsequently invoke:
imageManager.destroy()
Our local investigation indicates that LynxImageManager.destroy() is the explicit image-resource release path, including the image/Fresco reference cleanup owned by the manager.
2. UIBody.UIBodyView.clearNodeIndexImageMap()
The method clears mImageMap directly, without first iterating over the retained LynxImageManager values and calling destroy() on each one.
Local diagnostic patch used for verification
For investigation only, we applied the following narrow lifecycle patch to the resolved 4.0.1 AAR:
PlatformRendererContext.destroyImage(int):
LynxImageManager manager = rootView.obtainImageAccordingToNodeIndex(imageKey);
if (manager != null) {
manager.destroy();
}UIBody.UIBodyView.clearNodeIndexImageMap():
for (LynxImageManager manager : mImageMap.values()) {
if (manager != null) {
manager.destroy();
}
}
mImageMap.clear();The patch preserves the original map-removal/clear behavior; it only adds the manager lifecycle release before the existing removal is completed.
Why we believe this deserves confirmation
In our course-page scenario, Native Heap grows during repeated page/image transitions and a large portion is released only when the full LynxView / Activity is destroyed. The bitmap cache itself remained bounded, so we were looking for image objects whose explicit native resource release might be skipped while their Java-side bookkeeping is removed.
The two paths above are candidates for delayed resource release because the manager is removed/cleared but its explicit destroy() lifecycle method is not reached.
Questions
- Is it intentional that
PlatformRendererContext.destroyImage(int)does not callLynxImageManager.destroy()afterobtainImageAccordingToNodeIndex(...)? - Is it intentional that
UIBody.UIBodyView.clearNodeIndexImageMap()clears image managers without callingdestroy()first? - If another native or Java lifecycle path is expected to release these managers, could you point us to it? We would like to verify the ownership contract.
- Has either path changed in Lynx 4.1.x? If so, which version contains the fix?
- If this is a valid bug, would a fix equivalent to the two snippets above be acceptable upstream?
We can provide sanitized bytecode diffs, logs, and a minimized reproducer if needed.
Reproduce link
No response
Reproduce Steps
- Use Lynx Android 4.0.1 with a page containing flattened
<image>elements. - Render images, then repeatedly remove image nodes / switch course pages while keeping the same LynxView alive.
- Exercise the Android image-destruction and root-image-map cleanup paths.
- Inspect the resolved 4.0.1 AAR implementation of
PlatformRendererContext.destroyImage(int)andUIBody.UIBodyView.clearNodeIndexImageMap(). - Observe that image-manager bookkeeping is removed or cleared without an explicit
LynxImageManager.destroy()invocation.
Our production bundle is private. We are preparing a sanitized minimal reproduction based on the Lynx repro template if maintainers need one.
Source: lynx-family/lynx