android - cancel runnable that already start running - java

I have a long process in the background to do. So onCreate, I post a runnable in my handler from handlerThread but I have a button that allows users to cancel it. It's possible to stop a Runnable after it starts?
#Override
protected void onCreate(Bundle savedInstanceState) {
Handler h = new Handler( HandlerThread.getLooper() );
Runnable runnable = //class that implements runnable;
h.post( runnable );
btn.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
h.removeCallbacks(runnable);
}
}
}
but it seems that doesn't cancel runnable that already running, Or should I use postDelayed() with a huge delay?

Inside of your runnable have a boolean flag for running.
Your button then can set this flag to true/false, to stop it running.
You can implement pause, resume, or start, stop, it all depends on your usecase.
i.e. something like
while(running) {
// Your repeated background code
}
or
if(running) {
// do some one shot code, i.e. the user can stop it if they press the button before the if, but not after.
}
You could also have multiple steps, allowing you to cancel mid way.
if(running) {
// do step 1 code
}
if(running) {
// do step 2 code
}

Use below code
handler.removeCallbacksAndMessages(null);

Related

How to cancel the execution of postDelayed()

I'm making an app where some sort of widgets are created on the fly and can be re-arranged with drag and drop. I'm trying to implement 2 things:
Long press to initiate drag and drop
short press(or click) to open a menu to change some settings.
But i'm having a problem where I can't cancel the code in handler's postdelayed() function. I'm using the following code to make it happen.
_sliders_item[_sliders_counter].setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(final View v, MotionEvent event) {
Handler hndlr = new Handler();
Runnable _run = new Runnable() {
#Override
public void run() {
ClipData data = ClipData.newPlainText("", "");
View.DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(v);
v.startDrag(data, shadowBuilder, v, 0);
v.setVisibility(View.INVISIBLE);
_t1.setText("executed");
_t1.show();
}
};
if (_enable_editor) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
hndlr.postDelayed(_run,1000);
//return true;
}
if (event.getAction() == MotionEvent.ACTION_UP){
_t1.setText("menu");
_t1.show();
hndlr.removeCallbacks(_run);
//return true;
}
}
return _enable_editor;
}
});
But the problem is, When I long press the object, it works fine. I can initiate the drag and drop. But when I short press it, The code for ACTION_UP event executes but the code inside the runnable still executes. How can I cancel the runnable code when I short press the object?
You have to keep references to your Runnables and Handler and use Handler#removeCallbacks().
So basically:
Handler h = new Handler();
Runnable r = new Runnable() { /* does something */
h.postDelayed(r, 1000);
// When you want to cancel.
h.removeCallbacks(r);
Declare your Runnable outside of onTouch event, like seperate method in your Class. Then when you want to actually execute runnable, call your method.
Quick'n'Dirty Example:
onTouch(..) {
if (eventDown) {
// start Runnable
}
if (eventUp) {
// Do not call runnable
}
}
private void executeMyRunnable( /* Any arguments you need in Runnable */) {
// Your runnable code here
}
You can check for long press by taking the time difference between system time at action.down and system time at action.up. If the difference is greater than say 5000 milliseconds, then do the runnable code else the dialog code.
Simply run handler.removeCallbacksAndMessages(null);

Android Remove Handler postDelayed Dynamically

I want to set dynamically auto scroll speed to WebView. In onCreate calling autoScroll(25) and remove it, nextly calling autoScroll(300) but when the apk is running the auto scroll speed is 25 so earlier called 'mHandler.postDelayed' do not removing. How to fix the problem?
Handler mHandler;
Runnable runnable;
WebView wv;
protected void onCreate(Bundle savedInstanceState) {
...
autoScroll(25);
mHandler.removeCallbacks(runnable);
autoScroll(300);
}
public void autoScroll(final int speed){
if(mHandler == null) {
mHandler = new Handler();
}
wv.post(runnable = new Runnable() {
#Override
public void run() {
wv.scrollBy(0, 1);
mHandler.postDelayed(this, speed);
}
});
}
mHandler.removeCallbacks(runnable);
will only remove any pending posts of Runnable r that are in the message queue. It will not stop an already running thread. You have to explicitly stop the thread. One way to stop that thread is to use a boolean variable as a flag and run your code inside runnable based on the value of that flag. You can take some hints from https://stackoverflow.com/a/5844433/1320616

How to display some text in TextView for a specified amount of time?

I have developed an android application which extracts single line text messages from the server. Once a button is clicked, it makes a function call which gets the next message from the server. Some of those messages are time based,
i.e those messages have to be displayed in the TextView for a particular amount of time and after that time is elapsed, it should automatically make the function call to get the next message from the server(i.e without the button being clicked).
Could someone please help me out in achieving this.
I tried using while loop as follows:
while(!presentTime.equals(expiryTime)){
calculatePresentTym(); //This method calculates the presentTime value
display.settext(the received instruction);
}
if(presentTime.equals(expiryTime))
(make the function call)
If I do this, nothing is being displayed till presentTime and expiryTime are equal. Once they are equal, the next instruction is automatically fetched by the function call and is displayed in the TextView.
Use a a handler
Handler m_handler;
Runnable m_handlerTask ;
m_handler = new Handler();
#Override
public void run() {
// do something
m_handler.postDelayed(m_handlerTask, 1000);
}
};
m_handlerTask.run();
T0 cancel the run
m_handler.removeCallbacks(m_handlerTask); // to cancel the run
You can also use a timer but you will have to use runOnUiThread to update ui since timer runs on a different thread.
Timer _t = new Timer();
_t.scheduleAtFixedRate( new TimerTask() {
#Override
public void run() {
//do something
runOnUiThread(new Runnable() //run on ui thread
{
public void run()
{
//update ui
}
});
}
}, 1000, 1000 );
Note:
gets the next message from the server
Getting the message from server should be done on a background thread.
Edit:
While copy pasting the initialization part was missing. You have a counter i that is displayed in the textview. The counter increases by 1 every second. When it reaches 100 you cancel the run. Modify the below according to your requirements.
public class MainActivity extends Activity {
TextView tv;
Handler m_handler;
Runnable m_handlerTask ;
int i=0;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv = (TextView) findViewById(R.id.textView1);
m_handler = new Handler();
m_handlerTask = new Runnable() {
#Override
public void run() {
// TODO Auto-generated method stub
if(i<=100)
{
tv.setText(""+i);
i++;
}
else
{
m_handler.removeCallbacks(m_handlerTask);
}
m_handler.postDelayed(m_handlerTask, 1000);
}
};
m_handlerTask.run();
}
}
Use a timer. Schedule the timer for repeated interval executions, and after each execution you can get the next text from the server and display the same.
Check the Timer reference scheduleAtFixedRate(TimerTask task, long delay, long period)

