Shreya BS Shreya BS - 10 days ago 7
Android Question

Android Service shuts down while Runnable is still executing

I have a Service in my app with which I send requests to a server. After I send a request to start an operation on the server, I need to poll the server for updates by sending a request every 15 seconds to track the progress. For this I use a ScheduledExecutorService that calls a Runnable.

The Service does a bunch of things in

onHandleIntent
and then starts the ScheduledExecutorService that does the polling. All of this is working fine.

The issue is while the ScheduledExecutorService is executing the Runnable at intervals, the service gets destroyed, as in the onDestroy method is called. Is there a way to prevent this from happening?

I have a Broadcast Receiver registered in the service that can be triggered any time by the user while the ScheduledExecutorService is executing the Runnable. But since the Service is destroyed, the Receiver gets unregistered.

Here is some simplified code:

@Override
protected void onHandleIntent(Intent intent) {
Log.d(TAG, "OnHandleIntent started");

try {

if (intent != null) {
final String action = intent.getAction();

if (getActionUpload().equals(action)) {

wakeLock.acquire();
notificationConfig = new CustomNotificationConfig();

//Register receiver to stop operation
IntentFilter filter = new IntentFilter(ThirdPartyStopReceiver.ACTION_STOP);
filter.addCategory(Intent.CATEGORY_DEFAULT);
mThirdPartyStopReceiver = new ThirdPartyStopReceiver();
registerReceiver(mThirdPartyStopReceiver, filter);

Boolean operationStarted = startOperation();

if (operationStarted) {
checkProgressAtIntervals();
}
}
}

} catch (Exception e) {
Log.e(TAG, "Error with XML");
e.printStackTrace();
} finally {
wakeLock.release();
}
}


@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy");
try {
//Unregister the receiver
unregisterReceiver(mThirdPartyStopReceiver);
} catch (IllegalArgumentException e) {
//Receiver has already been unregistered. Do nothing
e.printStackTrace();
}
}

private void checkProgressAtIntervals() {
Log.d(TAG,"in checkProgressAtIntervals");

final ScheduledExecutorService ses = Executors.newSingleThreadScheduledExecutor();

ses.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
try {
int progress = sendProgressRequest();

if(isCanceled){
sendCancelRequest();
ses.shutdown(); //Stop polling
}

if (progress >= 100) {
ses.shutdown(); //Stop polling
}

} catch (Exception e) {
Log.e(TAG, "Exception in sendProgressRequest ");
e.printStackTrace();
}
}
}, 1, 15, TimeUnit.SECONDS);

}

public class ThirdPartyStopReceiver extends BroadcastReceiver {

public static final String ACTION_STOP = "thirdpartystop";

@Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "Cancel request received in onReceive");
isCanceled = true;

//...

}
}


So as you can see, I need to execute some code in the service if a "Stop" broadcast is received.

I have tried using a Handler with its
postDelayed
method but the service gets destroyed before the Handler starts to execute so the Runnable never executes. I tried instantiating the Handler with
Looper.myLooper()
but with the same result.
Since the runnable contains HTTP calls, instantiating Handler with
Looper.getMainLooper()
throws a
NetworkOnMainThreadException

Answer

It seems you are using an IntentService, which stops after it ends its work, that is, the onHandleIntent method. An IntentService is intended to be short-lived.

Try using a regular Service instead. Mind that Android can stop a Service at its own discretion, but in a general manner the Service will run for a long period.

Comments