Record Management System (RMS) 是 J2ME 唯一的持久化儲存機制,類似輕量級記錄型資料庫,適用於:高分紀錄、遊戲存檔、用戶設定、離線資料隊列、快取資料。
// 第一層:單一記錄資料模型 (POJO + 序列化)
public class GameRecord {
private int level;
private int score;
private long timestamp;
private String playerName;
private boolean completed;
// 建構子、getter/setter 略...
// 序列化為位元組陣列 (DataOutputStream + UTF-8)
public byte[] toBytes() {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(baos);
dos.writeInt(level);
dos.writeInt(score);
dos.writeLong(timestamp);
dos.writeUTF(playerName); // 中文安全
dos.writeBoolean(completed);
dos.flush();
return baos.toByteArray();
} catch (IOException e) {
throw new RuntimeException("序列化失敗", e);
}
}
// 反序列化
public static GameRecord fromBytes(byte[] data) {
try {
ByteArrayInputStream bais = new ByteArrayInputStream(data);
DataInputStream dis = new DataInputStream(bais);
GameRecord r = new GameRecord();
r.level = dis.readInt();
r.score = dis.readInt();
r.timestamp = dis.readLong();
r.playerName = dis.readUTF();
r.completed = dis.readBoolean();
return r;
} catch (IOException e) {
throw new RuntimeException("反序列化失敗", e);
}
}
}
// 第二層:RecordStore 操作封裝 (DAO 模式)
import javax.microedition.rms.*;
public class RecordStoreManager {
private static final String STORE_NAME = "GameData";
// 開啟/建立存儲
private RecordStore open() throws RecordStoreException {
return RecordStore.openRecordStore(STORE_NAME, true);
}
// 新增記錄
public boolean addRecord(GameRecord record) {
try {
RecordStore rs = open();
rs.addRecord(record.toBytes(), 0, record.toBytes().length);
rs.closeRecordStore();
return true;
} catch (RecordStoreException e) {
return false;
}
}
// 取得所有記錄
public GameRecord[] getAllRecords() {
try {
RecordStore rs = open();
int count = rs.getNumRecords();
GameRecord[] result = new GameRecord[count];
RecordEnumeration re = rs.enumerateRecords(null, null, false);
for (int i = 0; i < count; i++) {
int id = re.nextRecordId();
result[i] = GameRecord.fromBytes(rs.getRecord(id));
}
rs.closeRecordStore();
return result;
} catch (RecordStoreException e) {
return new GameRecord[0];
}
}
// 依 ID 取得單筆
public GameRecord getRecord(int recordId) {
try {
RecordStore rs = open();
byte[] data = rs.getRecord(recordId);
rs.closeRecordStore();
return GameRecord.fromBytes(data);
} catch (RecordStoreException e) {
return null;
}
}
// 更新記錄
public boolean updateRecord(int recordId, GameRecord record) {
try {
RecordStore rs = open();
rs.setRecord(recordId, record.toBytes(), 0, record.toBytes().length);
rs.closeRecordStore();
return true;
} catch (RecordStoreException e) {
return false;
}
}
// 刪除記錄
public boolean deleteRecord(int recordId) {
try {
RecordStore rs = open();
rs.deleteRecord(recordId);
rs.closeRecordStore();
return true;
} catch (RecordStoreException e) {
return false;
}
}
// 記錄總數
public int getRecordCount() {
try {
RecordStore rs = open();
int c = rs.getNumRecords();
rs.closeRecordStore();
return c;
} catch (RecordStoreException e) {
return 0;
}
}
// 刪除整個存儲 (重置遊戲)
public static void deleteStore() {
try { RecordStore.deleteRecordStore(STORE_NAME); }
catch (RecordStoreException ignored) {}
}
}
// 篩選:分數 > 1000 的記錄
RecordFilter highScoreFilter = new RecordFilter() {
public boolean matches(byte[] candidate) {
try {
GameRecord r = GameRecord.fromBytes(candidate);
return r.getScore() > 1000;
} catch (Exception e) { return false; }
}
};
// 排序:分數降序
RecordComparator scoreDescComparator = new RecordComparator() {
public int compare(byte[] rec1, byte[] rec2) {
try {
int s1 = GameRecord.fromBytes(rec1).getScore();
int s2 = GameRecord.fromBytes(rec2).getScore();
return (s1 > s2) ? RecordComparator.PRECEDES :
(s1 < s2) ? RecordComparator.FOLLOWS : RecordComparator.EQUIVALENT;
} catch (Exception e) { return RecordComparator.EQUIVALENT; }
}
};
// 使用
RecordEnumeration re = rs.enumerateRecords(highScoreFilter, scoreDescComparator, false);
public class HighScoreManager {
private static final int MAX_ENTRIES = 10;
private RecordStoreManager rsm = new RecordStoreManager();
public void submitScore(String name, int score) {
GameRecord r = new GameRecord(1, score, System.currentTimeMillis(), name, true);
GameRecord[] all = rsm.getAllRecords();
// 插入排序位置
int pos = 0;
while (pos < all.length && all[pos].getScore() >= score) pos++;
if (pos < MAX_ENTRIES) {
// 手動維護陣列順序 (RMS 無自動排序)
// 這裡簡化:重新寫入前 N 筆
// 實際專案建議用記錄 ID 管理或單獨存儲排序索引
}
}
}
public class SaveGameManager {
private static final String SAVE_STORE = "SaveSlot";
private static final int SAVE_RECORD_ID = 1; // 固定 ID
public void save(GameState state) {
RecordStore rs = null;
try {
rs = RecordStore.openRecordStore(SAVE_STORE, true);
if (rs.getNumRecords() == 0) {
rs.addRecord(state.toBytes(), 0, state.toBytes().length);
} else {
rs.setRecord(SAVE_RECORD_ID, state.toBytes(), 0, state.toBytes().length);
}
} catch (RecordStoreException e) {
// 處理失敗
} finally {
if (rs != null) try { rs.closeRecordStore(); } catch (Exception ignored) {}
}
}
public GameState load() {
try {
RecordStore rs = RecordStore.openRecordStore(SAVE_STORE, false);
if (rs.getNumRecords() > 0) {
byte[] data = rs.getRecord(SAVE_RECORD_ID);
rs.closeRecordStore();
return GameState.fromBytes(data);
}
} catch (RecordStoreException ignored) {}
return null; // 無存檔
}
}
public class OfflineQueue {
private static final String QUEUE_STORE = "NetQueue";
public void enqueue(NetworkRequest req) {
RecordStore rs = null;
try {
rs = RecordStore.openRecordStore(QUEUE_STORE, true);
rs.addRecord(req.toBytes(), 0, req.toBytes().length);
} catch (RecordStoreException e) { /* 處理 */ }
finally { if (rs != null) try { rs.closeRecordStore(); } catch (Exception ignored) {} }
}
public NetworkRequest dequeue() {
try {
RecordStore rs = RecordStore.openRecordStore(QUEUE_STORE, false);
RecordEnumeration re = rs.enumerateRecords(null, null, false); // FIFO
if (re.numRecords() > 0) {
int id = re.nextRecordId();
byte[] data = rs.getRecord(id);
rs.deleteRecord(id);
rs.closeRecordStore();
return NetworkRequest.fromBytes(data);
}
} catch (RecordStoreException ignored) {}
return null;
}
}
| 指標 | 典型限制 | 應對 |
|---|---|---|
| 單一 RecordStore 大小 | 64KB - 512KB (視裝置) | 分檔存儲、壓縮資料、定期清理 |
| 單筆記錄大小 | 通常無硬性限制,但建議 < 4KB | 大資料分片、外部存儲 (JSR 75) |
| 開啟/關閉開銷 | 較大 (涉及檔案系統) | 批次操作時保持開啟、單例模式管理 |
| 執行緒安全 | RecordStore 方法同步,但枚舉器非線程安全 | 單執行緒訪問或自行同步 |
| 原子性 | 單一 add/set/delete 原子,多操作非事務 | 關鍵業務用臨時檔+重命名模擬事務 |
DataOutputStream.writeUTF/readUTF 處理中文 (見文章 77945)。toBytes()/fromBytes() 封裝序列化邏輯。RuntimeException 或布林返回,避免 checked exception 汙染業務碼。RecordStore.closeRecordStore() 確保落盤。RecordStore.getSize()/getSizeAvailable() 監控空間,預警清理。