48

When using a wait in an AsyncTask, I get ERROR/AndroidRuntime(24230): Caused by: java.lang.IllegalMonitorStateException: object not locked by thread before wait()

Is it possible to use an Asynctask just for waiting? How?

Thanks

class WaitSplash extends AsyncTask<Void, Void, Void> {
    protected Void doInBackground(Void... params) {
        try {
            wait(MIN_SPLASH_DURATION);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return null;
    }       

    protected void onPostExecute() {
        waitSplashFinished = true;
        finished();
    }
}  
Quentin Dunand
  • 233
  • 2
  • 11
jul
  • 36,404
  • 64
  • 191
  • 318
  • I realise that the splash screen is just an example here, but if anyone is here with the intention to do this for a splash screen, please [consider the user experience](https://stackoverflow.com/questions/5486789/how-do-i-make-a-splash-screen#comment6223750_5486789) and whether or not it's really necessary to delay your users' ability to use your app for the sake of some static image. – Alex Peters Nov 12 '17 at 04:44

6 Answers6

97

Use Thread.sleep() instead of wait().

LarryF
  • 4,925
  • 4
  • 32
  • 40
Flo
  • 27,355
  • 15
  • 87
  • 125
  • just be aware, this might have side effects while cancelling AsyncTask [**LINK**](http://stackoverflow.com/questions/4959628/android-asynctask-wont-stop-when-cancelled-why/4960971#4960971) – Samuel Nov 01 '11 at 08:25
  • 7
    Thread.sleep() should throw an InterruptedException if the AsyncTask is cancelled. – Jules Apr 06 '12 at 17:32
  • 3
    Doesn't this block everything else that is also using AsyncTask to run stuff (After honeycomb), because it uses the same thread? – mskw Jul 08 '14 at 18:50
  • @mskw Yes it would, but it sounds like he wants his UI thread to catch up. – Joshua Pinter Aug 16 '14 at 18:20
  • Additionally, you could use [executeOnExecutor(java.util.concurrent.Executor, Params...)](http://developer.android.com/reference/android/os/AsyncTask.html#executeOnExecutor%28java.util.concurrent.Executor,%20Params...%29) with [THREAD_POOL_EXECUTOR](http://developer.android.com/reference/android/os/AsyncTask.html#THREAD_POOL_EXECUTOR) for the first parameter to explicitly state that you want to execute in a pool of threads instead of a single one. This will provide pre-Honeycomb like behavior. Beware of possible side effects of parallel execution though! – olik79 Oct 07 '15 at 07:23
28

You can use Thread.sleep method

    try {
        Thread.sleep(1000);         
    } catch (InterruptedException e) {
       e.printStackTrace();
    }
Luis Zandonadi
  • 667
  • 7
  • 4
  • See comments above on accepted answer. This has significant downsides particularly if you have other AsyncTasks running - it will sleep them too. – Andy Weinstein Jul 16 '21 at 05:43
7
@Override
        protected String doInBackground(String... params) {
            // TODO Auto-generated method stub
            try {
                Thread.currentThread();
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            return null;

        }
Rahul Jain
  • 63
  • 1
  • 1
3

If you're looking to just postpone execution of a method for a set amount of time, a good option is Handler.postDelayed()

define the handler and runnable...

private Handler handler = new Handler();
private Runnable runnable = new Runnable() {        
    finished();
};

and execute with delay...

handler.postDelayed(runnable, MIN_SPLASH_DURATION);
Rich
  • 36,270
  • 31
  • 115
  • 154
  • I don't: while showing a splash screen I'm downloading some data. I want to launch an activity when the data is loaded, after a min time of 3s. My idea was to launch two threads with asynctask, one downloading the data and the other one just waiting 3s, and in both `onPostExecute` set a boolean to true and call a method checking that both are finished. – jul May 25 '11 at 10:37
  • I would create a Service with an AIDL to download the data completely separate from the splash Activity code and the code you're using to navigate to the next Activity. You want to delay by 3 seconds, but you don't want to cancel the data download if it takes slightly longer than 3 seconds. The only way to handle this scenario is to decouple the data fetching from the Activities – Rich May 25 '11 at 10:41
  • 1
    I think a service is exaggerated for this task. I would suggest to decouple the AsyncTask from the Splash activity and implement some kind of observer pattern so the next activity which might wait for the AsyncTask to finish gets notified. – Flo May 25 '11 at 10:52
  • @Rich: Shouldn't you implement the run method inside the runnable as well? Also, using handler inside the doInBackground of the async task gives me the error - 'Can't create handler inside thread that has not called Looper.prepare()'. – Basher51 Jul 21 '14 at 05:26
  • 1
    @Basher51 Yeah, the Runnable is just pseudocode really. The finished method would be called inside the run method. The error you're getting is because the Handler needs to be created on the UI thread. If your AsyncTask is an inner class inside your activity, you just need to scope the Handler to the Activity and instantiate it there. If not, you probably need another solution. Maybe attach a listener to the AsyncTask that will call back to the Activity or something like that. – Rich Jul 21 '14 at 23:24
1

Use threads for this

public class SplashActivity extends Activity{

int splashTime = 5000;
private Thread splashThread;
private Context mContext;

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    this.mContext = this;
    setContentView(R.layout.splash_layout);
    splashThread = new Thread(){
        public void run() {
            try{
                synchronized (this) {
                    wait(splashTime);
                }
            }catch(InterruptedException ex){
                ex.printStackTrace();
            }finally{
                Intent i = new Intent(mContext,LocationDemo.class);
                startActivity(i);
                stop();
            }
        }
    };

    splashThread.start();
}

public boolean onTouchEvent(MotionEvent event) {
    if (event.getAction() == MotionEvent.ACTION_DOWN) {
        synchronized (splashThread) {
            splashThread.notifyAll();
        }
    }
    return true;
}

on touch event, thread get notified.. can change according to your need.

Iharob Al Asimi
  • 52,653
  • 6
  • 59
  • 97
Zoombie
  • 3,590
  • 5
  • 33
  • 40
  • Hi! Is it needed to have `synchronized (splashThread)` at `onTouchEvent` before `splashThread.notifyAll()`? – Kobor42 Feb 29 '12 at 03:17
  • Yes. Calls to wait() and notify() or notifyAll() must be synchronized. You should also be setting a flag to check that the two events haven't occurred in a different order to the one you expect (i.e. you should have something like "if (!splashDismissed) wait(splashTime);" and "splashDismissed = true; splashThread.notifyAll();" inside the synchronized blocks, otherwise there is a very slight chance that initializing the splash thread was delayed for some reason and the notifyAll() call actually happens first). – Jules Apr 06 '12 at 17:35
0

You have this way to work with asyntask and wait();

public class yourAsynctask extends AsyncTask<Void, Void, Void> {
    public boolean inWait;
    public boolean stopWork; 

    @Override
    protected void onPreExecute() {
        inWait = false;
        stopWork = false;
    }

    @Override
    protected Void doInBackground(Void... params) {
        synchronized (this) {
            while(true) {
                if(stopWork) return null;
                if(youHaveWork) {
                    //make some
                } else {
                    try {
                        wait();
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            }
        }
        return null;
    }

    public void mynotify() {
        synchronized (this) {
            if(inWait) {
                notify();
                inWait = false;
            }
        }
    }

    public void setStopWork() {
        synchronized (this) {
            stopWork = false;
            if(inWait) {
                notify();
                inWait = false;
            }
        }
    }
}
Cifus
  • 97
  • 7