MIDP 標準庫缺失 java.util.ResourceBundle,導致字符串常量硬編碼在代碼中,修改困難、多語言支持無從談起。本文實現一個輕量級 ResourceBundle 類,從 JAR 內部屬性文件加載 key=value 格式資源,提供 getString(int index) 索引式訪問,並演示完整 MIDlet 測試用例。
文件名:index.properties (置於 res/ 目錄,打包進 JAR)
0=ming
1=Java
2=hello
3=world
4=digital
5=hahaha
\n、\r\n、\rpackage fancy.test;
import java.io.*;
import java.util.Vector;
public class ResourceBundle {
private Vector indexVector; // 存儲解析後的值
private String fileName; // 資源文件路徑 (如 "/index.properties")
private ResourceBundle() {} // 防止無參實例化
public ResourceBundle(String fileName, int size) throws IOException {
this.fileName = fileName;
this.indexVector = new Vector(size);
init();
}
private void init() throws IOException {
readToVector();
}
// 從 JAR 獲取資源輸入流
private InputStream getInputStreamFromFile() {
return new ResourceBundle().getClass().getResourceAsStream(fileName);
}
// 核心解析邏輯:逐字節讀取,按行分割,驗證索引連續性
private void readToVector() throws IOException {
InputStream is = getInputStreamFromFile();
if (is == null) throw new IOException("Resource not found: " + fileName);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int c = 0;
int index = 0;
while ((c = is.read()) != -1) {
if (c == '\n' || c == '\r') {
String line = baos.toString();
baos.reset();
if (line.length() == 0) continue; // 跳過空行
int eqPos = line.indexOf('=');
if (eqPos == -1) throw new IOException("Invalid format (missing '='): " + line);
String keyStr = line.substring(0, eqPos).trim();
String value = line.substring(eqPos + 1).trim();
// 驗證索引連續性
if (!keyStr.equals(String.valueOf(index))) {
throw new IOException("Index mismatch: expected " + index + ", got " + keyStr);
}
indexVector.addElement(value);
index++;
} else {
baos.write(c);
}
}
// 處理文件末尾無換行符的最後一行
if (baos.size() > 0) {
String line = baos.toString();
int eqPos = line.indexOf('=');
if (eqPos != -1) {
String keyStr = line.substring(0, eqPos).trim();
if (keyStr.equals(String.valueOf(index))) {
indexVector.addElement(line.substring(eqPos + 1).trim());
}
}
}
is.close();
}
public String getString(int indexID) {
if (indexID < 0 || indexID >= indexVector.size()) return null;
return (String) indexVector.elementAt(indexID);
}
}
package fancy.test;
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
import java.io.IOException;
public class IndexMIDlet extends MIDlet {
private ResourceBundle indexBundle;
private Display display;
private TextBox box;
protected void startApp() throws MIDletStateChangeException {
display = Display.getDisplay(this);
try {
indexBundle = new ResourceBundle("/index.properties", 15);
} catch (IOException e) {
e.printStackTrace();
}
box = new TextBox("ResourceBundle Demo", null, 256, TextField.ANY);
box.setString(indexBundle.getString(2)); // 顯示 "hello"
display.setCurrent(box);
}
protected void pauseApp() {}
protected void destroyApp(boolean unconditional) throws MIDletStateChangeException {}
}
| 設計決策 | 理由 |
|---|---|
| 索引連續性驗證 | 防止屬性文件缺行或順序錯誤導致運行時難以定位的邏輯錯誤 |
| Vector 預分配容量 | 減少動態擴容開銷,適應內存受限環境 |
| 逐字節讀取而非 BufferedReader | MIDP 無 BufferedReader,ByteArrayOutputStream 兼容性最佳 |
| UTF-8 編碼假設 | J2ME 內部字符串為 Unicode,讀取字節流構造 String 時默認平台編碼,建議屬性文件存為 UTF-8 並在讀取時指定編碼 (如有 DataInputStream.readUTF 支持) |
getString(String key) 支持字符串鍵值,兼容標準 Java Properties 用法index_zh.properties、index_en.properties)通過 ~100 行代碼實現的 ResourceBundle,解決了 MIDP 缺失標準資源綁定機制的痛點。開發者可將所有 UI 文本、錯誤提示、配置參數外部化到屬性文件,實現「不改代碼、修改文本、支持多語言」的工程化目標。