Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Waiting for a Runnable to complete before running another Runnable

Tags:

java

android

I have an Android app with a main tab activity, and several activities within the individual tabs. In my main activity's onCreate(), I have a runnable that creates a list, and in the individual activities, I make use of this list.

In the individual activities's onCreate(), I also have Runnables that operate on the list. However, I need these Runnables to only run when the main tab activity's Runnable completes creating the list, otherwise I'd get a null list. I'm trying to find an elegant way of doing this. Right now, in my main activity's Runnable, I'm setting a global boolean variable isDone, and in my individual activity's Runnable, I'm waiting for isDone to be set via a while loop. This works, but probably isn't the best way of doing so.

Any thoughts?

Thanks.

Edit: I'm trying the following code out, but I'm getting runtime errors:

In my MainActivity's Runnable:

mainRunnable = new Runnable() {
  public void run() {
    try {
      generateList();
      synchronized(this) {
      listDone = true;
      notifyAll();
    }
  } catch (Exception e) {
      Log.e("BACKGROUND_PROC", e.getMessage());
    }
  }
};
Thread thread = new Thread(null, mainRunnable, "Background");
thread.start();

In my OtherActivity's Runnable:

otherRunnable = new Runnable() {
  public void run() {
    synchronized(MainActivity.mainRunnable) {
      if (!MainActivity.getListDone()) {
        try {
          wait();
        } catch (InterruptedException e) {
        }
      }
    }
  }
};
Thread thread = new Thread(null, otherRunnable, "Background");
thread.start();

The mainRunnable seems to run completely, but the otherRunnable seems to cause the app to crash. I get the following error message:

01-10 15:41:25.543: E/WindowManager(7074): Activity com.myapp.MainActivity has leaked window com.android.internal.policy.impl.PhoneWindow$DecorView@40539850 that was originally added here
01-10 15:41:25.543: E/WindowManager(7074): android.view.WindowLeaked: Activity com.myapp.MainActivity has leaked window com.android.internal.policy.impl.PhoneWindow$DecorView@40539850 that was originally added here
like image 277
user1118764 Avatar asked Jan 10 '12 06:01

user1118764


People also ask

How do you wait for a runnable to finish?

Create an Object called lock . Then after runOnUiThread(myRunnable); , you can call lock. wait() .

How do you create a wait method in Java?

wait() causes current thread to wait until another thread invokes the notify() method or the notifyAll() method for this object. In other words, this method behaves exactly as if it simply performs the call wait(0). The current thread must own this object's monitor.

What does runnable run do?

run. When an object implementing interface Runnable is used to create a thread, starting the thread causes the object's run method to be called in that separately executing thread. The general contract of the method run is that it may take any action whatsoever.

How do you pause a runnable?

The thread can go into runnable state by using the static method Thread. sleep(duration). This will pause the current thread to stop executing for that duration. Else if you want your thread to execute after some thread has finished then you can use the join() method of the thread class.


4 Answers

You can use the wait and notify methods.

To do this, there needs to be some globally accessible object whose lock isn't used by anything else in the program at this point in time. I'm assuming that the list-creating Runnable itself can play this role.

So you could add something like this to the list-creating Runnable class:

private boolean listsDone = false;

boolean getListsDone() {
    return listsDone;
}

And something like this to its run() method, immediately after it's done creating the lists:

synchronized (this) {
    listsDone = true;
    notifyAll();
}

And something like this to the other Runnables' run() methods, at the point where they need to wait:

synchronized (listCreatingRunnableObject) {
    if (!listCreatingRunnableObject.getListsDone()) {
        try {
            listCreatingRunnableObject.wait();
        } catch (InterruptedException e) {
            // handle it somehow
        }
    }
}

Update: To clarify, both synchronized blocks need to be synchronized over the same object, and you have to call wait() and notifyAll() on that object. If the object is the Runnable, then it can be implicit for the first one (as in the above code), but if it's the activity, you need to explicitly use the activity object in both cases.

like image 183
Taymon Avatar answered Oct 05 '22 08:10

Taymon


You can use a Queue like this:

public class RunQueue implemements Runnable
{
  private List<Runnable> list = new ArrayList<Runnable>();

  public void queue(Runnable task)
  {
    list.add(task);
  }

  public void run()
  {
    while(list.size() > 0)
    {
      Runnable task = list.get(0);

      list.remove(0);
      task.run();
    } 
  }
}

This allows you to use one thread rather than multiple threads. And you can maintain all your existing "Runnable" objects while simultaneously cleaning up any code they have for waits and joins.

like image 33
64BitBob Avatar answered Oct 05 '22 09:10

64BitBob


Set up a CountDownLatch with a value of 1 in the main thread, then have the dependent threads wait on it. When the main thread is done, you Count Down the latch to 0 and the waiters will start right up.

like image 26
Will Hartung Avatar answered Oct 05 '22 09:10

Will Hartung


An active wait using a while loop is not a good idea at all. The simplest thing would be for the first Runnable to just fire up the rest of them as its last step. If that can't be made to work for some reason, take a look at posting a message to a Handler.

like image 38
Ted Hopp Avatar answered Oct 05 '22 10:10

Ted Hopp