254

I have a Handler from my sub-Activity that was called by the main Activity. This Handler is used by sub-classes to postDelay some Runnables, and I can't manage them. Now, in the onStop event, I need to remove them before finishing the Activity (somehow I called finish(), but it still call again and again). Is there anyway to remove all callbacks from a Handler?

Floern
  • 33,559
  • 24
  • 104
  • 119
Luke Vo
  • 17,859
  • 21
  • 105
  • 181

9 Answers9

585

In my experience calling this worked great!

handler.removeCallbacksAndMessages(null);

In the docs for removeCallbacksAndMessages it says...

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

Kevin Kopf
  • 13,327
  • 14
  • 49
  • 66
josh527
  • 6,971
  • 1
  • 19
  • 19
  • 2
    @Malachiasz I think I would use it in onStop or onPause, to make sure no messages are handled after the activity has lost focus. But depends on what needs to be done when the callback/message is fired – Boy Aug 24 '14 at 13:08
  • 1
    I believe I've seen NPE before on some phones when doing this but it's been awhile. – Matt Wolfe Jul 22 '15 at 23:30
  • 4
    I've had some issues with `removeCallbacksAndMessages(null)` wouldn't remove some of my callbacks. When I would want to stop receiving Callbacks, I'd call `handler.removeCallbacksAndMessages(null)` and set my handler to null, but since I would still get the callback, I'd encounter a NPE when I'd want to loop with `handler.postDelayed()`. – Snaker May 23 '16 at 18:19
  • 1
    @Snaker Have you solved your issue yet? I'm having same issue where Handler.Callback is being called even after removing callbacks and messages by setting null. – ShrimpCrackers Feb 23 '17 at 03:59
  • 2
    @ShrimpCrackers I found out keeping an instance of your runnable and using `yourHandler.removeCallbacks(yourRunnable)` was the most reliable. Still using that today. – Snaker Feb 24 '17 at 19:37
  • The two parameter version of removeCallbacks must be used and you must have a reference to the Runnable. So, use handler.removeCallbacks(yourRunnable, null). – user1608385 May 22 '17 at 03:35
  • we need to handle any exceptions while using this? Ex. if(handler!=null && handle.isCallingsomething){--}; – Harish Reddy Sep 19 '18 at 06:42
20

For any specific Runnable instance, call Handler.removeCallbacks(). Note that it uses the Runnable instance itself to determine which callbacks to unregister, so if you are creating a new instance each time a post is made, you need to make sure you have references to the exact Runnable to cancel. Example:

Handler myHandler = new Handler();
Runnable myRunnable = new Runnable() {
    public void run() {
        //Some interesting task
    }
};

You can call myHandler.postDelayed(myRunnable, x) to post another callback to the message queue at other places in your code, and remove all pending callbacks with myHandler.removeCallbacks(myRunnable)

Unfortunately, you cannot simply "clear" the entire MessageQueue for a Handler, even if you make a request for the MessageQueue object associated with it because the methods for adding and removing items are package protected (only classes within the android.os package can call them). You may have to create a thin Handler subclass to manage a list of Runnables as they are posted/executed...or look at another paradigm for passing your messages between each Activity

Hope that Helps!

devunwired
  • 62,780
  • 12
  • 127
  • 139
  • Thanks, I know that. But I have a lot of Runnable in many sub-classes, and manage them all is a epic work! Is there anyway to remove them all, in the onStop() event? – Luke Vo May 04 '11 at 13:15
  • Understood, I updated the answer with a bit more information. Short version is you can't call a method to broadly clear a Handler's message queue... – devunwired May 04 '11 at 14:45
10

Define a new handler and runnable:

private Handler handler = new Handler(Looper.getMainLooper());
private Runnable runnable = new Runnable() {
        @Override
        public void run() {
            // Do what ever you want
        }
    };

Call post delayed:

handler.postDelayed(runnable, sleep_time);

Remove your callback from your handler:

