如何正确停止作为前台运行的服务

76

我正在尝试停止一个作为前台服务运行的服务。

当前的问题是,当我调用stopService()时,通知仍然存在。

因此,在我的解决方案中,我添加了一个接收器,并在onCreate()内进行注册。

onReceive()方法中,我调用stopforeground(true)来隐藏通知。 然后使用stopself()停止服务。

onDestroy()中,我取消了接收器的注册。

是否有更合适的方法来处理这个问题?因为stopService()根本不起作用。

@Override
public void onDestroy(){
  unregisterReceiver(receiver);
  super.onDestroy();
}

您需要将服务与活动绑定... - Looking Forward
你能展示一下你的 onDestroy() 方法吗? - user3110424
1
@user3110424,我已经发布了它,在销毁函数里面没有什么特别的,看起来运行良好。我想知道是否有更好的方法来实现它。 - user1940676
1
@AnilBhatiya 这个服务应该在活动被销毁后长时间运行,将其绑定到一个活动并不是必须的。 - user1940676
9个回答

104
  1. 根据您的活动,调用startService(intent)并传递一些数据表示停止服务的键。
  2. 从您的服务中,调用stopForeground(true)
  3. 然后立即在其后调用stopSelf()

4
onDestroy()方法内部是什么? - user1940676
1
@user1940676,不需要。只需从您希望停止它并删除通知的地方开始。 - Ilya Gazman
1
@user1940676 你需要从服务本身调用它。你需要以某种方式告诉他他需要自己完成。你可以通过意图传递数据,它将在Service.onStart()中接收到。 - Ilya Gazman
1
调用顺序是否有特定的好处?在调用 stopSelf 之前,首先调用 stopForeground 是否重要? - avalancha
3
必须调用stopForeground()吗?仅调用stopSelf()不足以完成任务吗?在我的情况下,我只是调用stopSelf(),它可以正常工作,通知也被移除了。 - K Pradeep Kumar Reddy
显示剩余7条评论

66

要从活动中启动和停止前台服务,请使用:

//start
    Intent startIntent = new Intent(MainActivity.this, ForegroundService.class);
    startIntent.setAction(Constants.ACTION.STARTFOREGROUND_ACTION);
    startService(startIntent);
//stop
    Intent stopIntent = new Intent(MainActivity.this, ForegroundService.class);
    stopIntent.setAction(Constants.ACTION.STOPFOREGROUND_ACTION);
    startService(stopIntent);

在你的前台服务中,使用(至少)这段代码:

@Override
 public int onStartCommand(Intent intent, int flags, int startId) {
    if (intent.getAction().equals(Constants.ACTION.STARTFOREGROUND_ACTION)) {
        Log.i(LOG_TAG, "Received Start Foreground Intent ");
        // your start service code
    }
    else if (intent.getAction().equals( Constants.ACTION.STOPFOREGROUND_ACTION)) {
        Log.i(LOG_TAG, "Received Stop Foreground Intent");
    //your end servce code
        stopForeground(true);
        stopSelfResult(startId);
    }
    return START_STICKY;
}

11
如果出现 STOPFOREGROUND_ACTION,我宁愿返回 START_NOT_STICKY - Hamzeh Soboh
stopSelfResult()很重要,它确保我们正确地停止服务。 - Henrique
3
调用 startService() 来停止一个服务...显然 :D。无论如何,谢谢,这很有帮助! - olivier

15
根据此处所述:https://developer.android.com/guide/components/services#Stopping 已启动的服务必须管理自己的生命周期。也就是说,系统不会停止或销毁服务,除非它必须恢复系统内存并且服务在onStartCommand()返回后继续运行。服务必须通过调用stopSelf()来停止自己,或者另一个组件可以通过调用stopService()来停止它。
一旦使用stopSelf()或stopService()请求停止服务,系统将尽快销毁该服务。
因此,您可以从您用于调用startService()的活动中调用stopService()。 我已在此处执行:
start_button.setOnClickListener {
            applicationContext.startForegroundService(Intent(this, ServiceTest::class.java))
        }
stop_button.setOnClickListener {
            applicationContext.stopService(Intent(this, ServiceTest::class.java))
        }

我创建了两个按钮来启动和停止服务,它正常工作。


14

来自活动

 stopService(new Intent(this, ForegroundService.class));

来自服务本身

  stopForeground(true);
  stopSelf()

3
除了其他答案之外,我最终使用以下代码片段来停止 Android Oreo 及更高版本上的作业服务。
Intent intent = new Intent(getApplicationContext(), LocationService.class);
intent.setAction(status);
ContextCompat.startForegroundService(getApplicationContext(), intent);

在服务中

@Override
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
  startForeground(121, notification);
  if (intent.getAction().equals("StopService")) {
    stopForeground(true);
    stopSelf();
  }

如果不调用startForeground()方法,应用将会崩溃。

新的Context.startForegroundService()方法可以启动前台服务。即使应用程序在后台运行,系统仍允许应用程序调用Context.startForegroundService()。但是,在服务创建后的5秒内,应用程序必须调用该服务的startForeground()方法。

https://developer.android.com/about/versions/oreo/android-8.0-changes.html


0
  • 您可以从服务活动停止服务

从服务:

 stopForeground(true);
 stopSelf();

来自活动:

stopService(new Intent(this, MyService.class));
//MyService is your service

你需要同时做两个还是其中一个? - Brandon Ros

0

我发现停止服务的最佳方法是让其自己停止。这样,您可以确保它实际上会停止并保持数据完整性。如果您想从外部(活动)执行此操作,我通常使用全局静态属性。

在这里检查我的答案:https://dev59.com/AW855IYBdhLWcg3wNxgM#57833370


0
如果您想停止服务并更新通知,可以先调用stopSelf(),然后再调用notify()。stopForeground(false)使通知可取消。
 private final BroadcastReceiver myBgWorkCompleteReceiver= new BroadcastReceiver() {
        @Override
  public void onReceive(Context context, Intent intent) {
            stopSelf();
            notificationLayoutExpanded.setTextViewText(R.id.file_download_notify_download_percentage, "Background work completed");
            manager.notify(mNotificationId, notification);
            stopForeground(false);
        }
};


 public void onDestroy() {
        super.onDestroy();
        // cancel any running threads here
        LocalBroadcastManager.getInstance(this).unregisterReceiver(myBgWorkCompleteReceiver);
    }

0
如果您有一个定期在前台运行的任务,例如使用System.Threading.Timer,您应该修改计时器,以便定期停止执行某个方法。
“停止”计时器的一种方法是:
if (timer != null)
{
timer.Change(Timeout.Infinite, Timeout.Infinite);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接