49

我有一个从 Activity 启动的 IntentService,我希望能够使用 Activity 中的“取消”按钮立即从该 Activity 停止该服务。一旦按下“取消”按钮,我希望服务停止执行代码行。

我发现了许多与此类似的问题(即此处此处此处此处),但没有好的答案。 Activity.stopService()并立即Service.stopSelf()执行该Service.onDestroy()方法,然后在销毁服务之前让代码onHandleIntent()一直完成。

由于显然没有保证立即终止服务线程的方法,我能找到的唯一推荐的解决方案(在这里)是在服务中有一个可以在onDestroy()方法中切换的布尔成员变量,然后几乎每一行代码onHandleIntent()包含在自己的“if”子句中,查看该变量。这是一种糟糕的代码编写方式。

有人知道在 IntentService 中执行此操作的更好方法吗?

4

9 回答 9

36

这是诀窍,使用 volatile 静态变量并检查服务中应检查服务继续的某些行中的继续条件:

class MyService extends IntentService {
    public static volatile boolean shouldContinue = true;
    public MyService() {
        super("My Service");
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        doStuff();
    }

    private void doStuff() {
        // do something 

        // check the condition
        if (shouldContinue == false) {
            stopSelf();
            return;
        }

       // continue doing something

       // check the condition
       if (shouldContinue == false) {
           stopSelf();
           return;
       }

       // put those checks wherever you need
   }
}

并在您的活动中这样做以停止您的服务,

 MyService.shouldContinue = false;
于 2014-04-09T15:34:43.710 回答
14

立即停止线程或进程通常是一件肮脏的事情。但是,如果您的服务是无状态的,那应该没问题。

在清单中将服务声明为单独的进程:

<service
     android:process=":service"
     ...

当你想停止它的执行时,只需终止该进程:

ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
List<RunningAppProcessInfo> runningAppProcesses = am.getRunningAppProcesses();

Iterator<RunningAppProcessInfo> iter = runningAppProcesses.iterator();

while(iter.hasNext()){
    RunningAppProcessInfo next = iter.next();

    String pricessName = getPackageName() + ":service";

    if(next.processName.equals(pricessName)){
        Process.killProcess(next.pid);
        break;
    }
}
于 2014-05-03T12:00:10.020 回答
7

我在服务中使用了一个广播接收器,它只是将停止布尔值设置为真。例子:

private boolean stop=false;

public class StopReceiver extends BroadcastReceiver {

   public static final String ACTION_STOP = "stop";

   @Override
   public void onReceive(Context context, Intent intent) {
       stop = true;
   }
}


@Override
protected void onHandleIntent(Intent intent) {
    IntentFilter filter = new IntentFilter(StopReceiver.ACTION_STOP);
    filter.addCategory(Intent.CATEGORY_DEFAULT);
    StopReceiver receiver = new StopReceiver();
    registerReceiver(receiver, filter);

    // Do stuff ....

    //In the work you are doing
    if(stop==true){
        unregisterReceiver(receiver);
        stopSelf();
    }
}

然后,从活动调用中:

//STOP SERVICE
Intent sIntent = new Intent();
sIntent.setAction(StopReceiver.ACTION_STOP);
sendBroadcast(sIntent);

停止服务。

PD:我使用布尔值,因为在我的情况下,我在循环中停止服务,但您可能可以在 onReceive 中调用 unregisterReceiver 和 stopSelf。

PD2:如果服务正常工作,不要忘记调用 unregisterReceiver,否则你会得到一个泄露的 IntentReceiver 错误。

于 2014-03-20T10:14:31.950 回答
2
@Override
protected void onHandleIntent(Intent intent) {
    String action = intent.getAction();
    if (action.equals(Action_CANCEL)) {
        stopSelf();
    } else if (action.equals(Action_START)) {
        //handle
    }
}

希望它有效。

于 2012-12-24T08:37:10.280 回答
2

IntentService的情况下,它不会停止或通过某些意图操作接受任何其他请求,直到其onHandleIntent方法完成前一个请求。

如果我们尝试通过其他操作再次启动IntentServiceonHandleIntent ,则只会在之前的意图/任务完成时调用。

在方法完成其任务之前,也stopService(intent);stopSelf();不工作。onHandleIntent()

所以我认为这里更好的解决方案是在这里使用 normal Service

我希望它会有所帮助!

于 2015-06-11T10:21:52.817 回答
1

如果使用IntentService,那么我认为你被困在做你描述的事情,onHandleIntent()代码必须轮询它的“停止”信号。

如果您的后台任务可能会长时间运行,并且您需要能够停止它,我认为您最好使用普通的Service。概括地说,将您的服务写入:

  • 公开一个“开始”意图来​​启动一个 AsyncTask 来执行你的后台工作,保存对新创建的 AsyncTask 的引用。
  • 公开一个“取消” Intent 来调用AsyncTask.cancel(true),或者让 onDestroy() 调用AsyncTask.cancel(true)
  • 然后 Activity 可以发送“取消”Intent 或调用 stopService()。

为了换取取消后台工作的能力,服务承担以下责任:

  • AsyncTaskdoInBackground()必须优雅地处理 InterruptedException 和/或定期检查 Thread.interrupted(),并“提前”返回。
  • 服务必须确保stopSelf()被调用(可能在 AsyncTask onPostExecute/onCancelled 中)。
于 2014-03-02T21:54:51.487 回答
0

正如@budius 在他的评论中已经提到的那样,您应该在Service单击该按钮时设置一个布尔值:

// your Activity.java
public boolean onClick() {
   //...
   mService.performTasks = false;
   mService.stopSelf();
}

在您的Intent处理中,在您执行提交/发送意图信息的重要任务之前,只需使用该布尔值:

// your Service.java
public boolean performTasks = true;

protected void onHandleIntent(Intent intent) {
   Bundle intentInfo = intent.getBundle();
   if (this.performTasks) {
      // Then handle the intent...
   }
}

否则,服务完成它的处理任务Intent。这就是它的用途, 因为如果您查看核心代码,我不太明白您如何解决它。

于 2014-03-02T22:30:18.723 回答
-1

这是一些启动/停止服务的示例代码

开始,

Intent GPSService = new Intent(context, TrackGPS.class);
context.startService(GPSService);

停止,

context.stopService(GPSService);

于 2012-06-29T07:59:51.323 回答
-1
context.stopService(GPSService);
于 2014-03-13T10:14:20.760 回答