This is an extension of my question here, where you can see all of my code for both the fragment containing the button and the Service being used: Android sending messages between fragment and service
I have a button, which when clicked will start a Service, collect some sensor data, insert into database. When clicked again, I want to display a progress dialog, wait for existing background tasks to finish and then dismiss the progress dialog.
I can now correctly send messages back and forth between the fragment and service using a Handler
in the fragment (as can be seen in my other question). The problem is that the progress dialog doesn't show in the UI thread until AFTER the executor task queue has been cleared
In my fragment, the button onClick
looks like this:
//Show stop dialog
stopDialog = new ProgressDialog(mainActivity);
stopDialog.setMessage("Stopping...");
stopDialog.setTitle("Saving data");
stopDialog.setProgressNumberFormat(null);
stopDialog.setCancelable(false);
stopDialog.setMax(100);
stopDialog.show();
Log.d(TAG, "dialog up");
//Stop the service
mainActivity.stopService(new Intent(mainActivity, SensorService.class));
and my Service onDestroy
looks like this:
public void onDestroy() {
//Prevent new tasks from being added to thread
executor.shutdown();
Log.d(TAG, "Executor shutdown is called");
try {
//Wait for all tasks to finish before we proceed
while (!executor.awaitTermination(1, TimeUnit.SECONDS)) {
Log.i(TAG, "Waiting for current tasks to finish");
}
} catch (InterruptedException e) {
executor.shutdownNow();
Thread.currentThread().interrupt();
}
if (executor.isTerminated()){
//Stop everything else once the task queue is clear
unregisterReceiver(receiver);
unregisterListener();
wakeLock.release();
dbHelper.close();
stopForeground(true);
//Dismiss progress dialog - commented out for debugging
//sendMessage("HIDE");
}
}
Expectation
When the Stop button is clicked, the progress dialog should show, onDestroy
gets called in the service, the executor queue finishes existing tasks, and then dialog gets dismissed via a message to a handler (this last part has been commented out so I can figure out what is going on)
Reality
When Stop is clicked, in logcat I can see the message dialog up
, followed by Executor shutdown is called
. While its finishing the current task queue I can see Waiting for current tasks to finish
. So the order of events is correct, but the progress dialog doesn't actually display until after all the onDestroy
code, even though (in the fragment) I tell it to show before the service is even told to stop.
I also see a lot of skipped frames after I click the Stop button, presumably while the executor is trying to clear its queue: Skipped 336 frames! The application may be doing too much work on its main thread.
Though I'm not sure why an executor working in a background thread is causing the UI to not update
Whats going on? UI blocking?
In my mind the dialog should show, and THEN service is told to stop, where it will work through the rest of the executor task queue, and then get dismissed. But for some reason the dialog show isn't happening until after the executor completely terminates.
Is there something in my code thats blocking the UI thread from showing the progress dialog? I would have thought that because the executor is running in a background thread that the UI thread will be free to show/display things like dialogs
Note: I know I can easily solve this using an AsyncTask
, and theres a similar problem/solution to it here, but I want to use an executor for this. Most questions deal with activities and asynctasks, but my question uses a strange combination of fragments, services and executors, for which I cannot find any questions/answers
EDIT:
After some googling I found this question here, which suggests that awaitTermination
might be blocking the UI thread. So I took most of my onDestroy
code that waits for the executor queue to clear and moved it into a new thread:
public void onDestroy() {
//Prevent new tasks from being added to thread
executor.shutdown();
Log.d(TAG, "Executor shutdown is called");
new Thread(new Runnable() {
public void run() {
try {
//Wait for all tasks to finish before we proceed
while (!executor.awaitTermination(1, TimeUnit.SECONDS)) {
Log.i(TAG, "Waiting for current tasks to finish");
}
} catch (InterruptedException e) {
executor.shutdownNow();
Thread.currentThread().interrupt();
}
if (executor.isTerminated()) {
//Stop everything else once the task queue is clear
unregisterReceiver(receiver);
unregisterListener();
wakeLock.release();
dbHelper.close();
stopForeground(true);
//Dismiss progress dialog
sendMessage("HIDE");
}
}
}).start();
}
Now when I hit Stop, it seems like the progress dialog shows instantly, but I also get this exception every time:
Exception dispatching input event. JNI DETECTED ERROR IN APPLICATION: JNI CallObjectMethod called with pending exception java.util.concurrent.RejectedExecutionException: Task com.example.app.SensorService$InsertHandler@35e04d3 rejected from java.util.concurrent.ThreadPoolExecutor@2b28810[Shutting down, pool size = 1, active threads = 1, queued tasks = 481, completed tasks = 2069]
(that is just the first part of the exception - it is very long)
I'm not sure what is happening. While that new thread is starting, the executor thread is running execute
to finish the tasks in its current queue. Is this causing a problem in how the 2 threads are working concurrently?