Cancelling Runnable - android

I eventually managed to get my other post sorted; to create a way to update the GUI every second or so. So my runnable runs fine but now I've added a button to the GUI which is meant to stop the runnable. But how do you do it?
I've tried this code:
// Button to stop the runnable
stop = ( Button ) findViewById( R.id.stop );
stop.setOnClickListener( new View.OnClickListener()
{
#Override
public void onClick(View v)
{
handler.removeCallbacksAndMessages( timerTask.class );
}
});
I implement Runnable in order to use it, therefore I don't create a new Thread manually and add a run() method to it. So how do you do it?
Thanks

You can't just murderize the thread. What you'll need to do is add a method to your Runnable object implementation that acknowledges a request to stop. That method then flips a condition that causes your Runnable.run() method to exit.
public class YourClass implements Runnable {
private boolean keepGoing = true;
public void run() {
while(keepGoing) {
// Do important work!
}
}
public void stop() {
this.keepGoing = false;
}
}
So in your onClick(View v) implementation for your stop button, you would call yourClassInstance.stop(). That breaks the loop, the run() method ends, and the thread is cleaned up.

Related

Handlers and Runnable

I read in a document that when we implement Runnable we are creating a new thread and post any code to run inside that thread and we use handler to perform UI updates on the uithread.
But in my below example
private class AutoIncrementer implements Runnable {
#Override
public void run() {
if(plusButtonIsPressed){
incrementValue();
handler.postDelayed( new AutoIncrementer(), REPEAT_INTERVAL_MS);
}
}
}
Here I used handler.postDelayed() to update the UI with incremented value
and again on implementing onLongClickListener()
plusButton.setOnLongClickListener(
new View.OnLongClickListener() {
#Override
public boolean onLongClick(View arg0) {
plusButtonIsPressed = true;
handler.post(new AutoIncrementer());
return false;
}
}
);
I again use handler.post(new AutoIncrementer())..... Why do I need to use this when I am updating the UI through handler.postDelayed()???
Runnable is not a new thread, it is just a piece of code that can be "run".
Your handler.post within onLongClick is what kicks off the first run of the AutoIncrementer. Then within that code it needs to schedule the next check (as onLongClick is only called once, and not repeatedly while the button is held down).

How can i handle my Looper in an OnClick method to run a function with delay?

I would like to be able to run my specific method in background through a looper in an on click event, is this the right way to do this?
myThread = new LooperThread();
myThread.start();
upload.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
}
});
stop.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
myThread.handler.post(new Runnable() {
#Override
public void run() {
//my methods
}
});
}
});
And my Looper Class:
class LooperThread extends Thread {
Handler handler;
public void run() {
Looper.prepare();
handler = new Handler();
Looper.loop();
}
}
If it is,
the problem is that in this way, i don't understand why the system don't recognize "handler" while i typing: "myThread.handler.post.." and run the methods.
Otherwise, can you help me on making this?
I am sorry if i made mistakes while making the question, but it's my first time here :)``
Welcome to Stack Overflow.
I would like to be able to run my specific method in background through a looper in an on click event, is this the right way to do this?
Your code works, but I couldn't say it's the right way to do it. Like #tynn mentioned, a HandlerThread might be a better option.
If it is, the problem is that in this way, i don't understand why the system don't recognize "handler" while i typing: "myThread.handler.post.." and run the methods.
Otherwise, can you help me on making this?
If I understood your problem, it's an access issue. Your handler seems unaccessible since it's declared as package-private. You could make your handler visible this way:
// Should this class be public or package-private?
public class LooperThread extends Thread {
private Handler handler;
public Handler getHandler() {
return handler;
}
// ...
}
And you will be able to reference the handler like this:
myThread.getHandler().post(...);
UPDATE
You can delay a Runnable execution this way:
// Delay execution of a Runnable task by 5000 milliseconds.
myThread.getHandler().postDelayed(myDelayedRunnable, 5000);

Android - Button icon loads after function call instead of before

