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

基於 MIDP 1.0 實作 RMS 容量偵測器:測試手機真實可用儲存空間

     編輯:J2ME

背景動機

在 Nokia 6108 開發個人通訊錄時,發現 RecordStore.getSizeAvailable() 回傳值幾乎等於手機剩餘總記憶體,顯然該 API 實作有誤,無法反映單一 RecordStore 的真實寫入上限。為此開發一款輕量級 RMS 容量偵測器,以實測方式探測單一 RecordStore 最大可寫入資料量(單位:KB)。

⚠ 重要提醒:請勿在模擬器執行,模擬器儲存機制與實機截然不同,測試結果無參考價值。

架構設計:三大類別職責

  • RMSModel:封裝 RecordStore 讀寫與刪除邏輯。
  • CounterCanvas:繼承 Canvas,負責即時繪製進度條與顯示最終結果,並透過 Timer 定時觸發寫入。
  • RMSAnalyzer (MIDlet):主控制器,處理生命週期、命令事件、錯誤提示與參數讀取。

核心邏輯:定時遞增寫入直到失敗

原理極簡:建立一個 RecordStore,每 100ms 寫入 1KB 資料,直到拋出 RecordStoreFullException,該時刻的計數器值即為最大容量(KB)。退出時自動刪除測試用 RecordStore。

RMSModel:資料層

public class RMSModel {
    public static final int K = 1024;
    private RecordStore rs;
    private int baseCount; // 起始已寫入 KB 數
    private RMSAnalyzer analyzer;
    public static final String NAME = "capacity_test";

    public RMSModel(int baseCount, RMSAnalyzer analyzer) throws RecordStoreException {
        this.baseCount = baseCount;
        this.analyzer = analyzer;
        rs = RecordStore.openRecordStore(NAME, true);
        writeRecord(baseCount); // 初始寫入
    }

    public void writeRecord(int count) throws RecordStoreException {
        byte[] data = new byte[count * K];
        Arrays.fill(data, (byte) 1); // 填充非零資料
        rs.addRecord(data, 0, data.length);
    }

    public void deleteRMS() {
        try {
            rs.closeRecordStore();
            RecordStore.deleteRecordStore(NAME);
        } catch (RecordStoreException e) {
            analyzer.showAlertError(e.getMessage());
        }
    }
}

CounterCanvas:視覺與定時邏輯

public class CounterCanvas extends Canvas implements CommandListener {
    private RMSModel model;
    private RMSAnalyzer analyzer;
    private int interTime; // 間隔毫秒
    private int counter;   // 當前 KB 計數
    private boolean go = true;
    private Timer timer = new Timer();
    private TimerTask timerTask;
    public static final Command EXIT_CMD = new Command("退出", Command.EXIT, 3);
    public static final int INC = 1; // 每次遞增 1KB

    public CounterCanvas(int interTime, int base, RMSAnalyzer analyzer) throws RecordStoreException {
        this.interTime = interTime;
        this.counter = base;
        this.analyzer = analyzer;
        model = new RMSModel(base, analyzer);
        addCommand(EXIT_CMD);
        setCommandListener(this);

        timerTask = new TimerTask() {
            public void run() {
                try {
                    model.writeRecord(INC);
                    counter++;
                    repaint();
                } catch (RecordStoreFullException e) {
                    go = false;
                    model.deleteRMS();
                    timer.cancel();
                    repaint(); // 觸發繪製最終結果
                } catch (RecordStoreException e) {
                    model.deleteRMS();
                    analyzer.showAlertError(e.getMessage());
                    timer.cancel();
                }
            }
        };
    }

    public void start() {
        timer.schedule(timerTask, 500, interTime); // 500ms 延遲啟動
    }

    protected void paint(Graphics g) {
        int w = getWidth(), h = getHeight();
        int barX = w / 10, barY = h / 2, barW = w * 4 / 5, barH = 10;
        g.drawRect(barX, barY, barW, barH);
        if (isColor()) g.setColor(128, 128, 255);
        g.fillRect(barX + 1, barY + 1, Math.min(counter, barW - 2), barH - 2);

        if (!go) {
            g.drawString("最大值: " + counter + " KB", barX, h / 10, Graphics.TOP | Graphics.LEFT);
        }
    }

    public void commandAction(Command c, Displayable d) {
        if (c == EXIT_CMD) analyzer.exitMIDlet();
    }
}

RMSAnalyzer:MIDlet 主控

public class RMSAnalyzer extends MIDlet implements CommandListener {
    private Display display;
    private CounterCanvas canvas;
    private Alert errorAlert;
    public static final Command START_CMD = new Command("開始", Command.ITEM, 1);
    public static final Command EXIT_CMD = new Command("退出", Command.EXIT, 2);

    protected void startApp() {
        display = Display.getDisplay(this);
        errorAlert = new Alert("錯誤");
        try {
            int interval = Integer.parseInt(getAppProperty("INTER")); // 從 JAD 讀取間隔
            canvas = new CounterCanvas(interval, 0, this);
            display.setCurrent(canvas);
        } catch (RecordStoreException e) {
            showAlertError(e.getMessage());
        }
    }

    public void commandAction(Command c, Displayable d) {
        if (c == START_CMD) {
            display.setCurrent(canvas);
            canvas.start();
        } else if (c == EXIT_CMD) {
            exitMIDlet();
        }
    }

    public void showAlertError(String msg) {
        errorAlert.setString(msg);
        errorAlert.setType(AlertType.ERROR);
        errorAlert.setTimeout(2500);
        display.setCurrent(errorAlert);
    }
    // pauseApp, destroyApp, exitMIDlet 略...
}

JAD 設定檔關鍵屬性

MIDlet-Name: RMSAnalyzer
MIDlet-Version: 1.0.0
MIDlet-Vendor: J2MEdev.com
MIDlet-1: RMSAnalyzer,,RMSAnalyzer
MIDlet-Jar-URL: RMSAnalyzer.jar
MIDlet-Jar-Size: 15785
MicroEdition-Configuration: CLDC-1.0
MicroEdition-Profile: MIDP-1.0
MIDlet-Data-Size: 8192
INTER: 100  // 寫入間隔(ms),可依手機效能調整

實測意義與限制

  • 測得數值為單一 RecordStore 連續寫入的最大 KB 數,受限於檔案系統配額、堆疊大小、JVM 實作細節。
  • 不同手機同型號可能因韌體版本不同而有差異,建議多機型測試取中位數。
  • 測試過程會佔用大量堆疊記憶體,若手機 RAM 極小(<2MB)可能導致 MIDlet 被系統強制終止。

小結

此工具以不足 200 行程式碼,解決了官方 API 不可靠的痛點。開發者可據此評估 RMS 方案可行性,或作為安裝前檢查機制的一部分。

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