MaikuB / flutter_local_notifications

A Flutter plugin for displaying local notifications on Android, iOS, macOS and Linux
2.45k stars 1.39k forks source link

Context.startForegroundService() did not then call Service.startForeground() #2413

Open leaf-node opened 2 days ago

leaf-node commented 2 days ago

Describe the bug Except for this one time, my app, which uses this package, runs fine without this error. However I ran into this crash that occurred maybe 1/2 to 2 minutes after starting the app.

To Reproduce

  1. (Likely) use custom app settings to disable sticky notifications within app
  2. Launch the otherwise mostly / usually working app in Android SDK 35 x86 emulator
  3. Random crash after a short while
  4. Relaunch app, without changing anything obviously related to notifications
  5. No crash
Launching lib/main.dart on sdk gphone64 x86 64 in debug mode...
✓ Built build/app/outputs/flutter-apk/app-debug.apk
I/FlutterBootReceiverPlugin( 6099): onAttachedToEngine
I/FlutterBackgroundExecutor( 6099): Starting BootHandlerService...
I/FlutterBootReceiverPlugin( 6099): onAttachedToEngine
Connecting to VM Service at ws://127.0.0.1:32997/DfT0pFTfUK8=/ws
Connected to the VM Service.
W/BootHandlerService( 6099): Attempted to start a duplicate background isolate. Returning...
[log] App data directory: /data/user/0/com.example.app/files/
I/BootHandlerService( 6099): BootHandlerService started!
I/FlutterBackgroundExecutor( 6099): Executing Dart callback: -7767485495945997519...
[log] App data directory: /data/user/0/com.example.app/files/
D/ProfileInstaller( 6099): Installing profile for com.example.app
D/EGL_emulation( 6099): app_time_stats: avg=1836.44ms min=4.41ms max=16048.77ms count=9
D/EGL_emulation( 6099): app_time_stats: avg=14.18ms min=1.07ms max=30.62ms count=56
D/EGL_emulation( 6099): app_time_stats: avg=6.06ms min=2.34ms max=11.26ms count=60
D/AndroidRuntime( 6099): Shutting down VM
E/AndroidRuntime( 6099): FATAL EXCEPTION: main
E/AndroidRuntime( 6099): Process: com.example.app, PID: 6099
E/AndroidRuntime( 6099): android.app.RemoteServiceException$ForegroundServiceDidNotStartInTimeException: Context.startForegroundService() did not then call Service.startForeground(): ServiceRecord{9e28a5e u0 com.example.app/com.dexterous.flutterlocalnotifications.ForegroundService c:com.example.app}
E/AndroidRuntime( 6099):    at android.app.ActivityThread.generateForegroundServiceDidNotStartInTimeException(ActivityThread.java:2271)
E/AndroidRuntime( 6099):    at android.app.ActivityThread.throwRemoteServiceException(ActivityThread.java:2239)
E/AndroidRuntime( 6099):    at android.app.ActivityThread.-$$Nest$mthrowRemoteServiceException(Unknown Source:0)
E/AndroidRuntime( 6099):    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2557)
E/AndroidRuntime( 6099):    at android.os.Handler.dispatchMessage(Handler.java:107)
E/AndroidRuntime( 6099):    at android.os.Looper.loopOnce(Looper.java:232)
E/AndroidRuntime( 6099):    at android.os.Looper.loop(Looper.java:317)
E/AndroidRuntime( 6099):    at android.app.ActivityThread.main(ActivityThread.java:8705)
E/AndroidRuntime( 6099):    at java.lang.reflect.Method.invoke(Native Method)
E/AndroidRuntime( 6099):    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:580)
E/AndroidRuntime( 6099):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:886)
I/Process ( 6099): Sending signal. PID: 6099 SIG: 9
Lost connection to device.

Exited.

Expected behavior The expectation was for the app to not crash this one time.

Sample code to reproduce the problem

    // run from background isolate
    await notifier.initializeAlertNotifications();
    await notifier.startAnroidStickyNotification();

  // ...

  Future<void> initializeAlertNotifications() async {
    if (Platform.isLinux) {
      var initializationSettingsLinux =
          const LinuxInitializationSettings(defaultActionName: "Launch app");
      var initializationSettings =
          InitializationSettings(linux: initializationSettingsLinux);
      await _flutterLocalNotificationsPlugin.initialize(initializationSettings);
    } else if (Platform.isAndroid) {
      const AndroidNotificationChannel stickyChannel =
          AndroidNotificationChannel(
              stickyNotificationChannelId, stickyNotificationChannelName,
              description: stickyNotificationChannelDescription,
              importance: Importance.low);
      const AndroidNotificationChannel alertChannel =
          AndroidNotificationChannel(
              alertsNotificationChannelId, alertsNotificationChannelName,
              description: alertsNotificationChannelDescription,
              importance: Importance.max);
      await _flutterLocalNotificationsPlugin
          .resolvePlatformSpecificImplementation<
              AndroidFlutterLocalNotificationsPlugin>()
          ?.createNotificationChannel(stickyChannel);
      await _flutterLocalNotificationsPlugin
          .resolvePlatformSpecificImplementation<
              AndroidFlutterLocalNotificationsPlugin>()
          ?.createNotificationChannel(alertChannel);
    } else {
      throw Exception("Unsupported platform for notifications.");
    }
  }

 Future<void> startAnroidStickyNotification() async {
    if (!Platform.isAndroid || !_settings.notificationsEnabled) {
      return;
    }
    var activeAlerts = await _flutterLocalNotificationsPlugin
        .resolvePlatformSpecificImplementation<
            AndroidFlutterLocalNotificationsPlugin>()
        ?.getActiveNotifications();
    if (activeAlerts
            ?.where((alert) => alert.id == stickyNotificationId)
            .isNotEmpty ??
        false) {
      return;
    }
    var duration = Util.prettyPrintDuration(
        duration: Duration(seconds: _settings.refreshInterval),
        longForm: true,
        stripLeadingOne: true);
    await _flutterLocalNotificationsPlugin
        .resolvePlatformSpecificImplementation<
            AndroidFlutterLocalNotificationsPlugin>()
        ?.startForegroundService(stickyNotificationId, stickyNotificationTitle,
            "$stickyNotificationContentStart $duration",
            notificationDetails: _stickyAndroidNotificationDetails,
            foregroundServiceTypes: {
          AndroidServiceForegroundType.foregroundServiceTypeDataSync
        });
  }