mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-10-05 19:28:25 +00:00
A full-screen napplet/browser surface ran onDestroy cleanly and was gone from
ActivityManager, yet the :napplet process kept the Activity, its window and its
WebView alive through repeated forced GCs. A heap dump gives the chain:
ROOT(JNI_GLOBAL) android.os.Handler$MessengerImpl
-> MessengerImpl.this$0 = android.os.Handler
-> Handler.mCallback = <lambda>
-> lambda.f$0 = NappletBrowserActivity
`replyMessenger = Messenger(Handler(mainLooper, ::onBrokerReply))` makes the
Activity the handler's callback (a bound method reference captures `this`), and
a Messenger sent over IPC is a binder — so while the broker holds it, ART keeps
a JNI global reference to that Handler here in the sandbox. One retained
Messenger therefore pinned Activity -> PhoneWindow -> DecorView -> WebView, and
no GC in the sandbox could reclaim it; only killing the process could.
The broker keeps replyTo in long-lived structures (incBus subscriptions,
liveSubscriptions, identityWatch, foregroundLeases) and onDestroy only called
unbindService, which releases none of them.
Fix both halves:
- MSG_RELEASE_CLIENT, sent first thing in onDestroy, so the broker drops the
Messenger's inc-bus subscriptions and this surface's foreground lease. It
goes directly on brokerMessenger rather than through sendToBroker, which
queues while unbound — a queued release would never be sent.
- The reply handler now holds the Activity through a WeakReference, so even a
broker that never processes the release cannot pin a surface again.
Verified on an emulator: opening one full-screen page and pressing back left
Activities:1 WebViews:2 across three forced GCs before, and settles to
Activities:0 WebViews:1 after. Heap dump: NappletBrowserActivity instances drop
from 13 (the leaked Activity plus its captured lambdas) to 1 — the Companion,
which is a static singleton and correctly retained.
Note it takes two GC cycles to settle; one of the reference paths runs through
a Cleaner chain, so a single forced GC still shows the old numbers.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>