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

MIDlet 生命週期深度解析與來電歸來死機問題排查

     編輯:J2ME

核心概念:MIDlet 三大狀態

J2ME 應用由應用管理軟體 (AMS/JAM) 控制生命週期,包含三種狀態:

  • Paused:建構子執行完畢後進入,等待啟動。
  • Active:startApp() 被呼叫後進入,正常執行中。
  • Destroyed:destroyApp() 完成後進入,資源釋放完畢。

關鍵回調方法時序

事件呼叫順序說明
啟動應用建構子 → startApp()建構子只執行一次,適合放一次性初始化
來電/切換到系統選單hideNotify() → pauseApp()畫面隱藏、進入暫停
掛斷/返回應用showNotify() → paint() → startApp()注意:startApp() 會被重複呼叫!

正確的初始化模式

錯誤寫法:在 startApp() 中 new MyCanvas(),導致每次回來都建立新 Canvas,舊資源洩漏、遊戲狀態重置。

正確寫法一:建構子初始化

public class MyMIDlet extends MIDlet {
    Display display;
    MyCanvas canvas;

    public MyMIDlet() {
        display = Display.getDisplay(this);
        canvas = new MyCanvas(); // 僅一次
    }

    public void startApp() {
        display.setCurrent(canvas); // 僅設定顯示
    }
    // ...
}

正確寫法二:延遲初始化 + 旗標

public void startApp() {
    if (display == null) { // 首次才執行
        display = Display.getDisplay(this);
        canvas = new MyCanvas();
    }
    display.setCurrent(canvas);
}

Canvas 繪製與執行緒模型

  • showNotify() → paint() → 執行緒 run() 開始。
  • 來電時:hideNotify() → pauseApp()。執行緒 仍在運行(除非你自行停止),但 repaint() 不會觸發 paint()。
  • 返回時:showNotify() → paint()(系統自動呼叫一次) → startApp()。

標準遊戲迴圈範例:

public void run() {
    while (running) {
        try { Thread.sleep(50); } catch (Exception e) {}
        repaint(); // 排程重繪
    }
}

死機元兇:Graphics 物件快取

許多開發者為了效能,將 paint(Graphics g) 傳入的 Graphics 參考存入成員變數 mg,並在輔助繪圖方法中直接使用 mg.drawString(...)。這在 Nokia 7610、6600 等機型上會導致來電歸來後死機或畫面錯亂。

錯誤模式

Graphics mg; // 成員變數

public void paint(Graphics g) {
    mg = g; // 危險!g 可能在下次 paint 失效
    drawMenu();
    drawGame();
}

void drawMenu() { mg.drawString(...); } // 直接用 mg

正確模式一:參數傳遞(推薦)

public void paint(Graphics g) {
    drawMenu(g);
    drawGame(g);
}

void drawMenu(Graphics g) { g.drawString(...); }
void drawGame(Graphics g) { g.drawString(...); }

正確模式二:若必須快取,需加防禦判斷

public void paint(Graphics g) {
    if (mg == null) mg = g; // 僅首次指派
    // 但仍有風險:系統可能回收舊 Graphics 實例
    // 最佳做法仍是參數傳遞
}

補充:display.callSerially(Runnable)

若需在事件執行緒中安全更新 UI,可使用 display.callSerially(this) 並實作 Runnable.run(),系統會在下一次繪製迴圈前執行,避免多執行緒競爭。

總結檢查清單

  1. 建構子放一次性初始化;startApp() 僅 setCurrent()。
  2. 所有繪圖方法接收 Graphics 參數,嚴禁 快取 Graphics 實例。
  3. 執行緒迴圈以 repaint() 排程重繪,不直接呼叫 paint()。
  4. pauseApp() 暫停邏輯/音效;destroyApp() 釋放資源、停止執行緒。

遵循上述原則,可徹底解決來電/切換應用導致的死機與畫面異常。

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