Skip to content

Android: clear stale callback registrations on hot restart - #22

Closed
Colton127 wants to merge 1 commit into
mainfrom
fix/android-hot-restart-callbacks
Closed

Android: clear stale callback registrations on hot restart#22
Colton127 wants to merge 1 commit into
mainfrom
fix/android-hot-restart-callbacks

Conversation

@Colton127

Copy link
Copy Markdown
Owner

Summary

onDetachedFromEngine does not fire on hot restart. The FlutterEngine and its id are unchanged while the Dart isolate is replaced, so every registered NativeCallable silently goes stale with no notification — and invoking a callable whose isolate is gone is undefined behaviour.

Approach

The plugin registers a FlutterEngine.EngineLifecycleListener and clears the registrations in onPreEngineRestart().

onEngineWillDestroy() clears too — it fires just before the plugin registry is destroyed, while the engine is still valid, so it is a slightly earlier point than the detach hook. The listener is removed on detach.

Only the callback bridges are cleared here, not the whole engine: after a hot restart the new isolate's init() finds the native engine still initialized and deinits it itself.

Verification

Compiled under -Xlint:all against stubs mirroring the real embedding API, and the JNI symbol name diffed against javac -h output rather than checked by eye (the flutter_soloud package underscore mangles to flutter_1soloud).

flutter analyze and flutter test unchanged.

Ordering

Based on #21, not main — the diff shown here is only this change. Merge #21 first.

Uses the existing nativeClearDartCallbackRegistrationsForEngine native function; no C++ change.


Generated by Claude Code

onDetachedFromEngine does not fire on hot restart. The FlutterEngine and its
id are unchanged while the Dart isolate is replaced, so every registered
NativeCallable silently goes stale with no notification -- and invoking a
callable whose isolate is gone is undefined behaviour.

The plugin now registers a FlutterEngine.EngineLifecycleListener and clears
the registrations in onPreEngineRestart(). onEngineWillDestroy() clears too;
it fires just before the plugin registry is destroyed, while the engine is
still valid, so it is a slightly earlier point than the detach hook. The
listener is removed on detach.

Only the callback bridges are cleared here, not the whole engine: after a hot
restart the new isolate's init() finds the native engine still initialized
and deinits it itself.

Compiled under -Xlint:all against stubs mirroring the real embedding API, and
the JNI symbol name diffed against javac -h output.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Base automatically changed from fix/android-no-startup-work to main July 26, 2026 01:22
@Colton127 Colton127 closed this Jul 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants