作者「mant」實作一款帶密碼保護的手機記事本,核心功能:
noteSave.insertCode/readCode)代碼結構:myNote (MIDlet 主類) + noteSave (資料實體/序列化) + passwordSave (密碼管理)。
關鍵欄位:
private Display display;
private List list; // 記事標題列表
private int[] ids; // RMS 記錄 ID 陣列
private noteSave ns = new noteSave();
private passwordSave ps = new passwordSave();
private String password;
private TextField tfPassWord;
private int flag = 0; // 0=有密碼需驗證, 1=首次設定密碼
生命周期:
constructor:初始化 Display、讀取密碼、設定 flagstartApp():顯示密碼輸入 Form (fPassWord)run():驗證通過後建立主列表 list,載入所有記事
// startApp() 中
fPassWord = new Form("登錄");
tfPassWord = new TextField("", "", 20, TextField.ANY);
if (flag == 1) tfPassWord.setLabel("設置密碼");
else tfPassWord.setLabel("輸入密碼");
// Command "OK" 處理
if (flag == 1) { ps.setPassword(tfPassWord.getString()); run(); }
else if (password.equals(tfPassWord.getString())) { run(); }
else { tfPassWord.setLabel("密碼錯誤"); }
private void readAll() {
list.deleteAll();
RecordStore rs = RecordStore.openRecordStore("DB", true);
RecordEnumeration re = rs.enumerateRecords(null, null, false);
ids = new int[re.numRecords()];
int i = 0;
while (re.hasNextElement()) ids[i++] = re.nextRecordId();
re.reset(); i = 0;
while (re.hasNextElement()) {
byte[] data = re.nextRecord();
ns.readCode(data); // 解密/反序列化
list.append(ns.noteTitle, null);
i++;
}
rs.closeRecordStore();
}
關鍵點:
RecordEnumeration需reset()才能重複遍歷;ids陣列保持索引與 RMS 記錄 ID 對應,供後續修改/刪除使用。
巨大的 if-else 鏈處理所有 UI 事件:
save()read(id) → 顯示內容 TextBoxmodify() 更新 RMSdelete() 刪除記錄notifyDestroyed() 終結 MIDlet
public class noteSave {
String noteTitle, noteString;
public byte[] insertCode() { // 序列化 + 簡易加密
ByteArrayOutputStream bos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(bos);
dos.writeUTF(noteTitle);
dos.writeUTF(noteString);
// 此處可插入加密邏輯:byte[] encrypted = encrypt(bos.toByteArray());
return bos.toByteArray();
}
public void readCode(byte[] data) { // 解密 + 反序列化
ByteArrayInputStream bis = new ByteArrayInputStream(data);
DataInputStream dis = new DataInputStream(bis);
noteTitle = dis.readUTF();
noteString = dis.readUTF();
}
public static RecordStore openRSAnyway(String name) {
if (name.length() > 32) return null; // RMS 名稱長度限制
return RecordStore.openRecordStore(name, true);
}
}
| 現狀問題 | 建議改進 |
|---|---|
| commandAction 逾 300 行,邏輯混雜 | 拆分為獨立 Command 類、狀態模式、或 MVC Controller |
| 密碼明文儲存 (passwordSave 未見代碼) | 採用 PBKDF2/BCrypt 雜湊儲存,驗證時比對雜湊值 |
| noteSave 簡易序列化,無真正加密 | 引入 AES-128 (JSR 177 SATSA) 或輕量級 XOR/RC4 |
| RMS 操作分散在 MIDlet 中 | 建立 Repository 類封裝所有 RMS 存取 |
| UI 硬編碼字串,無國際化 | 使用 Resource Bundle (.properties) 分離文案 |
| 無異常具體處理,僅 printStackTrace | 分類處理:儲存滿、資料損毀、權限拒絕,給用戶友好提示 |
Window,加入三個類檔案myNote, /icon.png, Window.myNote這份代碼雖屬「練筆」,卻完整呈現了 J2ME 應用的核心骨架:MIDlet 生命周期、高層 UI (Form/List/TextBox/Alert)、低層 UI (Canvas)、RMS 持久化、事件驅動、資料序列化。對初學者極具參考價值;對資深開發者,則是重構練習的絕佳素材——在 30KB JAR 限制下,如何將 500 行單體拆分為清晰分層架構,是功力的試金石。