Java Timer equivalent in Android

I recently began working with Java and am exploring Android development. I was trying to port over one of the Java programs I made, but I am having some difficulty with getting the java Timer to function the same way in Android. I read through a number of posts and they, for the most part, indicated that it would be better to use the Handler class in android as opposed to Timer.
This was my timer in Java:
playTimer = new Timer(1000/model.getFPS(), new ActionListener() {
public void actionPerformed(ActionEvent evt) {
// do something
...
if( finished everything ) playTimer.stop();
}
});
And once a certain button was clicked, I would simply run "playTimer.start()" to start it.
As you can see, I had it set up so that the user could set the FPS they wanted (by simply setting the first parameter of the Timer class to 1000/model.getFPS()).
Now I've tried to do something similar in Android using handlers, but I am having some difficulty. It appears that the Handler ticks are not firing at the proper intervals. It seems that they are quite slow compared to what I need it to be. This is what I did in android so far:
public void startTimer() {
playHandler = new Handler();
startTime = System.currentTimeMillis();
playHandler.removeCallbacks(updateTimeTask);
playHandler.postDelayed(updateTimeTask, 0);
}
private Runnable updateTimeTask = new Runnable() {
public void run() {
// do something
...
if( finished everything ) playHander.cancel();
else {
playHandler.postDelayed(updateTimeTask, 1000/model.getFPS());
}
}
};
Excuse the semi-pseudocode. Can anyone shed any light? Thanks guys.
You can use a timer as below. The timer runs every second incrementing the counter. Displs the counter value in textview.
Timer runs on a different thread. SO you should set the text on the UI Thread.
The counter runs from 0 to 99. After 99 the timer is cancelled. Also cancel the timer when not required like in onPause().
public class MainActivity extends Activity {
TextView _tv,tv2;
Timer _t;
int _count=0;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
_tv = (TextView) findViewById( R.id.textView1 );
_t = new Timer();
_tv.setText(R.string.app_name);
_t.scheduleAtFixedRate( new TimerTask() {
#Override
public void run() {
_count++;
runOnUiThread(new Runnable() //run on ui thread
{
public void run()
{
_tv.setText(""+_count);
if(_count==99)
{
_t.cancel();
}
}
});
}
}, 1000, 1000 ); //change this value of 1000 to whatever you need.
}
#Override
protected void onPause() {
// TODO Auto-generated method stub
super.onPause();
_t.cancel();
}
}
If you decide not to use Timer (for whatever reason) you can just write a separate Thread that sleeps for x milliseconds and then wakes up and calls whatever Runnable you want it to call. That's going to be pretty precise. I have it working at the 10 millisecond level and it works quite nicely.
Just remember that it HAS to call a Runnable because a separate Thread can't have direct effect on anything on the main display thread.
public boolean keepPlayingAnimation = true
Handler h = new Handler()
Runnable updateDisplay = new Runnable(){
public void run(){
//do something in my display;
}
}
new Thread(){
public void run(){
while(keepPlayingAnimation){
try{
sleep(10);
}catch(Exception e){
}
h.post(updateDisplay);
}
}
}.start();
Just don't forget to set keepPlayingAnimation to false when you're done with this cause otherwise it will sit there running in the background for ever (or just about).
Take a look at Android Timer
It already has everything you need i guess. From ticking every 1 second to finish handly and so on.
Here is an example how to setup an TimerTask: setup
Not sure if you need such but i just remembered that i made this.

Android: runOnUiThread does not always choose the right thread?

I've got an activity that keeps reading words to the user, and using onUtteranceCompleted with textTospeech to display something when the code is completed.
Inside onUtteranceCompleted I have this code to delay a function with a second:
Runnable task = new Runnable() {
public void run() {
//runs on ui
runOnUiThread(new Runnable() {
public void run() {
readWord();
}
});
}
};
worker.schedule(task, 1, TimeUnit.SECONDS);
This seems like it works well, but I think it is causing a problem.
When I rotate the screen of my phone (I guess this starts a new activity).
I hear some words being read in the background. I guess this is because of runOnUiThread() which makes the activity continue in the background.
How could I avoid 2 activities running ? I would prefer if I don't have to stop the screen from rotating on doing some weird patch!
Thank you
EDIT:
public void readWord() {
if (this.readingOnPause) {
return;
}
txtCurrentWord.setText(currentItem[1]);
this.hashAudio.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID,"word");
this.tts.setLanguage(Locale.US);
this.tts.speak(this.currentItem[1], TextToSpeech.QUEUE_FLUSH,this.hashAudio);
}
EDIT2:
instantiation of worker:
private static final ScheduledExecutorService worker = Executors.newSingleThreadScheduledExecutor();
I would use a Handler instead of runOnUiThread().
For one thing, you're using a Thread that starts another Thread - why?
Secondly, if you create a simple Handler, it should kill itself on the rotate config change. IE:
private Handler handler = new Handler() {
#Override
public void handleMessage(Message msg) {
// do your background or UI stuff
}
};
Then later use a Thread to call the handler, which will kick off whatever process you want to run on the UI thread:
new Thread() {
#Override
public void run() {
long timestamp = System.currentTimeMillis();
// thread blocks for your 1 second delay
while (System.currentTimeMillis() - timestamp <= 1000) {
// loop
}
handler.sendEmptyMessage(0);
}
}.start();
Ok so this is a fix I've come up with, if someone has a better solution, I'm listening.
I've added android:configChanges="keyboardHidden|orientation" inside the activity in the androidmanifest
2.
and then a function that is called when the screen is rotate:
#Override
public void onConfigurationChanged(Configuration newConfig)
{
super.onConfigurationChanged(newConfig);
setContentView(R.layout.streaming);
initializeUI(); //contains all the findViewByID etc...
}

Categories

Resources