How to Pause/Resume Thread in Android

How to pause/resume thread in Android?

Use wait() and notifyAll() properly using a lock.

Sample code:

class YourRunnable implements Runnable {
private Object mPauseLock;
private boolean mPaused;
private boolean mFinished;

public YourRunnable() {
mPauseLock = new Object();
mPaused = false;
mFinished = false;
}

public void run() {
while (!mFinished) {
// Do stuff.

synchronized (mPauseLock) {
while (mPaused) {
try {
mPauseLock.wait();
} catch (InterruptedException e) {
}
}
}
}
}

/**
* Call this on pause.
*/
public void onPause() {
synchronized (mPauseLock) {
mPaused = true;
}
}

/**
* Call this on resume.
*/
public void onResume() {
synchronized (mPauseLock) {
mPaused = false;
mPauseLock.notifyAll();
}
}

}

How to pause and resume a Thread?

You can use wait() and notify() method.

wait()

Causes the 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).

notify()

Wakes up a single thread that is waiting on this object's monitor. If any threads are waiting on this object, one of them is chosen to be awakened. The choice is arbitrary and occurs at the discretion of the implementation. A thread waits on an object's monitor by calling one of the wait methods.

Android: Pause and resume a thread within an activity

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.

Generic Thread which could be paused/resumed and execute user's Runnable Actions

You asked: "But this doesn't work. Why?"

I answer:
Your solution does not work because you are always running in the loop inside runUserAction. You never break out of that loop to check if you are paused.

I'm afraid you'll have to remodel your solution to run usrAction in shorter loops, otherwise you will either lose state (assuming you interrupt that loop from outside), which will end up in undefined behavior, OR you will only break out of it when it's over, OR you'll pause your loop at states you don't really want to pause at [e.g. while making a network call -- after resumed you'll get a SocketTimeoutException].

I'd suggest you to go with the former approach as it's more elegant.

Edit:

Another possible solution: every iteration inside the usrAction check for PausableThread's state, i.e. see whether it's paused, stopped or whatever.

Try this:

PausableRunnable.java

    public synchronized boolean canContinue() throws Exception {
synchronized (lockerObject) {
if (isPaused) {
lockerObject.wait();
}
if (isFinished) {
return false;
}
return true;
}
}

PausableThread.java

    public boolean canContinue() throws Exception {
return runnable.canContinue();
}

and the Application.java

private void initThread() {
Runnable r = () -> {
try {
while (totalRunTime > 0) {
if (bgThread.canContinue()) { // <--- !!!!!!
Thread.sleep(200);
totalRunTime--;
updateUi();
}
}
}
catch (Exception e) { e.printStackTrace(); }
};
bgThread = new PausibleThread(r);

}

This way you can run your application Runnable and still obey PausableThread's states at the times the runnable can tollereate. I.e. before/after transaction or other piece of calculation that is not supposed to be interrupted.

Edit 2:

feel free to lose ´synchronized´ modifier on methods like pause or resume, since you are already operating inside synchronized blocks in them.

How pause and then resume a thread?

Using wait() and notify() methods:

wait() - Causes the current thread to wait until another thread invokes the
notify() method or the notifyAll() method for this object.

notify() - Wakes up a single thread that is waiting on this object's monitor.

how to pause thread in buttonOnClick

See this link
How to pause/resume thread in Android?

Pause/Resume a Thread

You need to change your download() method to check for a stop or pause event and then stop or pause the thread. It has to be this way as the JVM does not know what steps need to be done in order to safely pause/stop the thread.

You may end up using wait but not the way you are using it. wait causes the currently running thread to wait until some calls notify on the object you have called wait on.

In the download method you have a loop (read a block, write a block). You should add two checks in the loop.

while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
if(paused) {
// How do you want to handle pausing? See below for options.
}
if(stopped) {
// delete the file and close the streams.
}
}

How you handle pausing is up to you. I can see two options: Save what you have as ".incomplete" and later restart the download using the Range header or continue looping with a pause (Thread.sleep, Object.wait or whatever).
I would go with the first option (Range Header). It is more work but also more robust.



Related Topics



Leave a reply



Submit