Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to force an IntentService to stop immediately with a cancel button from an Activity?

I have an IntentService that is started from an Activity and I would like to be able to stop the service immediately from the activity with a "cancel" button in the activity. As soon as that "cancel" button is pressed, I want the service to stop executing lines of code.

I've found a number of questions similar to this (i.e. here, here, here, here), but no good answers. Activity.stopService() and Service.stopSelf() execute the Service.onDestroy() method immediately but then let the code in onHandleIntent() finish all the way through before destroying the service.

Since there is apparently no guaranteed way to terminate the service's thread immediately, the only recommended solution I can find (here) is to have a boolean member variable in the service that can be switched in the onDestroy() method, and then have just about every line of the code in onHandleIntent() wrapped in its own "if" clause looking at that variable. That's an awful way to write code.

Does anybody know of a better way to do this in an IntentService?

like image 897
pvans Avatar asked Jun 29 '12 07:06

pvans


3 Answers

Here is the trick, make use of a volatile static variable and check continue condition in some of lines in your service that service continue should be checked:

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

and in your activity do this to stop your service,

 MyService.shouldContinue = false;
like image 50
Sadegh Avatar answered Nov 18 '22 23:11

Sadegh


Stopping a thread or a process immediately is often a dirty thing. However, it should be fine if your service is stateless.

Declare the service as a separate process in the manifest:

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

And when you want to stop its execution, just kill that process:

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;
    }
}
like image 30
kupsef Avatar answered Nov 19 '22 01:11

kupsef


I've used a BroadcastReceiver inside the service that simply puts a stop boolean to true. Example:

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

Then, from the activity call:

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

To stop the service.

PD: I use a boolean because In my case I stop the service while in a loop but you can probably call unregisterReceiver and stopSelf in onReceive.

PD2: Don't forget to call unregisterReceiver if the service finishes it's work normally or you'll get a leaked IntentReceiver error.

like image 7
JoeyCK Avatar answered Nov 19 '22 00:11

JoeyCK