I have an activity with 3 buttons: create, start and cancel. Button create creates a new thread, button start runs it and button cancel stops this thread. And my problem is that thread isn't interrupted after calling interrupt method (this action is performed after clicking on cancel button). I know, that in my thread I should check if thread is interrupted or no. I added it, but interrupting still doesn't work. Here's my code:
private View.OnClickListener listener = new View.OnClickListener() {
#SuppressLint("HandlerLeak")
#Override
public void onClick(View v) {
switch (v.getId()){
case R.id.create_button:
thread = new Thread(new Runnable() {
#Override
public void run() {
int i;
for (i = 0; i<10;i++){
final int finalI = i;
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
textCounter.post(new Runnable() {
#Override
public void run() {
textCounter.setText(String.valueOf(finalI));
}
});
}
}, 500*i);
if (thread.isInterrupted()){
Log.i(getClass().getSimpleName(), "Thread is interrupted");
return;
}
}
new Handler().postDelayed(new Runnable() {
#SuppressLint("SetTextI18n")
#Override
public void run() {
textCounter.setText("Done!");
}
},(i+1)*500);
}
});
break;
case R.id.start_button:
thread.run();
break;
case R.id.cancel_button:
thread.interrupt();
Log.i(getClass().getSimpleName(), "Cancel Button clicked");
break;
}
}
};
So, why thread isn't interrupted and how can I solve this problem?
Your thread is quickly adding 11 tasks (10 + the last one) to this handler you're creating and then dying. These tasks have a delay, and then the message queue will take care of running the 10 + 1 runnables. To do what you're trying to do you should make the thread to wait 500ms between each loop.
Something similar to this:
thread = new Thread(new Runnable() {
#Override
public void run() {
int i;
for (i = 0; i<10;i++){
final int finalI = i;
textCounter.post(new Runnable() {
#Override
public void run() {
textCounter.setText(String.valueOf(finalI));
}
});
try {
Thread.sleep(500);
} catch (InterruptedException ignored) {
Log.i(getClass().getSimpleName(), "Thread is interrupted");
return;
}
if (thread.isInterrupted()){
Log.i(getClass().getSimpleName(), "Thread is interrupted");
return;
}
}
textCounter.post(new Runnable() {
#Override
public void run() {
textCounter.setText("Done!");
}
});
}
});
There are many problems with your code
Problem 1: When users click on Create button, you create a new thread, it is not expected behavior as you want, so just create a new thread if it is not created yet or terminated.
Problem 2: When users click on Start button
thread.run();
This line does not start the thread, it just executes the code in run method on calling thread, in this case main/UI thread. To start a thread you must use start method. Make sure you start the thread if it is has been created.
Problem 3: When users click on Cancel button
thread.interrupt();
Because there is no thread started so this line will do nothing.
Problem 4: From your description, you want to use a thread to increase counter on a TextView from 0 to 9 each 0.5 second, then display "Done". Your code is incorrect and contains many redundant.
Solution: You can following this code.
private View.OnClickListener listener = new View.OnClickListener() {
#Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.create_button:
// Only create a new thread if it is not created or it is terminated.
if (thread == null || thread.getState() == Thread.State.TERMINATED) {
thread = new Thread(new Runnable() {
#Override
public void run() {
try {
for (int i = 0; i < 10; i++) {
final int finalI = i;
// This will post a message to main/UI thread.
textCounter.post(new Runnable() {
#Override
public void run() {
textCounter.setText(String.valueOf(finalI));
}
});
// Sleep current thread in 0.5 second before running next step.
Thread.sleep(500);
}
// Display Done after finishing counter.
textCounter.post(new Runnable() {
#SuppressLint("SetTextI18n")
#Override
public void run() {
textCounter.setText("Done!");
}
});
} catch (InterruptedException e) {
// Display Cancelled if the current thread is cancelled.
textCounter.post(new Runnable() {
#SuppressLint("SetTextI18n")
#Override
public void run() {
textCounter.setText("Cancelled!");
}
});
}
}
});
} else {
Toast.makeText(MainActivity.this,
"Thread is already created. No need to create anymore.",
Toast.LENGTH_SHORT)
.show();
}
break;
case R.id.start_button:
// Start thread if it is created.
if (thread != null && thread.getState() == Thread.State.NEW) {
thread.start();
} else {
Toast.makeText(MainActivity.this,
"Thread is not created yet or it is running.",
Toast.LENGTH_SHORT)
.show();
}
break;
case R.id.cancel_button:
// Cancel the thread if it is running.
if (thread != null && thread.isAlive()) {
thread.interrupt();
} else {
Toast.makeText(MainActivity.this,
"Thread is not running yet.",
Toast.LENGTH_SHORT)
.show();
}
break;
}
}
};
Related
I am struggling with setting the text of a TextView, so I am now trying to do it with a button push, but when I start the thread from the button readWeight the button updateButton does not work.
Here are my two button onClick methods:
readWeight.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
inputWindow.setText("helloooooooo worldddddd");
//connector.run();
System.out.println("********** PRINTING **********");
// readWeight.setVisibility(View.INVISIBLE);
}
});
updateButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
System.out.println("!!!!!!!!!!!!!!!"+weight+"!!!!!!!!!!!!!!!");
inputWindow.setText(weight);
}
});
and here is my method that starts the thread, this method is in another class:
public void run() {
new Handler().post(new Runnable() {
#Override
public void run() {
// Always cancel discovery because it will slow down a connection
//Log.d("workkkkkk","$$$$$$$$$$$$$$$$****** printingggggg ******$$$$$$$$$$$$$$$$");
int counter = 0;
while (true) {
counter++;
try {
output = "";
//read the data from socket stream
//mmInStream != null && counter%10000000 == 1
if (mmInStream != null) {
mmInStream.read(buffer);
for (byte b : buffer) {
char c = (char) b;
if (c >= ' ' && c < 'z') {
// System.out.print(c);
output += c;
}
}
System.out.println();
Intent intent = new Intent();
intent.setAction("com.curie.WEIGHT_RECEIVED");
intent.putExtra("Output",output);
if (counter % 10 == 0) {
System.out.println(counter);
//InputActivity.setInputWindowText(output);
LocalBroadcastManager.getInstance(InputActivity.getContext()).sendBroadcastSync(intent);
}
}
// Send the obtained bytes to the UI Activity
} catch (IOException e) {
//an exception here marks connection loss
//send message to UI Activity
break;
}
}
}
Any help would be very appreciated! thank you.
When you use
Handler.post()
It runs in UI thread, so if it is long action it will block all interface.
To avoid it you should run it in another thread. If you don't want to use something complicated you can try this:
mHandler = new Handler();
new Thread(new Runnable() {
#Override
public void run () {
mHandler.post(new Runnable() {
#Override
public void run () {
// place your action here
}
});
}
}).start();
Sir Please help me to add a thread that starts on a button click and ends the thread with another button click. In between I have a sound playing till the thread stops.
You can try this simple code:
final volatile boolean toExit = false;
final Thread t = new Thread(new Runnable() {
#Override
public void run() {
while(!toExit){
// Your code
Thread.sleep(100);
}
}
});
findViewById(R.id.button1).setOnClickListener(new OnClickListener() {
#Override
public void onClick(View arg0) {
t.start();
}
});
findViewById(R.id.button2).setOnClickListener(new OnClickListener() {
#Override
public void onClick(View arg0) {
toExit = true;
}
});
The thread will stop after button2 clicked and run to while(!toExit).
Threads stop method is deprecated.
The best solution will be having a boolean variable in the run method.
Your Thread:
public class MyThread implements Runnable {
private static final Logger LOGGER = LoggerFactory.getLogger(IndexProcessor.class);
private volatile boolean running = true;
public void terminate() {
running = false;
}
#Override
public void run() {
while (running) {
try {
//Your code that needs to be run multiple times
LOGGER.debug("Processing");
} catch (InterruptedException e) {
LOGGER.error("Exception", e);
running = false;
}
}
}
}
In your Activity:
MyThread t=new Thread();
findViewById(R.id.button1).setOnClickListener(new OnClickListener() {
#Override
public void onClick(View arg0) {
t.start();
}
});
findViewById(R.id.button2).setOnClickListener(new OnClickListener() {
#Override
public void onClick(View arg0) {
t.terminate();
}
});
Use Below Code
public class SomeBackgroundProcess implements Runnable {
Thread backgroundThread;
public void start() {
if( backgroundThread == null ) {
backgroundThread = new Thread( this );
backgroundThread.start();
}
}
public void stop() {
if( backgroundThread != null ) {
backgroundThread.interrupt();
}
}
public void run() {
try {
Log.i("Thread starting.");
while( !backgroundThread.interrupted() ) {
doSomething();
}
Log.i("Thread stopping.");
} catch( InterruptedException ex ) {
// important you respond to the InterruptedException and stop processing
// when its thrown! Notice this is outside the while loop.
Log.i("Thread shutting down as it was requested to stop.");
} finally {
backgroundThread = null;
}
}
Hope this will help you
Sorry for my english. I have timer and i wand if i click timer is on if i click again timer off. But my timer on only one time. If i click again(off timer) i have exception like this:
E/AndroidRuntime﹕ FATAL EXCEPTION: main
java.lang.IllegalStateException: Timer was canceled
at java.util.Timer.scheduleImpl(Timer.java:561)
at java.util.Timer.schedule(Timer.java:481)
at installation.ConnectDevice.callAsynchronousTask(ConnectDevice.java:211)
at installation.ConnectDevice$1.onClick(ConnectDevice.java:153)
at android.view.View.performClick(View.java:4240)
...
I dont know why its not work, please help. Below my class
My class
private Timer timer;
int time = 0;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.i_connect_device);
timer = new Timer();
// my botton
includeDevice.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (time < 1) {
callAsynchronousTask();
time++;
}
if (time > 0) {
stopTimer();
time--;
}
}
});
}
public void callAsynchronousTask() {
final Handler handler = new Handler();
TimerTask doAsynchronousTask = new TimerTask() {
#Override
public void run() {
handler.post(new Runnable() {
public void run() {
try {
Log.e("Timer is work", "Timer is work");
// GetMsgs performBackgroundTask = new GetMsgs();
// PerformBackgroundTask this class is the class
// that extends AsynchTask
// performBackgroundTask.execute();
} catch (Exception e) {
// TODO Auto-generated catch block
}
}
});
}
};
timer.schedule(doAsynchronousTask, 0, 1000 * 10); // execute in every
// 50000 ms
}
public void stopTimer() {
timer.cancel();
}
Change onClick logic as follows (because in your case at the first time only executed callAsynchronousTask() and stopTimer(). so it raises exception at next onClick)
btnTimer.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (time == 0) {
callAsynchronousTask();
time = 1;
} else {
stopTimer();
time = 0;
}
}
});
and doAsynchronousTask make it as field and cancel task on stopTimer().
public void stopTimer() {
doAsynchronousTask.cancel();
}
then it works fine.
From Javadocs:
cancel() : Terminates this timer, discarding any currently scheduled tasks. [...] Once a timer has been terminated, its execution thread terminates gracefully, and no more tasks may be scheduled on it.
and
schedule(Task task, long delay)
throws:
IllegalStateException - if task was already scheduled or cancelled, timer was cancelled, or timer thread terminated.
So basically, your class tells you that it cannot complete the set task due to the fact that the timer has been cancelled. Something you could try is to make the timer sleep until the button is pressed again, instead of cancelling it completely.
Once you cancel the timer; you cannot start it again because thread is stopped.See the link:Pause the timer and then continue itYou have to maintain the state somehow and recreate timer with the current value
You need an async task it is a class so you can extend it. Public class callAsynchronousTask extends async task
And GetMsgs performBackgroundTask = new GetMsgs();
// PerformBackgroundTask this class is the class that extends Async Taskbar goes into the do in background method
Initialize your timer object inside callAsynchronousTask as shown below.
public void callAsynchronousTask() {
final Handler handler = new Handler();
TimerTask doAsynchronousTask = new TimerTask() {
#Override
public void run() {
handler.post(new Runnable() {
public void run() {
try {
Log.e("Timer is work", "Timer is work");
//GetMsgs performBackgroundTask = new GetMsgs();
// PerformBackgroundTask this class is the class that extends AsynchTask
//performBackgroundTask.execute();
} catch (Exception e) {
// TODO Auto-generated catch block
}
}
});
}
};
timer=new Timer();
timer.schedule(doAsynchronousTask, 0, 1000*10); //execute in every 50000 ms
}
Also modify your in block because it is executing both if condition .
Use boolean flag instead of int
boolean isTimerRunning;
if (!isTimerRunning) {
callAsynchronousTask();
isTimerRunning=true;
}
else (isTimerRunning) {
stopTimer();
isTimerRunning=false;
}
I have this code:
if (value) {
thread = new Thread() {
#Override
public void run() {
try {
while (!isConnected()) {
synchronized (this) {
wait(3000);
}
}
} catch (InterruptedException ex) {
}
if(wifiManager.isWifiEnabled()){
sendMessageWidget();
} else {
showWifiSettingsAlert();
}
}
};
thread.start();
}
I want my app to wait until google api client is connected and than send a message.
The code for the isConnected method is:
public boolean isConnected() {
mGoogleApiClient.connect();
if (mGoogleApiClient.isConnected()) {
return true;
}
return false;
}
But I get this error message:
NullPointerException: Can't create handler inside thread that has not called Looper.prepare(), and it says that the mistake is somewhere id showWifiSettingsAlert()
here is the code:
public void showWifiSettingsAlert() {
AlertDialog.Builder alertDialog = new AlertDialog.Builder(getActivity());
// Setting Dialog Title
alertDialog.setTitle("Location accuracy tips");
// Setting Dialog Message
alertDialog
.setMessage("You can improve the accuracy of your location by turning on\n- Wi-Fi");
// On pressing Settings button
alertDialog.setPositiveButton("Turn on",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
wifiManager.setWifiEnabled(true);
// Posalji poruke al pre toga jos jednom azuriraj
// lokaciju al ako je pozvana aplikacija iz widgeta
if (value) {
sendMessageWidget();
}
}
});
// on pressing cancel button
alertDialog.setNegativeButton("Cancel",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
wifiManager.setWifiEnabled(false);
// Posalji poruke al pre toga jos jednom azuriraj
// lokaciju al ako je pozvana aplikacija iz widgeta
if (value) {
sendMessageWidget();
}
}
});
// Showing Alert Message
alertDialog.show();
}
I want, if wifi is not enabled, the user to choose to enable it or not, but either way the message should be sent... can you help please?
Since you can't touch the UI from a thread other than the main thread, you must post these changes back to the UI thread and its looper and associated handlers. You can do so explicitly by creating a handler associated with the UI thread (which will work anywhere, since Looper.getMainLooper() is a static call) such as:
if (value) {
Handler uiCallback = new Handler(Looper.getMainLooper());
thread = new Thread() {
#Override
public void run() {
try {
while (!isConnected()) {
synchronized (this) {
wait(3000);
}
}
} catch (InterruptedException ex) {
}
uiCallback.post(new Runnable() {
#Override public void run() {
if(wifiManager.isWifiEnabled()){
sendMessageWidget();
} else {
showWifiSettingsAlert();
}
}
});
}
};
thread.start();
}
Or instead of using a handler at all, you can wrap the part in the run() method in runOnUiThread() if you are in an activity which does the same thing.
You should note however, you don't actually need to use any threading here. If you follow the example on: https://developer.android.com/google/auth/api-client.html you'll find that by implementing ConnectionCallbacks, OnConnectionFailedListener you can call mGoogleApis.connect() from the activity's onStart() and when it connects or fails to do the corresponding callback will be executed on the calling thread. For instance,
#Override
public void onConnected(Bundle connectionHint) {
if(wifiManager.isWifiEnabled()){
sendMessageWidget();
} else {
showWifiSettingsAlert();
}
}
Achieves the same thing...
You are using mGoogleApiClient.connect();, which is an asynchronous method, in a thread and this isn't allowed.
You could try using runOnUiThread instead :
runOnUiThread(new Runnable() {
#Override
public void run() {
// TODO Auto-generated method stub
//do your stuff here
}
});
I have a button listener witch include a thread sleep and another button listener.
Second button listener must interrupt this thread and I don t know how to do this:
My code:
button1.setOnClickListener (new View.OnClickListener() {
#Override
public void onClick(View v) {
..........
button1.setEnabled(false);
button1.setVisibility(View.GONE);
button2.setVisibility(View.VISIBLE);
button2.setEnabled(true);
new Thread(new Runnable() {
#Override
public void run() {
try {
Thread.sleep(2800);
} catch (InterruptedException e) {
// ???????
}
MainActivity.this.runOnUiThread(new Runnable() {
#Override
public void run() {
button1.setEnabled(true);
button1.setVisibility(View.VISIBLE);
button2.setEnabled(false);
button2.setVisibility(View.GONE);
}
});
}
}).start();
button2.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
..............
// Thread.interrupted(); -> does not work
}
});
} });
How can I make button2 listener to interrupt the thread?
class TestInterruptingThread1 extends Thread{
public void run(){
try{
Thread.sleep(1000);
System.out.println("task");
}catch(InterruptedException e){
throw new RuntimeException("Thread interrupted..."+e);
}
}
b2 //
{
try{
t1.interrupt(); // t1 is the thread to be interrupted
}catch(Exception e){System.out.println("Exception handled "+e);}
}
}
Instead of an anonymous implementation of the thread, make the thread a class variable. Suppose the name of the thread is btnOneThread
private Thread btnOneThread;
Initialize the thread where needed and start it.
btnOneThread = new Thread( ){...}
btnOneThread.start();
When the button2 is clicked call
if (btnOneThread != null) {
btnOneThread.interrupt();
}
By calling the thread.interrupt() method. You also need to save your Thread instance in a field of your class.
Thread t = new Thread() {...}
t.start();
and in the listener:
t.interrupt();
As to
// Thread.interrupted(); -> does not work
it will work from within a class derived from Thread and will be the same as this.interrupted(); t.interrupted() will work outside of the class definition.
Also note that the method isInterrupted() does not clear the interruption flag, while interrupted() has the side-effect of clearing the flag.