问题起因
点击按钮就调用 handler.post(runnable); 就能启动定时器,这里是每隔1s打印线程名字,从打印中我们可以知道,他并没有另开线程,而是运行在 UI 线程当中,当你要取消定时器的时候,只需要调用 handler.removeCallbacks(runnable) 就可以了。
上面中有一个问题,有时候你会发现removeCallbacks有时候会失效,不能从消息队列中移除,看下面的代码
public class TimerActivity extends Activity {
Handler handler = new Handler();
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("update...");
handler.postDelayed(runnable, 1000);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.layout_roll_view);
Button mButtonStart = (Button) findViewById(R.id.button1);
Button mButtonStop = (Button) findViewById(R.id.button2);
mButtonStart.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
handler.post(runnable);
}
});
mButtonStop.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
handler.removeCallbacks(runnable);
}
});
}
}
当Activity进入后台运行后再转入前台运行,removeCallbacks 无法将 updateThread 从 message queue 中移除。
这是为什么呢?
在 Activity 由前台转后台过程中,线程是一直在运行的,但是当 Activity 转入前台时会重新定义 Runnable runnable;也就是说此时从message queue 移除的 runnable 与原先加入 message queue中的 runnable 并非是同一个对象。如果把 runnable 定义为静态的则removeCallbacks 不会失效,对于静态变量在内存中只有一个拷贝(节省内存),JVM只为静态分配一次内存,在加载类的过程中完成静态变量的内存分配。
也就是说 removeCallbacks 有时会出现移除失效的问题主要原因出在 runnable 对象上。
但是尽管我用了 removeCallbacksAndMessages 方法,依然有时会出现失效的现象,输出内存地址比较过后,发现 handler 对象也会发生变化。
解决方案
在 Application中建立容器存储 handler 和 runnable 对象,关闭时使用容器来关闭。
public static Map sHandlerMap=new HashMap<>();
public static Map sRunnableMap=new HashMap<>();
public static void stop() {
for (int i=0;i
以上解决方案适用于任意 handler 的任务移除。
引用
部分内容转自博客