I have this code:
fab.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(!clicked){
clicked = true;
fab.setImageResource(R.drawable.ic_media_stop);
char[] userInput = et.getText().toString().toLowerCase().toCharArray();
compareToMap(userInput);
}else{
clicked = false;
fab.setImageResource(R.drawable.ic_media_play);
}
}
});
When the floating action button is clicked, I want it to change to a stop symbol and then execute the function afterwards. What is happening is that the code is being executed before the image on the button changes. The image only changes once all of the code has been executed from the compareToMap function even though it is placed before it in the code. Is there a way to make sure that the image changes before the compareToMap function executes?
Note: The compareToMap function contains Thread.sleep methods which is causing the UI to block (I think), but shouldn't the image change before that function is executed?
At this moment the UI didn't load yet. If you want to do some work after the UI is loaded you can use the class Handler.
long postDelay = 100; //The delay (in milliseconds) until the Runnable will be executed
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
//Code that you want to execute after the UI is loaded
compareToMap(userInput);
}
}, postDelay);
Note: You can't use Thread.sleep on the UI main thread. You can learn more here. https://developer.android.com/guide/components/processes-and-threads.html.
You can use AsyncTask to execute the compareToMap(userInput) method, this will run the compareToMap method in a separated thread and the UI will not be blocked; something like this :
fab.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
fab.setImageResource(!clicked ? R.drawable.ic_media_stop : R.drawable.ic_media_play);
if (!clicked) {
// Cancel the executing AsyncTask if there's one already running
if (mAsyncTask != null) {
mAsyncTask.cancel(true);
}
mAsyncTask = new AsyncTask<String, Void, Void>() {
#Override
protected Void doInBackground(String... params) {
compareToMap(userInput);
return null;
}
};
mAsyncTask.execute();
}
clicked != clicked;
}
});
More info about AsyncTasks here or you can check this tutorial.

Updating UI on button click after a certain time