handler.removeCallbacks(runnable);
savepopulation
  • 11,736
  • 4
  • 55
  • 80
8

Please note that one should define a Handler and a Runnable in class scope, so that it is created once.removeCallbacks(Runnable) works correctly unless one defines them multiple times. Please look at following examples for better understanding:

Incorrect way :

    public class FooActivity extends Activity {
           private void handleSomething(){
                Handler handler = new Handler();
                Runnable runnable = new Runnable() {
                   @Override
                   public void run() {
                      doIt();
                  }
               };
              if(shouldIDoIt){
                  //doIt() works after 3 seconds.
                  handler.postDelayed(runnable, 3000);
              } else {
                  handler.removeCallbacks(runnable);
              }
           }

          public void onClick(View v){
              handleSomething();
          }
    } 

If you call onClick(..) method, you never stop doIt() method calling before it call. Because each time creates new Handler and new Runnable instances. In this way, you lost necessary references which belong to handler and runnable instances.

Correct way :

 public class FooActivity extends Activity {
        Handler handler = new Handler();
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                doIt();
            }
        };
        private void handleSomething(){
            if(shouldIDoIt){
                //doIt() works after 3 seconds.
                handler.postDelayed(runnable, 3000);
            } else {
                handler.removeCallbacks(runnable);
            }
       }

       public void onClick(View v){
           handleSomething();
       }
 } 

In this way, you don't lost actual references and removeCallbacks(runnable) works successfully.

Key sentence is that 'define them as global in your Activity or Fragment what you use'.

Ananth
  • 2,597
  • 1
  • 29
  • 39
oguzhan
  • 2,073
  • 1
  • 26
  • 23
8

If you don't have the Runnable references, on the first callback, get the obj of the message, and use removeCallbacksAndMessages() to remove all related callbacks.

alphazero
  • 27,094
  • 3
  • 30
  • 26
2

As josh527 said, handler.removeCallbacksAndMessages(null); can work.
But why?
If you have a look at the source code, you can understand it more clearly. There are 3 type of method to remove callbacks/messages from handler(the MessageQueue):

  1. remove by callback (and token)
  2. remove by message.what (and token)
  3. remove by token

Handler.java (leave some overload method)

/**
 * Remove any pending posts of Runnable <var>r</var> with Object
 * <var>token</var> that are in the message queue.  If <var>token</var> is null,
 * all callbacks will be removed.
 */
public final void removeCallbacks(Runnable r, Object token)
{
    mQueue.removeMessages(this, r, token);
}

/**
 * Remove any pending posts of messages with code 'what' and whose obj is
 * 'object' that are in the message queue.  If <var>object</var> is null,
 * all messages will be removed.
 */
public final void removeMessages(int what, Object object) {
    mQueue.removeMessages(this, what, object);
}

/**
 * Remove any pending posts of callbacks and sent messages whose
 * <var>obj</var> is <var>token</var>.  If <var>token</var> is null,
 * all callbacks and messages will be removed.
 */
public final void removeCallbacksAndMessages(Object token) {
    mQueue.removeCallbacksAndMessages(this, token);
}

MessageQueue.java do the real work:

void removeMessages(Handler h, int what, Object object) {
    if (h == null) {
        return;
    }

    synchronized (this) {
        Message p = mMessages;

        // Remove all messages at front.
        while (p != null && p.target == h && p.what == what
               && (object == null || p.obj == object)) {
            Message n = p.next;
            mMessages = n;
            p.recycleUnchecked();
            p = n;
        }

        // Remove all messages after front.
        while (p != null) {
            Message n = p.next;
            if (n != null) {
                if (n.target == h && n.what == what
                    && (object == null || n.obj == object)) {
                    Message nn = n.next;
                    n.recycleUnchecked();
                    p.next = nn;
                    continue;
                }
            }
            p = n;
        }
    }
}

