78

I have an "open" animation and am using Handler.postDelayed(Runnable, delay) to trigger a "close" animation after a short delay. However, during the time between open and close, there is possibly another animation triggered by a click.

My question is, how would I cancel the "close" animation in the handler?

ישו אוהב אותך
  • 28,609
  • 11
  • 78
  • 96
Bruce Lee
  • 3,049
  • 3
  • 20
  • 13
  • careful with this answer if you use Kotlin: https://stackoverflow.com/a/30905295/2736039, I tried that and I run into this problem (only in Kotlin): https://stackoverflow.com/questions/62405834/android-handler-callback-not-removed-for-token-type-int-or-long-kotlin – Ultimo_m Aug 26 '20 at 09:54

4 Answers4

108

Just use the removeCallbacks(Runnable r) method.

Cristian
  • 198,401
  • 62
  • 356
  • 264
  • 10
    Is it possible to remove callbacks for anonymous runnables? – Bruce Lee Sep 02 '10 at 13:36
  • 10
    I don't think so... you will have to use non-anonymous ones. Otherwise you won't be able to reference them in the future. – Cristian Sep 02 '10 at 15:31
  • 3
    See @NameSpace's answer. You can remove pending runnables if you post the runnable with a token. Or you can use Daniel L.'s method of removing all callbacks/messages using a null token. – vman Apr 22 '16 at 18:36
  • any recommendations for removing specific callbacks for API's where removeCallbacks(Runnable r) was not available? – gom1 Aug 23 '19 at 00:32
  • Will this remove all the occurrences of the instance? Because i am posting it multiple times with different delay times – yeshu Dec 23 '19 at 05:45
103

Cristian's answer is correct, but as opposed to what is stated in the answer's comments, you actually can remove callbacks for anonymous Runnables by calling removeCallbacksAndMessages(null);

As stated here:

Remove any pending posts of callbacks and sent messages whose obj is token. If token is null, all callbacks and messages will be removed.

Pang
  • 9,564
  • 146
  • 81
  • 122
Daniel L.
  • 5,060
  • 10
  • 36
  • 59
  • 7
    I'd just like to point out that this behavior is different whether you're dealing with a `Handler` or a `View` class. In a `View` class (and perhaps after 4.0?), you have to use the same `Runnable` object to cancel the task, while with a `Handler` class they will simply all be cancelled if you pass `null`. Nonetheless, the question specifies a `Handler` so your answer is correct. – Andre Feb 20 '14 at 10:51
  • 1
    really!! thanks .. had some runnable there, and removeCallbacks did just do nothing!!!!! but this really workd :) thx – cV2 Nov 04 '14 at 16:56
18

This is a late answer, but here's a different method for when you only want to remove a specific category of runnables from the handler (i.e. in OP's case, just remove the close animation, leaving other runnables in the queue):

    int firstToken = 5;
    int secondToken = 6;

    //r1 to r4 are all different instances or implementations of Runnable.  
    mHandler.postAtTime(r1, firstToken, 0);
    mHandler.postAtTime(r2, firstToken, 0);
    mHandler.postAtTime(r3, secondToken, 0);

    mHandler.removeCallbacksAndMessages(firstToken);

    mHandler.postAtTime(r4, firstToken, 0);

The above code will execute "r3" and then "r4" only. This lets you remove a specific category of runnables defined by your token, without needing to hold any references to the runnables themselves.

Note: the source code compares tokens using the "==" operand only (it does not call .equals()), so best to use ints/Integers instead of strings for the token.

NameSpace
  • 10,009
  • 3
  • 39
  • 40
  • I saw your answer and I changed from `string` to `int` and then I had this bug for one day :D https://stackoverflow.com/q/62405834/2736039 – Ultimo_m Jun 16 '20 at 10:09
  • This won't work in Kotlin for int > 127, just leaving this comment here as a hint for future readers – Ultimo_m Aug 26 '20 at 09:59
11

If your using recursion, you can acheive this by passing "this". See code below.

public void countDown(final int c){
    mHandler.postDelayed(new Runnable() {
        @Override
        public void run() {
            aq.id(R.id.timer).text((c-1)+"");
            if(c <= 1){
                aq.id(R.id.timer).gone();
                mHandler.removeCallbacks(this);
            }else{
                countDown(c-1);
            }
        }
    }, 1000);
}

This example will set the text of a TextView (timer) every second, counting down. Once it gets to 0, it will remove the the TextView from the UI and disable the countdown. This is only useful for someone who is using recursion, but I arrived here searching for that, so I'm posting my results.

robisaks
  • 193
  • 1
  • 9