ekasetiawans / flutter_background_service

269 stars 192 forks source link

after closing the app, and reopen i cant reach the service.on() in background service #293

Open daviddagan opened 1 year ago

daviddagan commented 1 year ago

I'm using latest version - 2.4.6 with flutter 3.3.10 only in android It happens in all the stages debug-release

when opening the app for the first time configuration and starting the service - the invoke / on service work just fine but after exit the app for and reopening I cant reach the service.on(). feels like i loose the service instance

arpitkushwaha commented 1 year ago

I am also facing the same issue as mentioned by @daviddagan. Adding to the description it happens when I set autoStart: false at configuration time. If I set autoStart: true, then it works as expected. Attaching my code below.

`import 'dart:async'; import 'dart:io'; import 'dart:ui';

import 'package:device_info_plus/device_info_plus.dart'; import 'package:flutter/material.dart'; import 'package:flutter_background_service/flutter_background_service.dart'; import 'package:flutter_background_service_android/flutter_background_service_android.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:flutter_ringtone_player/flutter_ringtone_player.dart';

Future main() async { WidgetsFlutterBinding.ensureInitialized(); await initializeService(); runApp(const MyApp()); }

Future initializeService() async { final service = FlutterBackgroundService();

/// OPTIONAL, using custom notification channel id const AndroidNotificationChannel channel = AndroidNotificationChannel( 'my_foreground', // id 'MY FOREGROUND SERVICE', // title description: 'This channel is used for important notifications.', // description importance: Importance.low, // importance must be at low or higher level );

final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();

if (Platform.isIOS) { await flutterLocalNotificationsPlugin.initialize( const InitializationSettings( // iOS: IOSInitializationSettings(), ), ); }

await flutterLocalNotificationsPlugin .resolvePlatformSpecificImplementation< AndroidFlutterLocalNotificationsPlugin>() ?.createNotificationChannel(channel);

await service.configure( androidConfiguration: AndroidConfiguration( // this will be executed when app is in foreground or background in separated isolate onStart: onStart,

  // auto start service
  autoStart: false,
  isForegroundMode: true,

  notificationChannelId: 'my_foreground',
  initialNotificationTitle: 'AWESOME SERVICE',
  initialNotificationContent: 'Initializing',
  foregroundServiceNotificationId: 888,
),
iosConfiguration: IosConfiguration(
  // auto start service
  autoStart: false,

  // this will be executed when app is in foreground in separated isolate
  onForeground: onStart,

  // you have to enable background fetch capability on xcode project
  onBackground: onIosBackground,
),

);

}

// to ensure this is executed // run app from xcode, then from xcode menu, select Simulate Background Fetch

@pragma('vm:entry-point') Future onIosBackground(ServiceInstance service) async { WidgetsFlutterBinding.ensureInitialized(); DartPluginRegistrant.ensureInitialized();

SharedPreferences preferences = await SharedPreferences.getInstance(); await preferences.reload(); final log = preferences.getStringList('log') ?? []; log.add(DateTime.now().toIso8601String()); await preferences.setStringList('log', log);

return true; }

@pragma('vm:entry-point') void onStart(ServiceInstance service) async { // Only available for flutter 3.0.0 and later DartPluginRegistrant.ensureInitialized();

// For flutter prior to version 3.0.0 // We have to register the plugin manually

SharedPreferences preferences = await SharedPreferences.getInstance();

var droppingTime = DateTime(DateTime.now().year, DateTime.now().month, DateTime.now().day, DateTime.now().hour, DateTime.now().minute, 50, 0, 0);

/// OPTIONAL when use custom notification final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();

if (service is AndroidServiceInstance) { service.on('setAsForeground').listen((event) { service.setAsForegroundService(); });

service.on('setAsBackground').listen((event) {
  service.setAsBackgroundService();
});

}

service.on('stopService').listen((event) { FlutterRingtonePlayer.stop(); service.stopSelf(); });

// bring to foreground Timer.periodic(const Duration(seconds: 1), (timer) async { if (service is AndroidServiceInstance) { if (await service.isForegroundService()) { /// OPTIONAL for use custom notification /// the notification id must be equals with AndroidConfiguration when you call configure() method. flutterLocalNotificationsPlugin.show( 888, 'Alarm set for chosen dropping point', droppingTime.difference(DateTime.now()).isNegative ? "Wake up, dropping point reached" : 'Reaching dropping point in : ${droppingTime.difference(DateTime.now())}', const NotificationDetails( android: AndroidNotificationDetails( 'my_foreground', 'MY FOREGROUND SERVICE', icon: 'ic_bg_service_small', ongoing: true, ), ), );

    if(droppingTime.difference(DateTime.now()).isNegative && preferences.getString("hello")=="0") {

      FlutterRingtonePlayer.play(
        fromAsset: "assets/alarm.mp3",
        ios: IosSounds.glass,
        looping: true, // Android only - API >= 28
        asAlarm: true, // Android only - all APIs
      );
      preferences.setString("hello", "1");
    }

    // if you don't using custom notification, uncomment this
    // service.setForegroundNotificationInfo(
    //   title: "My App Service",
    //   content: "Updated at ${DateTime.now()}",
    // );
  }
}

/// you can see this log in logcat
print('FLUTTER BACKGROUND SERVICE: ${DateTime.now()}');

// test using external plugin
final deviceInfo = DeviceInfoPlugin();
String? device;
if (Platform.isAndroid) {
  final androidInfo = await deviceInfo.androidInfo;
  device = androidInfo.model;
}

if (Platform.isIOS) {
  final iosInfo = await deviceInfo.iosInfo;
  device = iosInfo.model;
}

service.invoke(
  'update',
  {
    "current_date": droppingTime.difference(DateTime.now()).toString(),
    "device": device,
  },
);

}); }

class MyApp extends StatefulWidget { const MyApp({Key? key}) : super(key: key);

@override State createState() => _MyAppState(); }

class _MyAppState extends State { String text = "Stop Service"; @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( appBar: AppBar( title: const Text('Service App'), ), body: Column( children: [ ElevatedButton( child: Text("Start Service"), onPressed: () async { final service = FlutterBackgroundService(); SharedPreferences preferences = await SharedPreferences.getInstance(); var isRunning = await service.isRunning(); if (!isRunning) { preferences.setString("hello", "0"); service.startService(); } setState(() {}); }, ),

        ElevatedButton(
          child: Text("Stop Service"),
          onPressed: () async {
            final service = FlutterBackgroundService();
            var isRunning = await service.isRunning();
            if (isRunning) {
              service.invoke("stopService");
            }
            setState(() {});
          },
        ),

      ],
    ),
    floatingActionButton: FloatingActionButton(
      onPressed: () {},
      child: const Icon(Icons.play_arrow),
    ),
  ),
);

} }`

arpitkushwaha commented 1 year ago

I was able to find a workaround in my app for this issue. Posting it here as someone might find it helpful.

Basically, when you configure the FlutterBackgroundService do autoStart: true and isForegroundMode: true. This is important to trigger service.on() after the app has been killed. After that, As I don't want to start the service as soon as the app starts so I will set it as a background service: FlutterBackgroundService().invoke("setAsBackground"); (check docs) and then I can stop it if I want to. The next step is to start the service on whatever event you want if you have killed it, and then set it in the foreground : FlutterBackgroundService().invoke("setAsForeground"); (check docs) After following above steps, even if you kill the app, the service.on() will trigger after relaunching.

The only important thing was to configure autoStart: true and isForegroundMode: true and it worked. The remaining code is just a work around to start the foreground service on whatever event I want because I don't want to start it at the beginning.

daviddagan commented 1 year ago

Can I have a link to your git ?

בתאריך יום ו׳, 14 באפר׳ 2023, 14:54, מאת Arpit Kushwaha ‏< @.***>:

I was able to find a workaround in my app for this issue. Posting it here as someone might find it helpful.

Basically, when you configure the FlutterBackgroundService do autoStart: true and isForegroundMode: true. This is important to trigger service.on() after the app has been killed. After that, As I don't want to start the service as soon as the app starts so I will set it as a background service: FlutterBackgroundService().invoke("setAsBackground"); (check docs) and then I can stop it if I want to. The next step is to start the service on whatever event you want, if you have killed it and then set it in the foreground : FlutterBackgroundService().invoke("setAsForeground"); (check docs) After following above steps, even if you kill the app, the service.on() will trigger after relaunching.

The only important thing was to configure autoStart: true and isForegroundMode: true and it worked. The remaining code is just a work around to start the foreground service on whatever event I want because I don't want to start it at the beginning.

— Reply to this email directly, view it on GitHub https://github.com/ekasetiawans/flutter_background_service/issues/293#issuecomment-1508390687, or unsubscribe https://github.com/notifications/unsubscribe-auth/AH6ZIWQ3YKCOLCSM2N7A263XBE3HZANCNFSM6AAAAAAWCQ6ABU . You are receiving this because you were mentioned.Message ID: @.***>