首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> J2ME

J2ME TimerTask 深度解析:從基礎用法到遊戲循環架構

     編輯:J2ME

為什麼選 TimerTask 而非 Thread?

J2ME 裝置 CPU 慢、記憶體少,手動管理執行緒池、同步鎖、睡眠喚醒極易出錯。java.util.Timer + TimerTask 提供單執行緒排程,任務依序執行、無需同步、資源佔用極低,是 MIDP 遊戲主循環的首選。

基礎 API 快速查閱

Timer timer = new Timer();
// 一次性執行:500ms 後執行
timer.schedule(new MyTask(), 500);
// 週期性執行:首次 1s 後,之後每 20s
timer.schedule(new MyTask(), 1000, 20000);
// 固定頻率執行:以 scheduled time 為基準(適合計時器)
timer.scheduleAtFixedRate(task, delay, period);
// 取消單一任務
task.cancel();
// 取消整個 Timer(會清除佇列所有任務)
timer.cancel();

關鍵特性:所有任務共用同一背景執行緒;若某任務 run() 耗時過長,後續任務會排隊延遲;不保證精確觸發時間,僅保證順序。

實戰封裝:狀態驅動的遊戲任務

1. 通用任務包裝器

public class GameTask extends TimerTask {
    private final GameObject target;
    public GameTask(GameObject obj) { this.target = obj; }
    public void run() { target.gameTask(); }
}

2. 遊戲物件介面

public interface GameObject {
    void gameTask();  // 由 TimerTask 呼叫
}

3. Canvas 實作狀態機

public class GameCanvas extends FullCanvas implements GameObject {
    private static Timer timer;
    private static byte state;  // 0=開場, 1=主選單, 2=遊戲中...

    public void gameTask() {
        switch (state) {
            case 0: introTask(); break;
            case 1: menuTask(); break;
            case 2: playTask(); break;
        }
    }

    // 例:閃爍效果每 500ms 切換
    private void menuTask() {
        flash = !flash;
        repaint();
        timer.schedule(new GameTask(this), 500L);  // 遞迴排程
    }

    // 按鍵切換狀態
    protected void keyPressed(int keyCode) {
        switch (state) {
            case 1: menuKey(keyCode); break;
            case 2: playKey(keyCode); break;
        }
    }

    private void menuKey(int keyCode) {
        if (keyCode == -7) {  // 確認鍵
            timer.cancel();
            state = 2;
            repaint();
            new Timer().schedule(new GameTask(this), 500L);
        }
    }

    protected void paint(Graphics g) {
        switch (state) {
            case 0: drawIntro(g); break;
            case 1: drawMenu(g); break;
            case 2: drawPlay(g); break;
        }
    }
}

進階模式:固定時間步長遊戲循環

遞迴 schedule 易產生累積誤差。專業遊戲採用固定時間步長 + 積累器:

public class FixedStepGameLoop extends TimerTask {
    private static final long STEP_MS = 16; // ~60 FPS
    private long accumulator = 0;
    private long lastTime = System.currentTimeMillis();

    public void run() {
        long now = System.currentTimeMillis();
        long delta = now - lastTime;
        lastTime = now;
        accumulator += delta;

        while (accumulator >= STEP_MS) {
            update(STEP_MS);  // 邏輯更新
            accumulator -= STEP_MS;
        }
        render();             // 渲染(可變幀率)
        // 下一幀排程
        timer.schedule(this, STEP_MS);
    }
}

避坑指南

坑點症狀對策
run() 耗時 > period幀率崩塌、輸入延遲邏輯/渲染分離;耗時操作放入獨立執行緒
多 Timer 競爭同步問題、CPU 爭搶全程式單一 Timer;任務內部用狀態機分流
cancel() 後重用 TimerIllegalStateException新建 Timer 實例;或用 purge() 清理已取消任務
裝置睡眠/來電中斷Timer 暫停、時間跳躍在 pauseApp() 記錄時間戳,startApp() 校正 accumulator

效能對比:TimerTask vs Thread

  • 記憶體:Timer 單執行緒 ~1 KB 堆疊;Thread 每個 ~8-16 KB。
  • 切換開銷:Timer 內部輪詢;Thread 需 OS 級上下文切換。
  • 精度:兩者皆受限於 JVM 排程器,通常 ±10-50 ms。
  • 適用場景:Timer 適合「大量簡短週期性任務」;Thread 適合「長耗時 I/O、網路、複雜計算」。

結語

TimerTask 是 J2ME 遊戲主循環的基石。掌握「狀態機驅動 + 遞迴排程」或「固定時間步長 + 積累器」兩大模式,配合單 Timer 原則與耗時操作異步化,即可在 200 KB 記憶體、100 MHz CPU 的功能機上跑出流暢 20+ FPS 遊戲體驗。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved