djagtorf
djagtorf

Reputation: 23

How to wake thread by condition

I have an activity running, of course, on UI thread and there is another thread running in background and communicating with activity using Handler post method(through looper).
When screen is turned of or application is hidden it continues to work.
So I need to stop this thread in onPause method and wake it up in onResume mehtod.
In my thread I have condition to pause it or to stop.
How to can I put thread to sleep in onPause method. And wake it up after activity is again in foreground.
I can do it with one object using monitor calling wait method and than notify on this object.
But is it good approach ? Or there is another way to do this elegantly.

Upvotes: 1

Views: 816

Answers (3)

Alexei Kaigorodov
Alexei Kaigorodov

Reputation: 13525

It is a bad practice to stop/resume a thread outside that thread. The thread must decide itself when to run and when to stop. As a result, the background thread should check periodically if its work is still needed, and the client (foreground) thread should issue some signals about that. One way to issue signals is to form that signals as jobs of type Runnable and then execute them on a thread pool. So when the activity sleeps, it just does not issue signals.

The main problem when a background thread wants to update the UI is that the target Activity can be closed (or in the process of recreation) and the updating task fails. The AcyncTask class does not solve this problem. A correct solution is published at my Github workspace. But before to use this or another solution, think twice if you really need a background thread. The best way is not to use background thread at all, making all UI updates directly on the UI thread. Of course, if updates are taken from the network, then a background thread must be used.

Upvotes: 0

Solomon Slow
Solomon Slow

Reputation: 27115

Sounds like a good place to use a turnstile. Initialize a Semaphore with one permit:

Semaphore turnstile = new Semaphore(1);

Make your background activity periodically pass through the turnstile like so:

turnstile.acquire();
turnstile.release();

When the foreground thread wants the background thread to pause at the turnstile, it can lock the turnstile:

turnstile.acquire();

And when the foreground thread wants that background thread to start working again, it can unlock the turnstile():

turnstile.release();

Good software engineering practice would be to wrap the whole thing up in a Turnstile class with appropriately named methods for the foreground and background threads to call. I'll leave that as an exercise for the reader.

Upvotes: 2

Eric S.
Eric S.

Reputation: 1502

Android suggests using services for long term background tasks, but if you're just opening a new thread that is tied to your Android lifecycle, I don't think it would be bad to use a monitor and call wait/notify. Can you be more specific with what you are doing?

This is an overview of how I would stop and resume a stopped thread. (You may want to implement runnable in yours)

class ThreadDemo extends Thread {
   private Object monitor; //This is the monitor
   private boolean keepRunning = true;
   private Thread t;

   ThreadDemo(){
       System.out.println("Creating thread");
   }

   public void callinOnResume(){
       synchronized(monitor){
           monitor.notify();
           }       
   }
   public void callinOnPause(){
           try {
                synchronized(monitor){
                    System.out.println(threadName +  "Waiting");
                    monitor.wait();
                }
            } catch (InterruptedException e) {
                System.out.println("Thread interrupted " + e.toString());
                }
    }

    public void run() {
       System.out.println("Starting to loop.");
        while (keepRunning) {
          //stuff
        }
        System.out.println("Done looping.");
    }

   public void start ()
   {
      System.out.println("Starting " +  threadName );
      if (t == null)
      {
         t = new Thread (this, threadName);
         t.start ();
      }
   }
 }

Upvotes: 1

Related Questions