如何从处理程序中删除所有回调?
Posted
技术标签:
【中文标题】如何从处理程序中删除所有回调?【英文标题】:How to remove all callbacks from a Handler? 【发布时间】:2011-08-18 12:35:46 【问题描述】:我的子活动中有一个Handler,它由主Activity 调用。这个Handler被子类使用到postDelay
一些Runnables,我无法管理它们。现在,在onStop
事件中,我需要在完成活动之前删除它们(不知何故我调用了finish()
,但它仍然一次又一次地调用)。无论如何要从处理程序中删除所有回调?
【问题讨论】:
【参考方案1】:请注意,应该在类范围内定义Handler
和Runnable
,以便创建一次。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 Handler
和new Runnable
实例。这样,您丢失了属于 handler 和 runnable 实例的必要引用。
正确方法:
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)
可以成功运行。
关键句是'在你使用的Activity
或Fragment
中将它们定义为全局'。
【讨论】:
【参考方案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
子类来管理Runnable
s 的列表,因为它们被发布/执行...或者查看另一个范例,用于在每个Activity
之间传递您的消息
希望有帮助!
【讨论】:
谢谢,我知道。但是我在很多子类中有很多Runnable,管理它们都是史诗般的工作!无论如何在 onStop() 事件中将它们全部删除? 明白,我用更多信息更新了答案。简短的版本是你不能调用一个方法来广泛清除处理程序的消息队列......【参考方案6】:如果没有 Runnable 引用,在第一次回调时,获取消息的 obj,并使用 removeCallbacksAndMessages() 删除所有相关回调。
【讨论】:
以上是关于如何从处理程序中删除所有回调?的主要内容,如果未能解决你的问题,请参考以下文章