Skip to content

fix(messaging,android): fix an issue that could cause duplicate call stack - #18122

Merged
Lyokone merged 9 commits into
mainfrom
fix/17163
Aug 4, 2026
Merged

Lyokone merged 9 commits into
mainfrom
fix/17163

Conversation

@Lyokone

@Lyokone Lyokone commented Mar 19, 2026

Copy link
Copy Markdown
Contributor

Description

When FirebaseMessaging.onBackgroundMessage() was called on Android, the plugin eagerly started a background Dart isolate (FlutterEngine) at registration time, causing a duplicate main in the call stack and running app initialization twice. This fix defers isolate creation to when a background message actually arrives and the FlutterFirebaseMessagingBackgroundService is started.

Related Issues

Checklist

  • I read the Contributor Guide and followed the process outlined there for submitting PRs.
  • My PR includes unit or integration tests for all changed/updated/fixed behaviors (See Contributor Guide).
  • All existing and new tests are passing.
  • I updated/added relevant documentation (doc comments with ///).
  • The analyzer (melos run analyze) does not report any problems on my PR.
  • I read and followed the Flutter Style Guide.
  • I signed the CLA.
  • I am willing to follow-up on review comments in a timely manner.

Breaking Change

  • Yes, this is a breaking change.
  • No, this is not a breaking change.

@gemini-code-assist

Copy link
Copy Markdown
Contributor
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Lyokone added 6 commits August 4, 2026 10:31
Only conflict was the explanatory comment above the
Messaging#startBackgroundIsolate case. Kept this branch's updated wording
(the eager start it described is gone) at main's reindented column, since
main reformatted that block from 8 to 6 spaces.
…kground executor

Removing the eager background-isolate start left FlutterShellArgs always
null: the only remaining caller is the lazy path in
FlutterFirebaseMessagingBackgroundService.onCreate(), which goes through
the no-arg startBackgroundIsolate() and passed null explicitly.

Collapses the shellArgs parameter and its unreachable branch, and drops
the two imports that became unused.

This does not reopen #4078. That was a ClassCastException from casting
FlutterFragmentActivity to FlutterActivity to reach getFlutterShellArgs();
#4341 fixed it by switching to FlutterShellArgs.fromIntent(). With the
activity no longer consulted at all, the cast that caused #4078 is gone.
The minSdk = 23 -> flutter.minSdkVersion change was unrelated to the
duplicate-call-stack fix, and it breaks the example on the oldest Flutter
this package supports. pubspec declares flutter: '>=3.27.0', where
FlutterExtension.minSdkVersion is 21, while firebase_messaging's
local-config.gradle sets minSdk=23 -- so the manifest merger fails with
"minSdkVersion 21 cannot be smaller than version 23 declared in library".

CI only runs stable (currently 3.44.6, where the default is 24), so the
break would not show up here. Nine other example apps hardcode
minSdk = 23 for the same reason.
…olate keeps them

The eager background-isolate start was the only place with access to an
activity, so removing it left the background engine with no shell args at
all. Instead of dropping them, capture them at registration -- where an
activity is still available -- and persist them next to the callback
handles already stored in SharedPreferences. The lazy start in
FlutterFirebaseMessagingBackgroundService.onCreate() then restores them.

This is strictly wider coverage than before: previously the killed-app
path (the common background case) already started the isolate through the
no-arg overload with null args, so shell args only applied when an
activity happened to be alive. Now they apply in both paths.

Stored as a JSON array rather than a String set because arg order is
significant. org.json is already used by FlutterFirebaseMessagingStore.
Passing null or empty clears the stored value, so args cannot go stale
across a launch that has no activity.
…anifest flags

Reverts the SharedPreferences plumbing added to carry FlutterShellArgs
into the lazily-started background isolate, and documents the forward
path instead.

FlutterShellArgs is @deprecated upstream with a TODO to delete it once
engine args via Intent are unsupported (flutter/flutter#180686). Its
documented replacement, FlutterEngineFlags, reads flags from
<application> metadata in AndroidManifest.xml -- and FlutterLoader
applies those inside ensureInitializationComplete, which this executor
already calls. So manifest-declared flags reach the background engine
with no plugin code at all.

FlutterEngineFlags cannot be referenced directly: it first shipped in
Flutter 3.44.0, while this package supports flutter: '>=3.27.0'. Naming
it would break compilation on 3.27 through 3.43, and CI only runs stable
so that would not be caught here.

Net effect: no deprecated API, less code, and engine flags handled by the
mechanism Flutter is standardising on.
The background isolate runs in its own FlutterEngine started from a
service, so command-line engine flags never reached it, and the
intent-based mechanism that partially covered this is deprecated upstream
(flutter/flutter#180686).

Documents the supported replacement: <application> metadata in
AndroidManifest.xml, which FlutterLoader applies to every engine it
initializes. Notes the Flutter 3.44.0 floor, the mandatory
io.flutter.embedding.android. prefix, that most flags are ignored in
release builds, and that the command line wins over the manifest.
@Lyokone
Lyokone merged commit 1522bd8 into main Aug 4, 2026
42 of 46 checks passed
@Lyokone
Lyokone deleted the fix/17163 branch August 4, 2026 11:43
@firebase firebase locked and limited conversation to collaborators Sep 3, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[firebase_messaging] Unexpected Duplicate main in Call Stack on Android when Using FirebaseMessaging.onBackgroundMessage

3 participants