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

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

【问题讨论】:

    标签: java android callback android-handler


    【解决方案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 种方法可以从处理程序(消息队列)中删除回调/消息:

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

      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() 删除所有相关回调。

          【讨论】:

            猜你喜欢
            • 2021-10-29
            • 1970-01-01
            • 1970-01-01
            • 2019-09-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多