In my flutter application I have written a background service to get the users location. When the application is in the background this location service still get users location and the application still functions.
I don't want the background location service to run after user terminate the application.
But it seems location service seems to be still running when I terminate my application on Android.
Also when I start the application 2nd time it does not function correctly. I assume this is because the background service is still running.
If I stop the application by "Force Stop" all works fine in 2nd time.
Also if I manually stop the background service from the application (say from a button click, calling the stop function ) then close the app, again all works fine.
Can someone provide some advice on how to stop the background service when I close the application?
MainActivity.kt is;
class MainActivity: FlutterActivity() {
override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, LOCATION_CHANNEL).setMethodCallHandler {
call, result ->
if (call.method == "startLocationUpdate") {
var status = startUpdateLocation()
result.success(status.toString())
} else if (call.method == "stopLocationUpdate")
{
var status = stopUpdateLocation()
result.success(status.toString())
} else if (call.method == "isLocationPermissionEnabled")
{
var status = checkPermission()
result.success(status.toString())
}
else {
result.notImplemented()
}
}
EventChannel(flutterEngine.dartExecutor, LOCATION_EVENT_CHANNEL).setStreamHandler(
object : EventChannel.StreamHandler {
override fun onListen(arguments: Any?, events: EventChannel.EventSink) {
locationUpdateReceiver = receiveLocationUpdate(events)
}
override fun onCancel(arguments: Any?) {
unregisterReceiver(locationUpdateReceiver)
locationUpdateReceiver = null
isServiceStarted = false
}
}
)
}
override fun onDestroy() {
try {
if (locationUpdateReceiver != null )
{
unregisterReceiver(locationUpdateReceiver)
}
} catch (e: Exception) {
}
super.onDestroy()
}
private fun stopUpdateLocation() : Int {
if (isServiceStarted) {
unregisterReceiver(locationUpdateReceiver)
stopService(this)
isServiceStarted = false
return SUCCESS
}
else {
return SERVICE_NOT_RUNNING
}
}
private fun startUpdateLocation() : Int {
if (isServiceStarted) {
return SERVICE_ALREADY_STARTED
}
else if (!checkPermission()) {
//requestPermission()
return REQUESTING_PERMISSION
}
else {
registerReceiver(locationUpdateReceiver, locationIntentFilter);
isServiceStarted = true
startService(this)
return SUCCESS
}
}
private fun receiveLocationUpdate(events: EventChannel.EventSink): BroadcastReceiver {
return object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val key = LocationManager.KEY_LOCATION_CHANGED
val location: Location? = intent.extras!![key] as Location?
if (location != null) {
val runningAppProcessInfo = ActivityManager.RunningAppProcessInfo()
ActivityManager.getMyMemoryState(runningAppProcessInfo)
var appRunningBackground: Boolean = runningAppProcessInfo.importance != ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND
if (appRunningBackground) {
events.success("0," + location.latitude.toString() + "," + location.longitude.toString())
}
else {
events.success("1," + location.latitude.toString() + "," + location.longitude.toString())
}
}
}
}
}
private fun checkPermission(): Boolean {
val result = ContextCompat.checkSelfPermission(applicationContext, Manifest.permission.ACCESS_FINE_LOCATION)
val result1 = ContextCompat.checkSelfPermission(applicationContext, Manifest.permission.ACCESS_COARSE_LOCATION)
return result == PackageManager.PERMISSION_GRANTED && result1 == PackageManager.PERMISSION_GRANTED
}
companion object {
private const val LOCATION_CHANNEL = "flutter.io/location"
private const val LOCATION_EVENT_CHANNEL = "flutter.io/locationEvent"
private const val LOCATION_UPDATE_INTENT = "FLUTTER_LOCATION"
private const val PERMISSION_REQUEST_CODE = 1
private final const val SERVICE_NOT_RUNNING = 0;
private final const val SUCCESS = 1;
private final const val REQUESTING_PERMISSION = 100;
private final const val SERVICE_ALREADY_STARTED = 2;
var isServiceStarted = false
var duration = "1" ;
var distance = "20";
var locationIntentFilter = IntentFilter(LOCATION_UPDATE_INTENT)
var locationUpdateReceiver: BroadcastReceiver? = null
fun startService(context: Context) {
val startIntent = Intent(context, LocationService::class.java)
ContextCompat.startForegroundService(context, startIntent)
}
fun stopService(context: Context) {
val stopIntent = Intent(context, LocationService::class.java)
context.stopService(stopIntent)
}
}
}
in LocationSerivice.kt
class LocationService : Service() {
private val NOTIFICATION_CHANNEL_ID = "notification_location"
private val duration = 5 // In Seconds
private val distance = 0 // In Meters
override fun onCreate() {
super.onCreate()
isServiceStarted = true
val builder: NotificationCompat.Builder =
NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID)
.setOngoing(false)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val notificationManager: NotificationManager =
getSystemService(NOTIFICATION_SERVICE) as NotificationManager
val notificationChannel = NotificationChannel(
NOTIFICATION_CHANNEL_ID,
NOTIFICATION_CHANNEL_ID, NotificationManager.IMPORTANCE_LOW
)
notificationChannel.description = NOTIFICATION_CHANNEL_ID
notificationChannel.setSound(null, null)
notificationManager.createNotificationChannel(notificationChannel)
startForeground(1, builder.build())
}
}
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
LocationHelper().startListeningLocation(this, duration, distance);
return START_STICKY
}
override fun onBind(intent: Intent): IBinder? {
return null
}
override fun onDestroy() {
super.onDestroy()
isServiceStarted = false
}
override fun onTaskRemoved(rootIntent: Intent?) {
super.onTaskRemoved(rootIntent)
stopSelf()
}
companion object {
var isServiceStarted = false
}
}
in my AndroidManifest.xml I have
android:name=".LocationService"
android:enabled="true"
android:exported="true"
android:stopWithTask="true"
In my flutter app I call the stop service in
@override
void dispose() async {
if (_locationUpdateEventStarted) {
await methodChannel.invokeMethod('stopLocationUpdate');
}
super.dispose();
}
I also tried following, but it also did not work
@override
void didChangeAppLifecycleState(AppLifecycleState state) async {
super.didChangeAppLifecycleState(state);
if (state == AppLifecycleState.detached) {
if (_locationUpdateEventStarted) {
await methodChannel.invokeMethod('stopLocationUpdate');
}
}
}