void removeMessages(Handler h, Runnable r, Object object) {
    if (h == null || r == null) {
        return;
    }

    synchronized (this) {
        Message p = mMessages;

        // Remove all messages at front.
        while (p != null && p.target == h && p.callback == r
               && (object == null || p.obj == object)) {
            Message n = p.next;
            mMessages = n;
            p.recycleUnchecked();
            p = n;
        }

        // Remove all messages after front.
        while (p != null) {
            Message n = p.next;
            if (n != null) {
                if (n.target == h && n.callback == r
                    && (object == null || n.obj == object)) {
                    Message nn = n.next;
                    n.recycleUnchecked();
                    p.next = nn;
                    continue;
                }
            }
            p = n;
        }
    }
}

void removeCallbacksAndMessages(Handler h, Object object) {
    if (h == null) {
        return;
    }

    synchronized (this) {
        Message p = mMessages;

        // Remove all messages at front.
        while (p != null && p.target == h
                && (object == null || p.obj == object)) {
            Message n = p.next;
            mMessages = n;
            p.recycleUnchecked();
            p = n;
        }

        // Remove all messages after front.
        while (p != null) {
            Message n = p.next;
            if (n != null) {
                if (n.target == h && (object == null || n.obj == object)) {
                    Message nn = n.next;
                    n.recycleUnchecked();
                    p.next = nn;
                    continue;
                }
            }
            p = n;
        }
    }
}
1

None of the solutions worked for me. But I found a solution that worked.

The runnables already added in the handler queue got executed even after calling handler.removeCallbacksAndMessages(null). When I tried to stop the thread, it resulted in the error:

W/MessageQueue(6436): java.lang.RuntimeException: Handler (android.os.Handler) {416659f0} sending message to a Handler on a dead thread

My solution:

  • To remove all the callbacks. you need the reference for all the runnables which can be stored in an ArrayList.

     private ArrayList<Runnable> runnableQueue=new ArrayList<Runnable>();
    
  • Then every time u want to post a runnable, store it in the array, then post the array item using handler.post().

    private void postInHandler(){
      @override
      runnableQueue.add(new Runnable() {
          public void run() {   
              //your code 
          }
      });
      //Post the last item in the array 
      handler.post(runnableQueue.get(runnableQueue.size()-1));
      }
    
  • Then to remove all the callbacks use this method which will remove each callback by iterating through the array.

      private void removeHandlerCallbacks(){
         for(Runnable runnable:runnableQueue){
             networkHandler.removeCallbacks(runnable,null);
         }
         runnableQueue.clear();
     }
    
  • Hurray! the queue is cleared. But wait. After clearing the array we have to make sure that no more runnable is posted in the handler before you stop the thread. So you have to declare:

    boolean allowPosting=true;

so include this:

private void removeHandlerCallbacks(){           
    allowPosting=false;//add this line to stop posting after clearing the array
    for(Runnable runnable:runnableQueue){
        handler.removeCallbacks(runnable,null);
    }
     //Dont forget to clear the array
     runnableQueue.clear();
}

Then check the condition before posting in the handler:

if(allowPosting){
    postInHandler();
}

That's all, now the queue is cleared and we are sure that no more runnable is posted after clearing the queue. So it is safe to stop the thread.

halfer
  • 19,824
  • 17
  • 99
  • 186
0

The documentation says that removeCallbacksAndMessages(null) removes all callbacks, but it is not always true. Try this:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
    handler.removeCallbacksAndMessages("ACTION_NAME")
    handler.postDelayed(runnable, "ACTION_NAME", 10_000) //if you want restart runnable
} else {
    handler.removeCallbacksAndMessages(null)
    handler.postDelayed(runnable, 10_000) //if you want restart runnable
}
antaki93
  • 704
  • 7
  • 10
-1

To remove specific runnable

handler.removeCallbacks(yourRunnable)

To remove all runnables

handler.removeCallbacksAndMessages(null)
Tarun Anchala
  • 2,232
  • 16
  • 15