Skip to content

[firebase_app_installations] Android ID-change callback throws on a background thread #18646

Description

@youngminz

Is there an existing issue for this?

  • I have searched the existing issues.

Which plugins are affected?

App Installations

Which platforms are affected?

Android

Description

On a fresh install, firebase_app_installations calls EventSink.success() from a Firebase background thread, causing a main-thread exception at TokenChannelStreamHandler.kt:25.

Reproduced in debug and release with the attached minimal project, which uses only Firebase Core and Installations.

Expected: ID-change events are delivered without a thread exception.

Environment: Samsung SM-S936N / Android 16; Dart 3.13.2; AGP 9.1.0 / Gradle 9.3.1.

Disclaimer: This issue report was written by GPT-6 Astra.

Reproducing the issue

  1. Register a Firebase Android app for dev.example.installations_thread_mrp.
  2. Create .local/firebase.json from config/firebase.example.json and fill in your Firebase configuration.
  3. Uninstall any previous copy of the MRP, then run:
fvm flutter run --debug --dart-define-from-file=.local/firebase.json

The app subscribes to onIdChange and calls getId() followed by getToken() immediately after Firebase initialization. Leave it untouched and inspect adb logcat: getId() completes, then the native exception occurs.

Core sequence from the MRP (UI and logging omitted; firebaseOptions contains your Firebase app configuration):

await Firebase.initializeApp(options: firebaseOptions);

final installations = FirebaseInstallations.instance;
_subscription = installations.onIdChange.listen((_) {});

// No delay after initialization.
await installations.getId();
await installations.getToken();

flutterfire-installations-mrp.zip

Firebase Core version

4.14.0

Flutter Version

3.47.2

Relevant Log Output

java.lang.RuntimeException: Methods marked with @UiThread must be executed on the main thread. Current thread: Firebase Blocking Thread #0
	at io.flutter.embedding.engine.FlutterJNI.ensureRunningOnMainThread(FlutterJNI.java:1695)
	at io.flutter.embedding.engine.FlutterJNI.dispatchPlatformMessage(FlutterJNI.java:1177)
	at io.flutter.embedding.engine.dart.DartMessenger.send(DartMessenger.java:275)
	at io.flutter.embedding.engine.dart.DartExecutor$DefaultBinaryMessenger.send(DartExecutor.java:457)
	at io.flutter.embedding.engine.dart.DartExecutor.send(DartExecutor.java:217)
	at io.flutter.plugin.common.EventChannel$IncomingStreamRequestHandler$EventSinkImplementation.success(EventChannel.java:251)
	at io.flutter.plugins.firebase.installations.firebase_app_installations.TokenChannelStreamHandler.createTokenEventListener$lambda$0(TokenChannelStreamHandler.kt:25)
	at io.flutter.plugins.firebase.installations.firebase_app_installations.TokenChannelStreamHandler$$ExternalSyntheticLambda0.onFidChanged(D8$$SyntheticClass:0)
	at com.google.firebase.installations.FirebaseInstallations.checkAndUpdateFidListeners(FirebaseInstallations.java:454)
	at com.google.firebase.installations.FirebaseInstallations.doNetworkCallIfNecessary(FirebaseInstallations.java:413)
	at com.google.firebase.installations.FirebaseInstallations.lambda$doRegistrationOrRefresh$3$com-google-firebase-installations-FirebaseInstallations(FirebaseInstallations.java:382)
	at com.google.firebase.installations.FirebaseInstallations$$ExternalSyntheticLambda1.run(D8$$SyntheticClass:0)
	at com.google.firebase.concurrent.SequentialExecutor$1.run(SequentialExecutor.java:117)
	at com.google.firebase.concurrent.SequentialExecutor$QueueWorker.workOnQueue(SequentialExecutor.java:229)
	at com.google.firebase.concurrent.SequentialExecutor$QueueWorker.run(SequentialExecutor.java:174)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1100)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at com.google.firebase.concurrent.CustomThreadFactory.lambda$newThread$0$com-google-firebase-concurrent-CustomThreadFactory(CustomThreadFactory.java:47)
	at com.google.firebase.concurrent.CustomThreadFactory$$ExternalSyntheticLambda0.run(D8$$SyntheticClass:0)
	at java.lang.Thread.run(Thread.java:1572)

Flutter dependencies

Dependency versions
firebase_core: 4.14.0
firebase_app_installations: 0.4.3
firebase_core_platform_interface: 8.1.1
firebase_app_installations_platform_interface: 0.1.5
_flutterfire_internals: 1.3.77

The complete pubspec.lock is included in the project ZIP.

Additional context and comments

Suggested fix

Dispatch the event on the main looper:

return FidListener { token ->
  Handler(Looper.getMainLooper()).post {
    events.success(mapOf("token" to token))
  }
}

This change resolved the exception in the original application's release build.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions