Execute all scheduled (postDelayed) runnables in Handler
Asked Answered
V

3

6

I use a Handler, which post some Runnable via postDelayed(r, DELAY_TIME), but I need to execute all Runnables before posting new via postDelayed.

Any good ideas how to achieve this as simple as possible?

Edit:

I want it basically like this:

Runnable r = new Runnable() {
    // Do some fancy action
};

if (mHandler.runnablesScheduled) {
    mHandler.flushAllScheduledRunnables();
}
mHandler.postDelayed(r, DELAY);
Valorize answered 26/4, 2013 at 19:58 Comment(8)
Can I ask 'why' ? Because what you want to do sounds like such a bad idea that it might be worth trying to understand the reason why you might want to do that ? I mean: it is perfectly possible that someone using the same handler will requeue a message and that the queue will never be empty for you. In that case you would bring back the asynchronous queue to a synchronouse state in your thread, with all potential nefast sideeffects.Kenwood
It's completely impossible, that the queue will be never empty, because you can't delete infinitely items. It's because I want undoable database deletes. The listitem shall be deleted for the user, but in reality it is deleted after 5 seconds. I want to flush / do all database queries because when the user undo one action or delete another, they shouldn't reappear in the listview (which they would do, because they aren't deleted, only the view is deleted from the listview). It's pretty complicated, if you have another idea, tell it me! ;)Valorize
Write your own queue. Trying to recycle Handlers for this might be somewhat challenging. A good approach is also to enumerate messages so that you can directly discard 'up to message number X'Kenwood
I currently use only one Handler, I don't need to recycle Handlers. I can enumerate the messages, because I have a WeakReference to the Runnables in an ArrayList to cancel the Runnables. I can discard all the messages, though, but this doesn't help me, I need to call them. However, you braught me to an idea: I could remove the Runnables from the original Handler and post them afterwards instantly on another. ;)Valorize
... or have 1 runnabel that inspects another queue and figures out what to do. Why do you make it so complicated ?Kenwood
I'm also implementing undo functionality in a similar fashion as @Leandros. From what I understand, it's also important to flush the Handler's message queue when onPaused is called on your activity. Otherwise the queued up messages might never be executed. Are Handlers simply the wrong tool for the job? If so, what should we use instead?Orientate
@Orientate I actually use Handlers, I can post my solution tomorrow if you want .Valorize
You must accept an answer.Seigneur
R
3

In an Array, Keep track of runnables that are going to be called, then, if you want them to be called, cancel the postDelayed and call the runnables directly, to fire them just call the run() method from the runnable. Example code:

// Declaring the Handler and the Array that is going to track Runnables going to be tracked.
final mHandler = new Handler();  
final List<Runnable> callStack = new ArrayList<Runnable>();

// Method to remove a runnable from the track Array.
public void removePostDelayed(Runnable run) {
    callStack.remove(run);
}

// Method that we use in exchange of mHandler.postDelayed()
public void myPostDelayed(Runnable run, int delay) {
    // I remove callbacks because I don't know if can be called 2 times.
    mHandler.removeCallbacks(run);

    // We remove the Runnable from the tracking Array just in case we are going to add a Runnable that has not been called yet.
    removePostDelayed(run);

    // We add the runnable to the tracking Array and then use postDelayed()
    callStack.add(run);
    mHandler.postDelayed(run, delay);
}

// This is the Runnable. IMPORTANT: Remember to remove the Runnable from the tracking Array when the Runnable has been called.
Runnable myRunnable = new Runnable() {
    @Override
    public void run() {
        // Do some fancy stuff and remove from the tracking Array.
        removePostDelayed(this);
    }
}

// Method to execute all Runnables
public void callAllStack() {
    // We create a copy of the tracking Array because if you modify the Array while you are iterating through it, will return an Exception.
    List<Runnable> callStackCopy = new ArrayList<Runnable>();

    // here we copy the array and remove all callbacks, so they are not called by the Handler.
    for (Runnable runnable : callStack) {
        callStackCopy.add(runnable);
        mHandler.removeCallbacks(runnable);
    }

    // Then we call all the Runnables from the second Array
    for (Runnable runnable : callStackCopy) {
        runnable.run();
    }

    // And clear the tracking Array because the Handler has no more Runnables to call (This is redundant because supposedly each run() call removes himself from the tracking Array, but well... just in case we forgot something).
    callStack.clear();
}

// Example of postDelaying a Runnable while tracking if has been fired.
myPostDelayed(myRunnable, 1000)

// Example of firing all Runnables.
callAllStack();

Is pretty easy and I've commented it so you can understand it better, but if you don't understand something, just comment it. You can modify it to support multiple calls to the same Runnable or just creating your own class extension of a Handler called TrackingHandler with these functions implemented or something.

I've written the code right now on the fly so is possible that is plenty of typos, don't know.

Rickyrico answered 30/6, 2013 at 17:5 Comment(0)
F
0

Well, all the Runnables are run on a queue in your handler, so if you want to run something at the end of it, the easiest way that comes to mind is to place it as another Runnable on the queue:

mHandler.post(new Runnable() {
    @Override
    public void run() {
        mHandler.postDelayed(r, DELAY);
    }
});
Freeze answered 26/4, 2013 at 20:20 Comment(2)
Well, this won't work, because the Runnables should be executed immediately. The adding of new Runnables is done by the user and can't be predicted. The delay is 5s, pretty high.Valorize
Ahh, I think I misunderstood your question. So you're saying all of your Runnables are posted with the delay and you want to at some point get rid of the delay for the already posted Runnables?Freeze
B
0

You need a few things to make this work.

  1. Use Handler.sendMessageDelayed(Message) instead of Handler.postDelayed and assign a meaningful what value to your Message
  2. When you need to flush the queue, check whether anything is queued already with Handler.hasMessages(int). If there's anything, you can remove it with Handler.removeMessages and execute it yourself.
Bacteria answered 28/4, 2013 at 18:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.