在 Nokia 6108 開發個人通訊錄時,發現 RecordStore.getSizeAvailable() 回傳值幾乎等於手機剩餘總記憶體,顯然該 API 實作有誤,無法反映單一 RecordStore 的真實寫入上限。為此開發一款輕量級 RMS 容量偵測器,以實測方式探測單一 RecordStore 最大可寫入資料量(單位:KB)。
⚠ 重要提醒:請勿在模擬器執行,模擬器儲存機制與實機截然不同,測試結果無參考價值。
原理極簡:建立一個 RecordStore,每 100ms 寫入 1KB 資料,直到拋出 RecordStoreFullException,該時刻的計數器值即為最大容量(KB)。退出時自動刪除測試用 RecordStore。
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());
}
}
}
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();
}
}
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 略...
}
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),可依手機效能調整
此工具以不足 200 行程式碼,解決了官方 API 不可靠的痛點。開發者可據此評估 RMS 方案可行性,或作為安裝前檢查機制的一部分。