How to use Runnable.wait() in AsyncTask? Why does the AsyncTask NOT wait...?
android, android-asynctask, java, multithreading, runnable
Solution
The Basics
When you start an `AsyncTask` first the `onPreExecute()` method runs on the UI thread. You can override this method to make changes to the UI prior to the `doInBackground()` method running.
After the `doInBackground()` method finishes, the `onPostExecute()` method runs on the UI thread, so you can use this to make changes to the UI from here. If you need to make regular changes to the UI Thread during the `doInBackground()` method you override the `onProgressUpdate()` method which runs on the UI Thread, and then call it from within `doInBackground()`, which will allow you to periodically update the UI.
You could use something like the following;
private class DoStuffTask extends AsyncTask {
@Override
protected void doInBackground(Object... args) {
// Do stuff
onProgressUpdate(x);
// Do more stuff
}
@Override
protected void onProgressUpdate(Object... args) {
// Update your UI here
}
}
Now if this doesn't quite do it and you want the AsyncTask to wait for input during the `doInBackground()` method it is probably worth considering using multiple `AsyncTasks` instead. You can then finish each `AsyncTask`, ask for input, and then start a new `AsyncTask` to continue working.
Given that `AlertDialog` instances are asynchronous, this is probably the preferred solution because you can start the next `AsyncTask` from the `AlertDialog` itself.
Using wait() in an AsyncTask
If you would prefer to use a single `AsyncTask` you can use wait from within your `AsyncTask` to prevent execution continuing until some condition is met. Instead of using a new `Runnable` we are just using two threads in this instance, the thread running `doInBackground()` and the main thread, and we are synchronizing on the `AsycTask` itself.
Example below;
public class TestTask extends AsyncTask{
private boolean notified;
private Promptable p;
public interface Promptable { public abstract void prompt(); }
public TestTask(Promptable p){
this.p = p;
}
@Override
protected Object doInBackground(Object... arg0) {
Log.d("First", "First");
onProgressUpdate(null);
synchronized(this){
while(!notified){
try{
this.wait();
}
catch(InterruptedException e){ }
}
}
Log.d("Second", "Second");
return null;
}
@Override
protected void onProgressUpdate(Object... args){
synchronized(this){
notified = true;
p.prompt();
this.notify();
}
}
}
In the example above, assume that your `Activity` is parsed into the `AsyncTask`'s constructor, and that it implements an interface we create called `Promptable`. You'll notice that even though we're calling `wait()` we are putting it in a while loop. If we didn't do this, and somehow `notify()` got called before `wait()` then your thread would lock up indefinitely. Also, you can't depend on the fact that your thread will wait forever, so the while loop ensures that it doesn't continue until notify is called.
I hope this helps.
Problem
I am using AsyncTask to run a background operation. Of course switching to another thread while already working in a background thread does not make a lot of sense in general, except the other thread is the UI thread. This what I would like to to: While the task is running I need to "access" the UI, e.g. to show a dialog to ask the user how to proceed. - run the background task - stop the task at some point to get user feedback - switch to the UI thread to show dialog and ask for input - switch back to background task and continue work How can this be done? I thought I could use `Runnable` with `myActivity.runOnUiThread(runnable)` but this does not work: ``` private void copyFiles() { CopyTask copyTask = new CopyTask(this); copyTask.execute(); } // CustomAsyncTask is a AsyncTask subclass that takes a reference to the current // activity as parameter private class CopyTask extends CustomAsyncTask<Void, Void, Void> { private doCopy; @Override protected Boolean doInBackground(Void... params) { // Custom code, e.g. copy files from A to B and check for conflict for (File file : allFiles) { doCopy = true; if (isConflict(file)) { // Stop current thread and ask for user feedback on UI Thread Runnable uiRunnable = new Runnable() { public void run() { // Pos 1. --> Execute custom code, e.g. use AlertDialog to ask user if file should be replaced... doCopy = false; synchronized (this) { this.notify(); } } }); synchronized(uiRunnable) { // Execute code on UI thread activity.runOnUiThread(uiRunnable); // Wait until runnable finished try { uiRunnable.wait(); } catch (InterruptedException e ) { e.printStackTrace(); } } } // Pos 2. --> Continue work if (doCopy) copyFromAToB(File); } return null; } } ``` Within `doInBackground()` (--> in a background thread) the `AsyncTask` calls `activity.runOnUiThread(uiRunnable)`. Next `uiRunnable.wait()` is called. Regarding to the docu `wait()` should do the following: Causes the calling thread to wait until another thread calls the notify() or notifyAll() method of this object. Thus the background thread should wait to continue its work until `this.notify()` (== `uiRunnable.notifiy()`) is called on another thread (= the UI thread), shouldn't it? Well, id does not wait! After calling `uiRunnable.wait()` the background thread immediately continues by jumping to `if (doCopy)...`. It seems that the background thread and the main thread are executed in parallel (not surprising since this is what thread do...) and thus its a race condition whether `doCopy = false` on the UI thread or `if (doCopy)` on the background thread is reached first. How is this possible? Why doesn't `wait()` works as described? Or am I getting something wrong? Thank you very much! EDIT: To avoid missunderstandings: Of course I know the lifecycle methodes of AsyncTask but as far as I understand them, they are not what I am looking for (see my reply to the comment blow). Interrupting the AsyncTask as soon as a UI interaction is necessary, query the UI and start a new AsyncTask would be possible of course. However this would result in code which is very hard to read/understand/maintain. As I understand the docu of `wait()` everything should work fine here. Primary question is not how to do UI interaction during the lifecycle of an AsyncTask but why `wait()`does not work as expected.