問題起因
點擊按鈕就調(diào)用 handler.post(runnable); 就能啟動定時器,這里是每隔1s打印線程名字马胧,從打印中我們可以知道,他并沒有另開線程殿衰,而是運行在 UI 線程當(dāng)中幔妨,當(dāng)你要取消定時器的時候阅仔,只需要調(diào)用 handler.removeCallbacks(runnable) 就可以了薪伏。
上面中有一個問題,有時候你會發(fā)現(xiàn)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 無效問題#
當(dāng)Activity進(jìn)入后臺運行后再轉(zhuǎn)入前臺運行,removeCallbacks 無法將 updateThread 從 message queue 中移除梗夸。
這是為什么呢层玲?
在 Activity 由前臺轉(zhuǎn)后臺過程中,線程是一直在運行的反症,但是當(dāng) Activity 轉(zhuǎn)入前臺時會重新定義 Runnable runnable辛块;也就是說此時從message queue 移除的 runnable 與原先加入 message queue中的 runnable 并非是同一個對象。如果把 runnable 定義為靜態(tài)的則removeCallbacks 不會失效,對于靜態(tài)變量在內(nèi)存中只有一個拷貝(節(jié)省內(nèi)存)铅碍,JVM只為靜態(tài)分配一次內(nèi)存润绵,在加載類的過程中完成靜態(tài)變量的內(nèi)存分配。
也就是說 removeCallbacks 有時會出現(xiàn)移除失效的問題主要原因出在 runnable 對象上胞谈。
但是盡管我用了 removeCallbacksAndMessages 方法尘盼,依然有時會出現(xiàn)失效的現(xiàn)象,輸出內(nèi)存地址比較過后烦绳,發(fā)現(xiàn) handler 對象也會發(fā)生變化卿捎。
解決方案
在 Application中建立容器存儲 handler 和 runnable 對象,關(guān)閉時使用容器來關(guān)閉径密。
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 容易造成內(nèi)存泄漏午阵,需要在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 的任務(wù)移除。
引用
部分內(nèi)容轉(zhuǎn)自博客