背景服务在Android上的应用程序关闭后不停止

发布于 2025-02-13 13:07:34 字数 8046 浏览 3 评论 0原文

在我的Flutter应用程序中,我写了一个背景服务以获取用户位置。当应用程序在后台时,此位置服务仍然可以获取用户位置,并且该应用程序仍然运行。

我不希望后台位置服务在用户终止应用程序后运行。

但是,当我在Android上终止我的应用程序时,位置服务似乎仍在运行。

同样,当我启动应用程序第二次时,它无法正常运行。我认为这是因为背景服务仍在运行。

  • 如果我通过“强制停止”停止应用程序,则第二次都可以正常工作。

  • 另外,如果我从应用程序中手动停止背景服务(例如单击按钮,调用停止功能),然后关闭应用程序,请同样工作。

有人关闭应用程序时如何停止背景服务,有人可以提供一些建议吗?

mainActivity.kt是;

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)
        }
    }
}

在我的androidmanifest.xml中的位置erivice.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
    }
}

,我

   android:name=".LocationService"
   android:enabled="true"
   android:exported="true"
   android:stopWithTask="true"
   

在我的flutter应用程序中使用的是我在我也尝试关注的stop服务

  @override
  void dispose() async {

    if (_locationUpdateEventStarted) {
      await methodChannel.invokeMethod('stopLocationUpdate');
    }

    super.dispose();
  }

,但它也无效

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) async {
    super.didChangeAppLifecycleState(state);

    if (state == AppLifecycleState.detached) {
      
      if (_locationUpdateEventStarted) {
        await methodChannel.invokeMethod('stopLocationUpdate');
      }
    }
  }

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');
      }
    }
  }

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

此岸叶落 2025-02-20 13:07:34

我使用 Flutter_background_service 有相似或相同的问题。我正在修改 onTaskRemaved

flutter_background_service由InvokeMethod停止(“ stopService”)。因此,我发现 stopService 方法代码。

            if (method.equalsIgnoreCase("stopService")) {
                isManuallyStopped = true;
                WatchdogReceiver.remove(this);

                try {
                    synchronized (listeners) {
                        for (Integer key : listeners.keySet()) {
                            IBackgroundService listener = listeners.get(key);
                            if (listener != null) {
                                listener.stop();
                            }
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }

                stopSelf();
                result.success(true);
                return;
            }

而且我能够通过应用StopService Mothod代码来解决它。

@Override
public void onTaskRemoved(Intent rootIntent) {
    isManuallyStopped = true;
    WatchdogReceiver.remove(this);

    try {
        synchronized (listeners) {
            for (Integer key : listeners.keySet()) {
                IBackgroundService listener = listeners.get(key);
                if (listener != null) {
                    listener.stop();
                }
            }
        }
    } catch (Exception e) {
        e.printStackTrace();
    }

    stopSelf();
}

我不确定这是正确的解决方案。但是我希望该解决方案对您的问题有所帮助。

我提交了一个问题,因此请记住这一点。
https://github.com/ekasetiawans/flutter_background_servcookeground_service/service/issues/sissues/300

i had similar or the same problem using flutter_background_service. i'm modify the onTaskRemoved.

flutter_background_service is stop by invokeMethod("stopService"). so, i found the stopService method code.

            if (method.equalsIgnoreCase("stopService")) {
                isManuallyStopped = true;
                WatchdogReceiver.remove(this);

                try {
                    synchronized (listeners) {
                        for (Integer key : listeners.keySet()) {
                            IBackgroundService listener = listeners.get(key);
                            if (listener != null) {
                                listener.stop();
                            }
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }

                stopSelf();
                result.success(true);
                return;
            }

and i was able to solve it by applying the stopService mothod code.

@Override
public void onTaskRemoved(Intent rootIntent) {
    isManuallyStopped = true;
    WatchdogReceiver.remove(this);

    try {
        synchronized (listeners) {
            for (Integer key : listeners.keySet()) {
                IBackgroundService listener = listeners.get(key);
                if (listener != null) {
                    listener.stop();
                }
            }
        }
    } catch (Exception e) {
        e.printStackTrace();
    }

    stopSelf();
}

i'm not sure this is the correct solution. but i hope this solution help your problem.

I submited an issue, so keep that in mind.
https://github.com/ekasetiawans/flutter_background_service/issues/300

北音执念 2025-02-20 13:07:34

实际服务是前景服务即使应用程序已关闭,也可以运行。

前景服务显示状态栏通知,以便用户积极意识到您的应用程序正在前台执行任务,并且正在消耗系统资源。除非将服务停止或从前景中删除,否则该通知不能被驳回。

Actually service is Foreground service, which runs even if the application is closed.

Foreground services show a status bar notification, so that users are actively aware that your app is performing a task in the foreground and is consuming system resources. The notification cannot be dismissed unless the service is either stopped or removed from the foreground.

樱娆 2025-02-20 13:07:34

这是我所做的,它有效

await service.configure(
    androidConfiguration: AndroidConfiguration(
        
        onStart: onStart,
        autoStart: true,
        isForegroundMode: false,

        notificationChannelId: 'my_foreground',
        initialNotificationTitle: 'my service',
        initialNotificationContent: 'the service is started',
        foregroundServiceNotificationId: 888,
        autoStartOnBoot: true
    ),
    iosConfiguration: IosConfiguration(
        autoStart: true,
        onForeground: onStart,
        onBackground: onIosBackground,
    ),
);

This what i've made and it works

await service.configure(
    androidConfiguration: AndroidConfiguration(
        
        onStart: onStart,
        autoStart: true,
        isForegroundMode: false,

        notificationChannelId: 'my_foreground',
        initialNotificationTitle: 'my service',
        initialNotificationContent: 'the service is started',
        foregroundServiceNotificationId: 888,
        autoStartOnBoot: true
    ),
    iosConfiguration: IosConfiguration(
        autoStart: true,
        onForeground: onStart,
        onBackground: onIosBackground,
    ),
);
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文