使用Handler.postDelayed(Runnable, delayMillis)實現周期性任務時,常見錯誤是只在外部調用一次,導致任務僅執行一次。關鍵在於在run()方法結尾處再次postDelayed。
mRunnable = new Runnable() {
@Override public void run() {
if (count == 0) { setImage(); count = 1; }
else { weather = ...; count = 0; }
// 缺少遞歸postDelayed,任務結束後不再調度
}
};
mHandler = new Handler();
mHandler.postDelayed(mRunnable, 3000); // 只執行一次
mHandler = new Handler();
mRunnable = new Runnable() {
@Override
public void run() {
// 1. 執行具體任務邏輯
if (count == 0) {
setImage();
count = 1;
} else {
weather = mContentResolver.getType(mUri);
setWeather(weather);
count = 0;
}
// 2. 關鍵:在run結尾重新postDelayed,形成循環
mHandler.postDelayed(this, 3000); // this指向當前Runnable實例
}
};
// 3. 啟動:直接調用run()或postDelayed均可
mRunnable.run(); // 立即執行第一次
// 或 mHandler.postDelayed(mRunnable, 3000); // 延遲3秒執行第一次
mHandler.postDelayed(this, 3000)在run()末尾調用,確保每次執行完重新排隊。this:傳遞當前Runnable實例,避免創建新對象。stop()方法調用mHandler.removeCallbacks(mRunnable),防止內存洩漏與Activity銷毀後繼續執行。
public class PeriodicTask {
private final Handler handler = new Handler(Looper.getMainLooper());
private final Runnable runnable;
private final long intervalMillis;
private boolean running = false;
public PeriodicTask(long intervalMillis, Runnable task) {
this.intervalMillis = intervalMillis;
this.runnable = () -> {
task.run();
if (running) handler.postDelayed(runnable, intervalMillis);
};
}
public void start() {
if (!running) {
running = true;
handler.post(runnable); // 立即執行首次
}
}
public void stop() {
running = false;
handler.removeCallbacks(runnable);
}
}
// 使用
PeriodicTask task = new PeriodicTask(3000, () -> {
// 任務邏輯
});
task.start();
// onDestroy()中 task.stop();
| 方案 | 優點 | 缺點 |
|---|---|---|
| Handler + postDelayed | 輕量、主線程安全、易控制 | 需手動管理生命周期 |
| ScheduledExecutorService | 標準API、支持固定頻率/延遲 | 非主線程,更新UI需runOnUiThread |
| Timer/TimerTask | 簡單 | 異常會終止所有任務、不推薦新代碼 |
| RxJava interval | 響應式、鏈式操作、自動生命周期綁定 | 引入依賴、學習曲線 |
| Kotlin Coroutines | 結構化並發、自動取消 | 需Kotlin環境 |