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

J2ME 實現阻塞式對話框:模擬 Win32 Dialog 同步交互模式

     編輯:J2ME

痛點:事件回調地獄

MIDP UI 採事件回調機制:系統 UI 線程維護組件,開發者註冊 CommandListener,事件觸發時回調 commandAction()。處理簡單的「是/否」確認對話框時,不得不:

  • 為每個對話框寫專屬 Listener 類,類爆炸
  • 或在同一 Listener 用大量 if-else 判斷來源,邏輯碎片化

期望體驗:像 Win32 DialogBox() 一樣阻塞當前線程,同步等待返回值:


int choice = Dialog.show("確認退出?", Dialog.YES_NO);
if (choice == Dialog.YES) { exitGame(); }
else { resumeGame(); }
// 代碼連貫、邏輯內聚

核心難點與解法

難點解決方案
不能阻塞系統 UI 線程 將所有遊戲邏輯移至獨立工作線程,UI 線程僅負責渲染/轉發事件
工作線程如何等待用戶輸入 引入信號量(boolean flag = false)+ wait()/notifyAll() 機制

完整實現方案

1. 同步對話框核心類


public class SyncDialog {
    private static final int YES = 1, NO = 0, CANCEL = -1;
    private int result = CANCEL;
    private boolean ready = false;
    private final Object lock = new Object();

    // 由 UI 線程調用:顯示對話框並註冊臨時 Listener
    public void show(final Display display, final String title, final int type) {
        // 必須在 UI 線程執行
        Display.getDisplay(midlet).callSerially(new Runnable() {
            public void run() {
                Form form = new Form(title);
                if ((type & YES) != 0) form.addCommand(new Command("是", Command.OK, 1));
                if ((type & NO) != 0) form.addCommand(new Command("否", Command.NO, 2));
                form.setCommandListener(new CommandListener() {
                    public void commandAction(Command c, Displayable d) {
                        if (c.getCommandType() == Command.OK) result = YES;
                        else if (c.getCommandType() == Command.NO) result = NO;
                        else result = CANCEL;
                        synchronized (lock) { ready = true; lock.notifyAll(); }
                    }
                });
                display.setCurrent(form);
            }
        });
    }

    // 由工作線程調用:阻塞等待結果
    public int waitForResult() throws InterruptedException {
        synchronized (lock) {
            while (!ready) lock.wait();  // 釋放鎖並進入等待
        }
        return result;
    }
}

2. 遊戲主線程使用範例


public class GameThread extends Thread {
    private SyncDialog dialog = new SyncDialog();
    private Display display;

    public void run() {
        // ... 遊戲循環 ...
        if (playerHitsExit()) {
            // 同步彈窗,不阻塞 UI 線程
            dialog.show(display, "確定退出?", SyncDialog.YES | SyncDialog.NO);
            int choice = dialog.waitForResult();  // 當前線程掛起
            if (choice == SyncDialog.YES) {
                saveAndExit();
            }
            // 否則繼續遊戲循環
        }
    }
}

3. 關鍵點總結

  1. UI 線程純淨:callSerially() 確保對話框創建在系統 UI 線程,不違反 MIDP 單線程 UI 規則。
  2. 工作線程阻塞:wait() 釋放 CPU,不佔用資源;用戶點擊後 notifyAll() 喚醒。
  3. 狀態封裝:result + ready 雙變量避免虛假喚醒競態。
  4. 可擴展性:輕鬆增加輸入框、列表選擇、進度條等同步 UI 組件。

進階:通用同步 UI 工具類

將上述模式封裝為 SyncUI 工具類,提供:

  • confirm(title) → boolean
  • input(title, defaultText) → String
  • select(title, String[] options) → int
  • progress(title, max) → ProgressHandle(支援取消)

所有方法內部均採用「UI 線程創建組件 + 工作線程 wait()」模式,業務代碼保持同步寫法,極大降低認知負荷。

注意事項

  • 不可在 UI 線程調用 waitForResult(),會導致死鎖(UI 線程自己等待自己永遠不會發出的通知)。
  • 對話框顯示期間,遊戲邏輯暫停;若需背景動畫繼續播放,可在 paint() 中維持幀更新,僅邏輯線程阻塞。
  • 真機測試驗證:來電、短信中斷時對話框行為,確保 ready 標誌正確重置。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved