RMS (Record Management System) 是 J2ME 唯一的持久化儲存機制,對遊戲存檔、高分榜、設定保存至關重要。本文提供生產級的兩層封裝架構:記錄結構類 (資料模型) + RMS 管理類 (存取邏輯),含完整異常處理與索引查詢功能。
Appointment (記錄結構) ←→ byte[] ←→ RecordStore
↑ ↑
toBytes()/init() RMS 管理類
Appointment
package com.cuilichen.usual;
import java.io.*;
public class Appointment {
// 業務欄位
private int int1, int2;
private long long1;
private String str1, str2, str3; // str1 作為檢索鍵值
private boolean wroteFlag;
// 構造函數:新建記錄
public Appointment(int _int1, int _int2, long _long1,
String _str1, String _str2, String _str3, boolean _flag) {
this.int1 = _int1; this.int2 = _int2; this.long1 = _long1;
this.str1 = _str1; this.str2 = _str2; this.str3 = _str3;
this.wroteFlag = _flag;
}
// 構造函數:從 RMS 位元組陣列還原
public Appointment(byte[] rec) { initFromBytes(rec); }
// 序列化為位元組陣列 (寫入 RMS)
public byte[] toBytes() {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(baos);
dos.writeInt(int1);
dos.writeInt(int2);
dos.writeLong(long1);
dos.writeUTF(str1);
dos.writeUTF(str2);
dos.writeUTF(str3);
dos.writeBoolean(wroteFlag);
dos.flush();
byte[] data = baos.toByteArray();
dos.close(); baos.close();
return data;
} catch (Exception e) { e.printStackTrace(); return null; }
}
// 反序列化 (讀取 RMS)
private void initFromBytes(byte[] rec) {
try {
ByteArrayInputStream bais = new ByteArrayInputStream(rec);
DataInputStream dis = new DataInputStream(bais);
int1 = dis.readInt();
int2 = dis.readInt();
long1 = dis.readLong();
str1 = dis.readUTF();
str2 = dis.readUTF();
str3 = dis.readUTF();
wroteFlag = dis.readBoolean();
dis.close(); bais.close();
} catch (Exception e) { e.printStackTrace(); }
}
// Getter 方法...
public int getInt1() { return int1; }
public String getStr1() { return str1; } // 檢索關鍵
// ... 其他 getter 略
}
RMS
package com.cuilichen.usual;
import javax.microedition.rms.*;
public class RMS {
// 預設值常數
public static final int DEF_INT = 0;
public static final long DEF_LONG = 0L;
public static final String DEF_STR = "";
// 新增記錄
public static boolean addRecord(String storeName, int int1, int int2, long long1,
String str1, String str2, String str3, boolean flag) {
try {
RecordStore rs = RecordStore.openRecordStore(storeName, true);
Appointment appt = new Appointment(int1, int2, long1, str1, str2, str3, flag);
rs.addRecord(appt.toBytes(), 0, appt.toBytes().length);
rs.closeRecordStore();
return true;
} catch (Exception e) { e.printStackTrace(); return false; }
}
// 取得記錄總數
public static int getRecordCount(String storeName) {
try {
RecordStore rs = RecordStore.openRecordStore(storeName, true);
int count = rs.getNumRecords();
rs.closeRecordStore();
return count;
} catch (Exception e) { return 0; }
}
// 取得所有記錄 (陣列)
public static Appointment[] getAllRecords(String storeName) {
try {
RecordStore rs = RecordStore.openRecordStore(storeName, false);
RecordEnumeration re = rs.enumerateRecords(null, null, false);
Appointment[] result = new Appointment[rs.getNumRecords()];
for (int i = 0; i < result.length; i++) {
int recId = re.nextRecordId();
result[i] = new Appointment(rs.getRecord(recId));
}
rs.closeRecordStore();
return result;
} catch (Exception e) { return new Appointment[0]; }
}
// 依記錄 ID 取得單筆
public static Appointment getRecordById(String storeName, int recordId) {
try {
RecordStore rs = RecordStore.openRecordStore(storeName, false);
Appointment appt = new Appointment(rs.getRecord(recordId));
rs.closeRecordStore();
return appt;
} catch (Exception e) { return new Appointment(); }
}
// 依檢索鍵 (str1) 查詢記錄 ID
public static int findRecordIdByKey(String storeName, String key) {
try {
RecordStore rs = RecordStore.openRecordStore(storeName, false);
RecordEnumeration re = rs.enumerateRecords(null, null, false);
int count = getRecordCount(storeName);
for (int i = 0; i < count; i++) {
int recId = re.nextRecordId();
Appointment appt = new Appointment(rs.getRecord(recId));
if (appt.getStr1().equals(key)) {
rs.closeRecordStore();
return recId;
}
}
rs.closeRecordStore();
} catch (Exception e) {}
return -1; // 未找到
}
// 更新指定 ID 記錄
public static boolean updateRecord(String storeName, int recordId,
int int1, int int2, long long1,
String str1, String str2, String str3, boolean flag) {
try {
RecordStore rs = RecordStore.openRecordStore(storeName, false);
Appointment appt = new Appointment(int1, int2, long1, str1, str2, str3, flag);
rs.setRecord(recordId, appt.toBytes(), 0, appt.toBytes().length);
rs.closeRecordStore();
return true;
} catch (Exception e) { return false; }
}
// 刪除記錄 (可擴充)
public static boolean deleteRecord(String storeName, int recordId) {
try {
RecordStore rs = RecordStore.openRecordStore(storeName, false);
rs.deleteRecord(recordId);
rs.closeRecordStore();
return true;
} catch (Exception e) { return false; }
}
}
// MIDlet 啟動時初始化測試資料
private static final String STORE_NAME = "GameData";
protected void startApp() {
if (RMS.getRecordCount(STORE_NAME) == 0) {
for (int i = 0; i < 6; i++) {
// 只使用 int2、str1(鍵)、str3 三個欄位
RMS.addRecord(STORE_NAME,
RMS.DEF_INT, i, RMS.DEF_LONG,
String.valueOf(i), // str1 = "0","1","2"...
RMS.DEF_STR,
"1234567890123456789",
false);
}
}
// 後續查詢範例
int id = RMS.findRecordIdByKey(STORE_NAME, "3");
if (id != -1) {
Appointment appt = RMS.getRecordById(STORE_NAME, id);
// appt.getStr3() → "1234567890123456789"
}
}
範例為簡潔在內部捕獲並列印,生產環境建議:
RMSException 封裝底層例外向上拋出RecordStoreFullException (空間不足)、RecordStoreNotFoundException (存儲不存在) 等具體錯誤Hashtable,查詢 O(1)記錄結構變更時:
Appointment 加入 version 欄位initFromBytes 依版本號兼容舊格式讀取openRecordStore(name, true, RecordStore.AUTHMODE_ANY, true))這套「記錄類 + 管理類」雙層架構,將資料模型與存取邏輯分離,符合單一職責原則,且易於單元測試與維護。掌握 DataOutputStream 序列化、RecordEnumeration 遍歷、索引查詢模式,即能驾輕就熟處理 J2ME 所有持久化需求。