功能機時代,文件系統(JSR 75)支援極有限,Record Management System (RMS) 成為 MIDP 規範強制實現的本地存儲標準。本文系統梳理 RMS 核心概念、API 細節、跨版本差異、裝置差異陷阱,並附贈實機存儲上限自動測試工具完整源碼。
byte[],無結構定義。DataOutputStream/ByteArrayOutputStream 寫入基本類型、字串、陣列。compact)。addRecord/setRecord/deleteRecord 保證不損壞。AUTHMODE_PRIVATE/PUBLIC + WRITEABLE/READABLE,支援跨 Suite 共享。MIDlet-Data-Size: 8192(單位 Byte),安裝時裝置檢查可用空間是否滿足。RecordStoreFullException。建議:實測目標機型最大可用值 × 0.8 作為宣告值。| 方法 | 說明 | 備註 |
|---|---|---|
openRecordStore(name, createIfNecessary) | 開啟/建立 | 同名存儲全 Suite 可見 |
closeRecordStore() | 關閉釋放鎖 | 必須成對呼叫 |
deleteRecordStore(name) | 刪除整個存儲 | 靜態方法 |
addRecord(data, offset, len) | 新增記錄 | 回傳記錄 ID (1-based) |
setRecord(id, data, offset, len) | 覆寫記錄 | ID 必須存在 |
getRecord(id, buffer, offset) | 讀取到指定緩衝區 | 避免建立新 byte[] |
getRecord(id) | 讀取並回傳新 byte[] | 簡單但頻繁呼叫產生 GC |
deleteRecord(id) | 刪除單筆 | ID 不會重用 |
getNumRecords() | 當前記錄數 | |
getSize() | 已用位元組數 | 含索引開銷 |
getSizeAvailable() | 剩餘可用位元組 | 不可靠! 見下文實測 |
enumerateRecords(filter, comparator, keepUpdated) | 枚舉/篩選/排序 | 進階查詢利器 |
addRecordListener(listener) | 變更通知 | 多 MIDlet 同步用 |
getSizeAvailable() 不可信?作者在 Nokia 6108 實測:
getSizeAvailable()回傳 >1MB,但寫入 40KB 即拋RecordStoreFullException。
原因:
結論: 僅作參考,實際上限必須實機壓力測試。
原理:背景執行緒每 INTER 毫秒寫入 1KB 記錄,直到拋 RecordStoreFullException,記錄成功筆數即為上限(KB)。
// MIDlet 入口
public class RMSAnalyzer extends MIDlet {
private Display display; private CounterCanvas canvas; private Alert alert;
protected void startApp() {
display = Display.getDisplay(this);
alert = new Alert("錯誤");
try {
int t = Integer.parseInt(getAppProperty("INTER")); // JAD 設定間隔
canvas = new CounterCanvas(t, 1, this);
display.setCurrent(canvas);
} catch (RecordStoreException e) { showAlertError(e.getMessage()); }
}
public void showAlertError(String msg) {
alert.setString(msg); alert.setType(AlertType.ERROR); alert.setTimeout(3000);
display.setCurrent(alert);
}
protected void pauseApp() {} protected void destroyApp(boolean u) {}
}
// 畫面與測試邏輯
public class CounterCanvas extends Canvas implements CommandListener {
private RMSModel model; private RMSAnalyzer midlet;
private int interTime, counter; private boolean go = true;
private Timer timer = new Timer();
public static Command backCommand = new Command("退出", Command.EXIT, 3);
public CounterCanvas(int t, int base, RMSAnalyzer m) throws RecordStoreException {
interTime = t; counter = base; midlet = m; model = new RMSModel(base, m);
addCommand(backCommand); setCommandListener(this);
timer.schedule(new TimerTask() {
public void run() {
try {
model.writeRecord(1); // 寫入 1KB
counter++; repaint();
} catch (RecordStoreFullException e) {
go = false; model.deleteRMS(); timer.cancel(); repaint();
} catch (RecordStoreException e) {
model.deleteRMS(); timer.cancel(); midlet.showAlertError(e.getMessage());
}
}
}, 1000, interTime);
}
protected void paint(Graphics g) {
int w = getWidth(), h = getHeight();
g.drawRect(w/10, h/2, w*4/5, 10);
if (isColor()) g.setColor(0x8080FF);
g.fillRect(w/10, h/2, counter, 10); // 進度條
if (!go) g.drawString("最大值:" + counter + "K字節", 0, 0, Graphics.TOP|Graphics.LEFT);
}
public void commandAction(Command c, Displayable d) {
if (c == backCommand) {
try { model.deleteRMS(); midlet.destroyApp(false); midlet.notifyDestroyed(); }
catch (Exception e) {}
}
}
}
// 資料模型:封裝 RecordStore 讀寫
public class RMSModel {
public static final int K = 1024;
private RecordStore rs; private int baseCount; private RMSAnalyzer midlet;
public static final String NAME = "test";
public RMSModel(int base, RMSAnalyzer m) throws RecordStoreException {
baseCount = base; midlet = m;
rs = RecordStore.openRecordStore(NAME, true);
writeRecord(baseCount); // 初始寫入
}
public void writeRecord(int count) throws RecordStoreException {
byte[] data = new byte[count * K];
for (int i=0; i
MIDlet-Data-Size: 8192
INTER: 100 // 每 100ms 寫入 1KB,約 10KB/s,平衡速度與系統負載
getSizeAvailable() 回傳的 1MB+)| 特性 | MIDP 1.0 | MIDP 2.0 |
|---|---|---|
| 同 Suite 共享 | 支援 | 支援 |
| 跨 Suite 共享 | 不支援 | 支援(授權模式 + 讀寫控制) |
| 建立參數 | openRecordStore(name, true) | openRecordStore(name, true, authMode, writable) |
| 授權模式 | — | AUTHMODE_PRIVATE(僅同 Suite)/ AUTHMODE_PUBLIC(任意 Suite) |
| 讀寫控制 | — | true=可寫 / false=唯讀 |