2

EDIT: I've found that what I'm describing below only occurs on my emulated device (Nexus 5, target api 19, 4.4.2 with Intel Atom (x86) cpu), but NOT on my physical device (HTC One)....

EDIT2: Edit1 was due to an IllegalStateException that I didnt catch. Added some code to check if the thread was already running before trying to start it. This combined with the accepted answer resolved my issue.

I have implemented an activty that starts a new thread in the activity's onCreate method, like this:

...

private boolean running;

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

    running = true;
    new Thread(null, work, "myThread").start();
}

Runnable work = new Runnable() {

    @Override
    public void run() { 
        while (running) {
            //Doing work
        }
    }
};

I'm "pausing" my thread with my activity's onPause method, like this:

@Override
protected void onPause() {
    running = false;
    super.onPause();
}

So I thought that resuming it would be just as easy...¨

@Override
protected void onResume(){
    running = true;
    super.onResume();
}

but my thread isn't resuming. Any ideas why? Thankful for any help.

Marcus

Marcus
  • 6,697
  • 11
  • 46
  • 89

3 Answers3

2

All of the answers i think have some issues about your running variable because you can not write and read a variable from two different Threads without synchronized block so i post my own answer:

package com.example.threadandtoast;

import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.widget.Toast;

public class MainActivity extends ActionBarActivity {
    public class MonitorObject{
           public boolean running = true;
           public String message = "";
           public boolean mustBePost = true;
    }

    Thread t;
    int threadNameCounter = 0; // i use this variable to make sure that old thread is deleted 
                               // when i pause, you can see it and track it in DDMS

    Runnable work = new Runnable() {
        boolean myRunning;
        @Override
        public void run() {
            synchronized(mSync) {
               myRunning = mSync.running;
            }
            while (myRunning) {
                runOnUiThread(new Runnable() {  // in order to update the UI (create Toast)
                @Override                       // we must switch to main thread
                public void run() { 
                    // i want to read the message so i must use synchronized block
                    synchronized(mSync) { 
                    // i use this variable to post a message just for one time because i am in an infinite loop
                    // if i do not set a limit on the toast i create it infinite times 
                      if(mSync.mustBePost){       
                          Toast.makeText(MainActivity.this, mSync.message, Toast.LENGTH_SHORT).show();
                          // the message post so i must set it to false
                          mSync.mustBePost = false;
                          // if i am going to pause set mSync.running to false so at the end of infinite loop 
                          //of thread he reads it and leaves the loop
                          if(mSync.message.equals("Main Activity is going to pause")){
                              mSync.running=false;            
                          }
                      }
                    }
                }
               });

            synchronized(mSync) {
               myRunning = mSync.running;
           }
         }
       }
    };

    final MonitorObject mSync = new MonitorObject();

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

    }

    @Override
    protected void onPause() {
        super.onPause();
        synchronized(mSync) {
            // mSync.running = false; you can not set it here because 
            // it is possible for the thread to read it and exit the loop before he posts your message
            mSync.mustBePost=true;
            mSync.message = "Main Activity is going to pause";
        }    
    }

    @Override
    protected void onResume(){
       super.onResume();
       threadNameCounter++;
       synchronized(mSync) {
            mSync.running = true;
            mSync.mustBePost=true;
            mSync.message = "Main Activity is going to resume";
        } 
       t = new Thread(work,"My Name is " + String.valueOf(threadNameCounter));
       t.start();
    }

}

Or you can use this code:

public class MainActivity extends ActionBarActivity {

    Thread t;
    int threadNameCounter = 0; // i use this variable to make sure that old thread is deleted 
                               // when i pause, you can see it in DDMS

    String message = "";
    boolean isPost = false;

    Runnable work = new Runnable() {

        @Override
        public void run() {

            while (true) {
                runOnUiThread(new Runnable() {  
                @Override                       
                public void run() { 
                    if(!isPost){
                        Toast.makeText(MainActivity.this, message, Toast.LENGTH_SHORT).show();
                        isPost = true;
                        if( message.equals("Main Activity is going to pause")){
                            t.interrupt();
                        }

                    }
                }
               });
           if(Thread.currentThread().isInterrupted()){
               break;
           } 
         }
       }
    };


    @Override
    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

    }

    @Override
    protected void onPause() {
        super.onPause();
        message = "Main Activity is going to pause";
        isPost = false;
    }

    @Override
    protected void onResume(){
       super.onResume();
       message = "Main Activity is going to resume";
       isPost = false;
       threadNameCounter++;
       t = new Thread(work,"My Name is " + String.valueOf(threadNameCounter));
       t.start();
    }

}

you can also use semaphore or wait-notify approach.

i put public String message = ""; and public boolean mustBePost = true; in to mSync object but it is not necessary because only main thread have an access to them.

if you have any problem please ask.

mmlooloo
  • 18,937
  • 5
  • 45
  • 64
  • and the accepted answer is wrong because it is not thread safe. – mmlooloo Sep 21 '14 at 17:19
  • Elaborate on 'not thread safe' please. I have limited experience with threads but as far as I know, pausing/unpausing/stopping threads with booleans are fine. – Marcus Sep 21 '14 at 17:21
  • i can not explain it completely because it takes me much time so read these links:http://tutorials.jenkov.com/java-concurrency/thread-safety.html and http://tutorials.jenkov.com/java-concurrency/thread-safety-and-immutability.html and http://tutorials.jenkov.com/java-concurrency/synchronized.html – mmlooloo Sep 21 '14 at 17:27
  • I've read up a bit on thread safety and you are right, I will add the synchronized block and upvote – Marcus Sep 21 '14 at 17:30
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/61635/discussion-between-marcus-and-mmlooloo). – Marcus Sep 21 '14 at 17:33
  • i created a sample project and it is working look at it and run it. – mmlooloo Sep 21 '14 at 21:06
  • Is there any way to set the speed of the runnable? So for example I want to put a random number generator in there. It will spit out one number per 5 seconds... In 5 minutes that 5 secs will be 4.5 seconds... In 10 minutes the 4.5 secs will be 4 seconds? The 5 minute incremental will be static so even if i pause it will make sure it completes the full incremental time before decreasing the speed after it resumes. – Si8 Dec 02 '16 at 00:47
1

The statement running = false; will stop execution of the Thread, instead of pausing it. Use two variables: One for stopping current Thread, and another for pausing and resuming the Thread, as follow:

boolean isThreadPause=false;
Runnable work = new Runnable() {    
    @Override
    public void run() { 
        while (running) {
            if (!isThreadPause) {
               // Doing work
            }
        }
    }
};

In the onPause event of the Activity, set isThreadPause to true, and in the onResume event, set isThreadPause to false.

ChuongPham
  • 4,761
  • 8
  • 43
  • 53
ρяσѕρєя K
  • 132,198
  • 53
  • 198
  • 213
0

This is because your Runnable object stops when the while loop stops. You could try this:

Runnable work = new Runnable() {

    @Override
    public void run() { 
        while () {
          if(running){
            //Doing work
          }
        }
    }
};
Mohamed_AbdAllah
  • 5,311
  • 3
  • 28
  • 47