J2ME (CLDC/MIDP) 環境資源極度受限:弱 CPU、少量記憶體 (幾十~百 KB)、無預 emptive 多執行緒支援。傳統 `Thread` + `sleep()` 方式:
`Timer` + `TimerTask` 提供**輕量級排程機制**:單一背景執行緒循序執行所有任務,無同步困擾,API 簡潔,極適合資源受限裝置。
java.util.Timer:排程器,維護單一背景執行緒java.util.TimerTask:抽象類,實作 `run()` 定義任務邏輯Timer timer = new Timer();
// 1. 延遲一次性執行
timer.schedule(task, delayMs);
// 2. 延遲 + 固定間隔重複 (固定延遲:下次執行時間 = 上次完成時間 + period)
timer.schedule(task, delayMs, periodMs);
// 3. 固定頻率重複 (固定頻率:下次執行時間 = 上次開始時間 + period,會追趕延遲)
timer.scheduleAtFixedRate(task, delayMs, periodMs);
// 4. 指定日期一次性執行
timer.schedule(task, date);
// 5. 指定日期 + 固定間隔
timer.schedule(task, date, periodMs);
run():任務邏輯,**必須快速完成**,避免阻塞後續任務cancel():取消自身任務 (在 `run()` 內呼叫可實現條件停止)scheduledExecutionTime():取得理論排程執行時間public class CountdownTask extends TimerTask {
private int counter;
public CountdownTask(int start) {
this.counter = start;
}
@Override
public void run() {
counter--;
System.out.println("Counter: " + counter);
if (counter <= 0) {
cancel(); // 停止自身
}
}
}
// 使用
Timer timer = new Timer();
timer.schedule(new CountdownTask(50), 1000, 2000); // 1秒後開始,每2秒執行一次
針對遊戲/動畫場景,封裝 **狀態機 + TimerTask 委派模式**,實現清晰的遊戲階段控制。
public interface GameObject {
void gameTask(); // 由 TimerTask 呼叫的統一入口
}
import java.util.TimerTask;
public class GAMETask extends TimerTask {
private GameObject target;
public GAMETask(GameObject object) {
this.target = object;
}
@Override
public void run() {
target.gameTask(); // 委派給遊戲物件處理
}
}
import javax.microedition.lcdui.game.GameCanvas;
import javax.microedition.lcdui.Graphics;
import java.util.Timer;
public class GameCanvas extends GameCanvas implements GameObject {
private static Timer timer;
private static byte gameState; // 0=初始, 1=遊戲中, 2=暫停, 3=結束...
public GameCanvas(MIDlet midlet) {
super(false); // 不使用預設按鍵處理
// 初始化資源...
}
// GameObject 介面實作:TimerTask 統一入口
public void gameTask() {
switch (gameState) {
case 0: initGameTask(); break;
case 1: mainGameTask(); break;
case 2: pauseGameTask(); break;
case 3: overGameTask(); break;
}
}
// 各狀態具體邏輯
private void mainGameTask() {
// 遊戲邏輯更新:碰撞檢測、移動、分數...
// 畫面重繪觸發
repaint();
// 續排下一幀 (約 20 FPS = 50ms)
timer.schedule(new GAMETask(this), 50);
}
private void pauseGameTask() {
// 閃爍效果等
flash = !flash;
repaint();
timer.schedule(new GAMETask(this), 500); // 較低頻率
}
// 按鍵處理驅動狀態轉換
protected void keyPressed(int keyCode) {
switch (gameState) {
case 0: handleStartInput(keyCode); break;
case 1: handleGameInput(keyCode); break;
case 2: handlePauseInput(keyCode); break;
}
}
private void handleStartInput(int keyCode) {
if (keyCode == Canvas.KEY_STAR) { // *鍵開始
timer.cancel(); // 取消前一任務
gameState = 1;
repaint();
new Timer().schedule(new GAMETask(this), 50); // 新 Timer 啟動主迴路
}
}
// 繪製邏輯同樣按狀態分派
protected void paint(Graphics g) {
switch (gameState) {
case 0: drawStartScreen(g); break;
case 1: drawGameScreen(g); break;
case 2: drawPauseScreen(g); break;
case 3: drawGameOver(g); break;
}
}
}
| 設計點 | 說明 |
|---|---|
| 單一 Timer 實例 | 全遊戲共用一個 `Timer`,避免多執行緒同步問題 |
| 狀態機驅動 | `gameState` 變數控制邏輯分派,結構清晰易擴展 |
| TimerTask 輕量委派 | `GAMETask` 僅負責呼叫 `gameTask()`,不包含業務邏輯 |
| 遞歸排程 | 每次 `run()` 結尾 `schedule` 下一次,實現遊戲迴路 |
| 狀態切換取消舊任務 | `timer.cancel()` 確保無殘留任務干擾新狀態 |
| repaint() 非阻塞 | 只請求重繪,實際繪製在事件執行緒,不阻塞 Timer 執行緒 |
J2ME `TimerTask` 是資源受限環境下的**最佳排程選擇**:單執行緒循序執行、無同步負擔、API 極簡。遊戲開發中,結合**狀態機 + 遞歸排程 + 委派模式**,可構建清晰、高效、可維護的主迴路架構。核心原則:**TimerTask 做調度,GameObject 做邏輯,paint() 做繪製,三者分離,各司其職。**