如何从处理程序中删除所有回调?

Posted

技术标签:

【中文标题】如何从处理程序中删除所有回调?【英文标题】:How to remove all callbacks from a Handler? 【发布时间】:2011-08-18 12:35:46 【问题描述】:

我的子活动中有一个Handler,它由主Activity 调用。这个Handler被子类使用到postDelay一些Runnables,我无法管理它们。现在,在onStop 事件中,我需要在完成活动之前删除它们(不知何故我调用了finish(),但它仍然一次又一次地调用)。无论如何要从处理程序中删除所有回调?

【问题讨论】:

【参考方案1】:

请注意,应该在类范围内定义HandlerRunnable,以便创建一次。removeCallbacks(Runnable) 可以正常工作,除非多次定义它们。请查看以下示例以更好地理解:

错误的方式:

    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();
          
     

如果你调用onClick(..) 方法,你永远不会在它调用之前停止doIt() 方法的调用。因为每次都会创建new Handlernew Runnable 实例。这样,您丢失了属于 handlerrunnable 实例的必要引用。

正确方法:

 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();
       
  

这样,您不会丢失实际引用,removeCallbacks(runnable) 可以成功运行。

关键句是'在你使用的ActivityFragment 中将它们定义为全局'

【讨论】:

【参考方案2】:

正如josh527 所说,handler.removeCallbacksAndMessages(null); 可以工作。 但是为什么呢? 如果你看一下源代码,你可以更清楚地理解它。 有 3 种方法可以从处理程序(消息队列)中删除回调/消息:

    通过回调(和令牌)删除 通过 message.what(和令牌)删除 按令牌删除

Handler.java(留一些重载方法)

/**
 * 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 做真正的工作:

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;
        
    

【讨论】:

【参考方案3】:

根据我的经验,这很有效!

handler.removeCallbacksAndMessages(null);

在 removeCallbacksAndMessages 的文档中它说...

删除所有待处理的回调帖子和 obj 为令牌的已发送消息。 如果token是null,所有的回调和消息都会被移除。

【讨论】:

@Malachiasz 我想我会在 onStop 或 onPause 中使用它,以确保在活动失去焦点后不会处理任何消息。但取决于触发回调/消息时需要做什么 我相信我之前在某些手机上看到过 NPE,但已经有一段时间了。 我遇到了removeCallbacksAndMessages(null) 的一些问题,不会删除我的一些回调。当我想停止接收回调时,我会调用 handler.removeCallbacksAndMessages(null) 并将我的处理程序设置为 null,但由于我仍然会收到回调,所以当我想使用 handler.postDelayed() 循环时会遇到 NPE。 @Snaker 你的问题解决了吗?我遇到了同样的问题,即使在通过设置 null 删除回调和消息之后,也会调用 Handler.Callback。 @ShrimpCrackers 我发现保留一个可运行的实例并使用yourHandler.removeCallbacks(yourRunnable) 是最可靠的。今天还在用。【参考方案4】:

定义一个新的处理程序并运行:

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

电话发布延迟:

handler.postDelayed(runnable, sleep_time);

从您的处理程序中删除您的回调:

handler.removeCallbacks(runnable);

【讨论】:

【参考方案5】:

对于任何特定的Runnable 实例,请致电Handler.removeCallbacks()。请注意,它使用Runnable 实例本身来确定要取消注册的回调,因此如果您在每次发布帖子时都创建一个新实例,则需要确保引用确切的Runnable 以取消。示例:

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

您可以调用 myHandler.postDelayed(myRunnable, x) 将另一个回调发布到代码中其他位置的消息队列,并使用 myHandler.removeCallbacks(myRunnable) 删除所有待处理的回调

不幸的是,您不能简单地“清除”整个MessageQueue 以获得Handler,即使您请求与之关联的MessageQueue 对象也是如此,因为添加和删除项目的方法是包保护的(仅android.os 包中的类可以调用它们)。您可能必须创建一个精简的Handler 子类来管理Runnables 的列表,因为它们被发布/执行...或者查看另一个范例,用于在每个Activity 之间传递您的消息

希望有帮助!

【讨论】:

谢谢,我知道。但是我在很多子类中有很多Runnable,管理它们都是史诗般的工作!无论如何在 onStop() 事件中将它们全部删除? 明白,我用更多信息更新了答案。简短的版本是你不能调用一个方法来广泛清除处理程序的消息队列......【参考方案6】:

如果没有 Runnable 引用,在第一次回调时,获取消息的 obj,并使用 removeCallbacksAndMessages() 删除所有相关回调。

【讨论】:

以上是关于如何从处理程序中删除所有回调?的主要内容,如果未能解决你的问题,请参考以下文章

在遍历事件处理程序集合时,如何安全地从回调中移除处理程序?

如何从事件中删除所有事件处理程序

如何从事件中删除所有事件处理程序

从事件处理程序回调调用的函数中“this”的值?

如何删除所有eventhandler

立即从同步代码执行异步回调