【发布时间】:2011-08-18 12:35:46
【问题描述】:
我的子活动中有一个Handler,它由主Activity 调用。这个Handler被子类使用到postDelay一些Runnables,我无法管理它们。现在,在onStop 事件中,我需要在完成活动之前删除它们(不知何故我调用了finish(),但它仍然一次又一次地调用)。无论如何要从处理程序中删除所有回调?
【问题讨论】:
标签: java android callback android-handler
我的子活动中有一个Handler,它由主Activity 调用。这个Handler被子类使用到postDelay一些Runnables,我无法管理它们。现在,在onStop 事件中,我需要在完成活动之前删除它们(不知何故我调用了finish(),但它仍然一次又一次地调用)。无论如何要从处理程序中删除所有回调?
【问题讨论】:
标签: java android callback android-handler
请注意,应该在类范围内定义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 中将它们定义为全局'。
【讨论】:
正如josh527 所说,handler.removeCallbacksAndMessages(null); 可以工作。
但是为什么呢?
如果你看一下源代码,你可以更清楚地理解它。
有 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;
}
}
}
【讨论】:
根据我的经验,这很有效!
handler.removeCallbacksAndMessages(null);
在 removeCallbacksAndMessages 的文档中它说...
删除所有待处理的回调帖子和 obj 为令牌的已发送消息。 如果token是
null,所有的回调和消息都会被移除。
【讨论】:
removeCallbacksAndMessages(null) 的一些问题,不会删除我的一些回调。当我想停止接收回调时,我会调用 handler.removeCallbacksAndMessages(null) 并将我的处理程序设置为 null,但由于我仍然会收到回调,所以当我想使用 handler.postDelayed() 循环时会遇到 NPE。
yourHandler.removeCallbacks(yourRunnable) 是最可靠的。今天还在用。
定义一个新的处理程序并运行:
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);
【讨论】:
对于任何特定的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 引用,在第一次回调时,获取消息的 obj,并使用 removeCallbacksAndMessages() 删除所有相关回调。
【讨论】: