問題起因
點擊按鈕就調用 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); } }); } }
Handler removeCallbacks 無效問題#
當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<Integer,Handler> sHandlerMap=new HashMap<>();
public static Map<Integer,Runnable> sRunnableMap=new HashMap<>();
public static void stop() {
for (int i=0;i<NewsApplication.sHandlerMap.size();i++){
if (NewsApplication.sHandlerMap.get(i)!=null){
//NewsApplication.sHandlerMap.get(i).removeCallbacksAndMessages(null);
NewsApplication.sHandlerMap.get(i).removeCallbacks(NewsApplication.sRunnableMap.get(i));
}
}
}
/**
* Handler 容易造成內存泄漏,需要在application中全局保存
*/
public void start() {
if(NewsApplication.sRunnableMap.get(mIndex)==null){
RollRunable mRollRunable=new RollRunable();
NewsApplication.sRunnableMap.put(mIndex,mRollRunable);
}
if(NewsApplication.sHandlerMap.get(mIndex)==null){
Handler handler=new Handler();
NewsApplication.sHandlerMap.put(mIndex,handler);
}
NewsApplication.sHandlerMap.get(mIndex).postDelayed(NewsApplication.sRunnableMap.get(mIndex),2000);
}
以上解決方案適用于任意 handler 的任務移除。
引用
部分內容轉自博客