flutter_background_service
flutter_background_service copied to clipboard
`flutter_background_service` stops only in release mode in some Android devices
I've keep counting user's steps as using flutter packages flutter_background_service
, flutter_background_service_android
and flutter_local_notifications
.
I write all codes in class StepNotification
.
class StepNotification {
Future<void> initializeService() async {
final String stepPrefsName = "${todayToStringLine()}_stepCount";
final service = FlutterBackgroundService();
const AndroidNotificationChannel channel = AndroidNotificationChannel(
notificationChannelId,
notificationChannelName,
description: "This channel is used for step notification.",
importance: Importance.low,
playSound: false,
enableVibration: false,
);
final FlutterLocalNotificationsPlugin flutterLocalNotifiationPlugin =
FlutterLocalNotificationsPlugin();
if (Platform.isIOS || Platform.isAndroid) {
await flutterLocalNotifiationPlugin.initialize(
const InitializationSettings(
iOS: DarwinInitializationSettings(),
android: AndroidInitializationSettings('@mipmap/noti_icon'),
),
);
}
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
await sharedPreferences.reload();
int todayStepCount = sharedPreferences.getInt(stepPrefsName) ?? 0;
await flutterLocalNotifiationPlugin
.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin>()
?.createNotificationChannel(channel);
await service.configure(
androidConfiguration: AndroidConfiguration(
foregroundServiceNotificationId: stepNotificationId,
onStart: onStart,
autoStart: true,
autoStartOnBoot: true,
notificationChannelId: notificationChannelId,
initialNotificationTitle: "$todayStepCount 걸음",
initialNotificationContent: "",
isForegroundMode: true,
),
iosConfiguration: IosConfiguration(
autoStart: true,
onForeground: onStart,
onBackground: onIosBackground,
),
);
service.startService();
}
}
// background
@pragma('vm:entry-point')
Future<void> onStart(ServiceInstance service) async {
await dotenv.load(fileName: ".env");
await Firebase.initializeApp();
await Supabase.initialize(
url: dotenv.env["SUPABASE_URL"]!,
anonKey: dotenv.env["SUPABASE_ANONKEY"]!,
);
DartPluginRegistrant.ensureInitialized();
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin();
StepEventCount stepEventCount = StepEventCount();
StepRepository stepRepository = StepRepository();
if (service is AndroidServiceInstance) {
service.on('setAsForeground').listen((event) {
service.setAsForegroundService();
});
service.on('setAsBackground').listen((event) {
service.setAsBackgroundService();
});
service.on('stopService').listen((event) {
service.stopSelf();
});
}
// link step event count
stepEventCount.initializeStepCount();
Timer.periodic(
const Duration(
seconds: 1,
), (timer) async {
final String stepPrefsName = "${todayToStringLine()}_stepCount";
await sharedPreferences.reload();
int todayStepCount = sharedPreferences.getInt(stepPrefsName) ?? 0;
if (!sharedPreferences.containsKey(stepPrefsName)) {
DateTime currentDate = currentKoreaDateTime();
DateTime yesterdayDate = currentDate.subtract(const Duration(days: 1));
String yesterdayString = dateTimeToStringDateLine(yesterdayDate);
String yesterdayPrefsName = "${yesterdayString}_stepCount";
if (sharedPreferences.containsKey(yesterdayPrefsName) &&
sharedPreferences.containsKey("dummy")) {
// 어제 값 있는 경우
int yesterdayStepCount = sharedPreferences.getInt(yesterdayPrefsName) ??
await stepRepository.fetchUserCertainDateStepCount(yesterdayString);
await stepRepository.resetStepDatabase(
yesterdayDate, yesterdayStepCount);
int dummyPrefs = sharedPreferences.getInt("dummy") ?? 0;
int updateDummyCount = yesterdayStepCount + dummyPrefs;
await sharedPreferences.setInt("dummy", updateDummyCount);
await sharedPreferences.setInt(stepPrefsName, 0);
}
// 2일 전 값 삭제
DateTime dayBeforeYesterdayDate =
currentDate.subtract(const Duration(days: 2));
String dayBeforeYesterdayString =
dateTimeToStringDateLine(dayBeforeYesterdayDate);
String dayBeforeYesterdayPrefsName =
"${dayBeforeYesterdayString}_stepCount";
if (sharedPreferences.containsKey(dayBeforeYesterdayPrefsName)) {
await sharedPreferences.remove(dayBeforeYesterdayPrefsName);
}
}
service.invoke("step", {
stepStreamKey: todayStepCount,
});
if (service is AndroidServiceInstance) {
flutterLocalNotificationsPlugin.show(
stepNotificationId,
"$todayStepCount 걸음",
"",
const NotificationDetails(
android: AndroidNotificationDetails(
notificationChannelId,
notificationChannelName,
showWhen: false,
ongoing: true,
autoCancel: false,
importance: Importance.high,
priority: Priority.high,
playSound: false,
enableVibration: false,
),
iOS: DarwinNotificationDetails(
presentAlert: false,
presentBadge: false,
presentSound: false,
),
),
);
}
});
// 1 hours todatStepCount update
Timer.periodic(const Duration(hours: 1), (timer) async {
if (service is AndroidServiceInstance) {
final String stepPrefsName = "${todayToStringLine()}_stepCount";
int todayStepCount = sharedPreferences.getInt(stepPrefsName) ?? 0;
await stepRepository.updateTodayStepCount(todayStepCount);
}
});
}
I check activityRecognition and if this permission is given, I call background work.
bool userStepStatus = Platform.isAndroid
? await Permission.activityRecognition.status
: await Permission.sensors.status;
if(userStepStatus) {
StepNotification().initializeService();
}
I realized that some permission is more needed in AndroidManifest from Android 14 based on document.
So I set below.
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_HEALTH" />
<service
android:name="id.flutter.flutter_background_service.BackgroundService"
android:foregroundServiceType="health"
/>
And it works well.
However after some days later, some Android devices shows not our notification icon, but default icon, which means something go wrong.
Even this counting user step doesn't work at all.
And I know that background work can be killed by device automatically, but after this default icon shows, then even if user open app, which means this app calls StepNotification().initializeService()
, Default icon is not changed and counting step also doesn't work at all.
And even sometimes without default icon, just app icon disappears and of course background work is not working even user opens app which means it calls background work even from foreground again.
I can understand device kills background work due to battery. But even user call background work again, why doesn't it work again?
Even I debug with the device having this issue, It works perfect. But only when I download app in release mode, it shows this error so I can't see any issue log.
Please help me whether some code or permission is missing here.