Dev-hwang / flutter_foreground_task

This plugin is used to implement a foreground service on the Android platform.
https://pub.dev/packages/flutter_foreground_task
MIT License
149 stars 109 forks source link

Add force restart on service destroy #260

Closed TheValkDokk closed 2 months ago

TheValkDokk commented 2 months ago

In my app, the service is really importance as it's handles bluetooth data and sync to server, but it's have 25% change of getting kill by the OS (Android, Samsung phones), i tested with 4 phone and 1 out of 4 foreground service will get terminated. So i wonder is there any other way to avoid this? Could you add a flag that can be use for force restart when service get terminated.

i tried something like this before using onDestroy to send to BroadcastReceiver and start the service again.

I know this will make the app hard to be approve by Playstore but an option flag would help alot.

My app requirement is like this:

I seek guidance, Thanks in advance

Dev-hwang commented 2 months ago

This plugin already registers an alarm to restart the service if it terminates abnormally.

https://github.com/Dev-hwang/flutter_foreground_task/blob/48b2ad57cdbdb2f1c97ad416f1ddeda73648de03/android/src/main/kotlin/com/pravera/flutter_foreground_task/service/ForegroundService.kt#L188

The below permission are required to allow the OS to better restart service in doze mode.

// AndroidManifest.xml
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />

// dart
FlutterForegroundTask.requestIgnoreBatteryOptimization();

but, this method also seems to have limitations. If anyone has a better idea, please let me know...

TheValkDokk commented 2 months ago

This is how i do it on my other Kotlin app, works even on Xiaomi Hyper OS which aggressively kill the app as soon as user remove it from the recent apps.

Restarter

class Restarter : BroadcastReceiver() {
    override fun onReceive(context: Context?, intent: Intent?) {
        context?.let {
            startSerialService(it)
        }
    }

    private fun startSerialService(context: Context) {
        val newIntent = Intent(context, SerialService::class.java)
        newIntent.action = SerialService.Action.START.toString()
        context.startService(newIntent)
    }
}

Both Service and Main Intent have this:

  override fun onDestroy() {
        super.onDestroy()
        val broadcastIntent = Intent()
        broadcastIntent.action = Constant.RESTART_SERVICE
        broadcastIntent.setClass(this, Restarter::class.java)
        this.sendBroadcast(broadcastIntent)
    }

Service onStartCommand

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        notification = getNotification("Serial Service is running")
        startForeground(1, notification.build())
        Timber.d("onStartCommand")
        when (intent?.action) {
            Action.START.toString() -> start()
            Action.STOP.toString() -> stop{}
        }
        return START_STICKY
    }

I think keeping the BroadcastReceiver alive is important for the service to start again when terminated

Dev-hwang commented 2 months ago

The RestartReceiver.setRestartAlarm function also requests service restart in a similar way as above.

The difference is that AlarmManager is used to wake up the device from the doze state and restart the service.

Calling sendBroadcast directly is blocked in doze mode. It is explained in the official documentation, and this limitation has also been confirmed on actual devices.

Dev-hwang commented 2 months ago

I see it easier to restart the service using setAlarmClock and setExactAndAllowWhileIdle functions of AlarmManager, but this requires the SCHEDULE_EXACT_ALARM permission and the user to allow it by going to the Alarms & Reminders settings page.

Unless the app provides a exact alarm service, the SCHEDULE_EXACT_ALARM permission may make it difficult to distribute the app due to Google policy.

This might be one way too, so I'm planning to add an option.

TheValkDokk commented 2 months ago

Thank for the info about sendBroadcast in doze mode, my app does in fact categorize somewhat as an alarm app with health tracking features so this could work

Dev-hwang commented 2 months ago

@TheValkDokk

released flutter_foreground_task: 8.5.0

check CHANGELOG.md for more detatils :)

TheValkDokk commented 2 months ago

Sorry for the late reply, i will check it out

Thank you for your hard work