I have a TextView. I want to update its text (append a "1") after 1 second of a button click.
public class HaikuDisplay extends Activity {
Method m;
Timer t;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
t = new Timer();
m = HaikuDisplay.class.getMethod("change");
}
//Event handler of the button
public void onRefresh(View view)
{
//To have the reference of this inside the TimerTask
final HaikuDisplay hd = this;
TimerTask task1 = new TimerTask(){
public void run(){
/*
* I tried to update the text here but since this is not the UI thread, it does not allow to do so.
*/
//Calls change() method
m.invoke(hd, (Object[])null);
}
};
t.schedule(task1, 1000);
}
public void change()
{
//Appends a "1" to the TextView
TextView t = (TextView)findViewById(R.id.textView1);
t.setText(t.getText() + "1");
}
//Event handler of another button which updates the text directly by appending "2".
//This works fine unless I click the first button.
public void onRefresh1(View view)
{
TextView t = (TextView)findViewById(R.id.textView1);
t.setText(t.getText() + "2");
}
}
Consider all Exceptions be handled.
On first click, m.invoke gives InvocationTargetException. But it calls the method change() on successive invokes without any Exceptions(verified by logging). But it does not update the text. Where am I wrong?
Also, I see in the debugger that it creates a new Thread every time I click the button. That is fine. But why isn't it removing the previous Threads though their execution has been completed?
Do something like this
public void onRefresh1(View v) {
// You can have this in a field not to find it every time
final EditText t = (EditText) findViewById(R.id.textView1);
t.postDelayed(new Runnable() {
#Override
public void run() {
t.append("1");
}
}, 1000);
}
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
// Update UI
}
}, 1000);
implement this on button click
UPDATE:
There are some other answers. dtmilano suggested another solution which is almost same to mine except he is calling the postDelayed method of View class and In my answer I used postDelayed method of handler class.
from the api reference of android the postDelayed method of Handler says
The runnable will be run on the thread to which this handler is
attached.
and the postDelayed method of View says
The runnable will be run on the user interface thread.
This is the only difference between these two solution. in my answer instead of creating new Handler every time you can use any other handler instance. Then the runnable will be run on that thread where that specific handler is declared. And if the postDelayed of EditText is used the the runnable method will be run on the user Interface Thread.
Now the performance issue, both has the same performance (If anybody can prove me wrong with reference I will be happy)
That's looking awful convoluted - have you considered using CountDownTimer instead?
new CountDownTimer(1000, 1000) {
public void onTick(long millisUntilFinished) {
// no-op
}
public void onFinish() {
change();
}
}.start();
This should call change (and hence change the text) on the UI thread, avoiding reflection and threading errors.
Hi Use the following code for that. Hope this will help you .
new java.util.Timer().schedule(
new java.util.TimerTask() {
#Override
public void run() {
// your code here
}
},
1000
);
Have a look of this question also.
display data after every 10 seconds in Android
You can try with this also.
private Handler handler = new Handler();
private Runnable runnable = new Runnable() {
public void run() {
doStuff();
/*
* Now register it for running next time
*/
handler.postDelayed(this, 1000);
}
};
**EDIT 3**
Try with this once you are need to enable once (i mean if you put your code in yourmethod()== this will get automatically call 1 seconds once.
private Timer timer;
TimerTask refresher;
// Initialization code in onCreate or similar:
timer = new Timer();
refresher = new TimerTask() {
public void run() {
yourmethod();
};
};
// first event immediately, following after 1 seconds each
timer.scheduleAtFixedRate(refresher, 0,100);

removeCallbacks not stopping runnable

I am calling from a method:
myHandler.postDelayed(mMyRunnableHide, 6000);
which calls:
public Runnable mMyRunnableHide = new Runnable()
{
public void run()
{
mTextDisplay.setText("");
DisplayX();
}
};
if a button on screen is clicked I want to stop the runnable:
Button next = (Button) findViewById(R.id.Breaction);
next.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
myHandler.removeCallbacks(mMyRunnableHide);
mTextDisplay.setText("");
DisplayX();
}
});
}
the removecallbacks is not stopping the runnable. What am I doing wrong? Am I using the correct method? I just want the runnable to "Not Run" when the user clicks the button.
Thanks for any help.
It appears to me that removeCallbacks(..) only stops pending messages (Runnables). If your runnable has already started, then there's no stopping it (at least not this way).
Alternatively, you can extend the Runnable class and give it some kind of kill switch like this:
public class MyRunnable implements Runnable
{
private boolean killMe = false;
private void run()
{
if(killMe)
return;
/* do your work */
}
private void killRunnable()
{
killMe = true;
}
}
This will only prevent it from starting, but you could occasionally check killMe and bail out. If you are looping the runnable (like some kind of background thread) you can say:
while(!killMe) {
/* do work */
}
Hope this helps
EDIT I just wanted to post an update on this. Since this original post, Google has come up with a great class called AsyncTask that handles all of this stuff for you. Anyone reading this really should look into it because it is the correct way of doing things.
You can read about it here
Handler.removeCallback is synchronous and will work nicely provided:
You call postDelayed always in the main thread.
You call removeCallback always in the main thread
You don't call postDelayed again after having removed callbacks.
So in your case removeCallbacks is called from a button handler, which runs in the main thread. But you didn't show in your code the point from where you call postDelayed. If you call it from a background thread thats where your problem is.
If you are sure you don't call any of these methods from background threads, and the order of the calls is correct, then you might be leaving uncancelled tasks unadvertedly alive due to activity recreation on config changes (screen rotation, etc). Always make sure to call removeCallbacks again in the onDestroy method to prevent this kind of problems.
Here is another way to accomplish what mtmurdock is describing. This class will allow editing of instance variables in any class that your Runnable is defined as an anonymous inner class.
package support;
/**
* Runnable that can be stopped from executing
*/
public abstract class KillableRunnable implements Runnable{
private boolean isKilled=false;
/**
* Instead of Overriding run(), override this method to perform a Runnable operation.
* This will allow editing instance variables in the class that this Runnable is defined
*/
public abstract void doWork();
//The handler that posts this Runnable will call this method.
//By default, check if it has been killed. doWork() will now be the method
//override to implement this Runnable
#Override
final public void run(){
if(!isKilled){
doWork();
}
}
final public void kill(){
isKilled=true;
}
}
I don't think that removeCallbacks(..) only stops pending messages (Runnables) ,I think removeCallbacks(..) not working have other cause,but i don‘t know. because postDelayed(..) and removeCallbacks(..) is in the same thread
the following has worked for me. Place it in onResume.
mService= null;
public void onServiceConnected(ComponentName name, IBinder service) {
Log.i(TAG, "OnServiceConnected");
ContadorFG.LocalBinder binder = (ContadorFG.LocalBinder) service;
mService = binder.getService();
connected = true;
synchronized (lock){
lock.notifyAll();
}
}
public void onResume() {
super.onResume();
loopDelayed();
}
private void loopDelayed(){
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
if (mService != null) {
----
----
----
return;
}else{
//auto call
loopDelayed();
}
}
}, 10);
}

Categories

Resources