在智慧型手機尚未普及的年代,J2ME (Java 2 Platform, Micro Edition) 是行動裝置開發的主流平台。雖然 MIDP 2.0 提供了 HTTP 連線能力,但缺乏對 POP3、SMTP 等郵件協定的原生支援。本教學將帶你打造一個完整的行動郵件應用「MicroMail」,涵蓋 UI 設計、帳號管理、網路通訊、XML 解析,以及後端 Servlet 整合 JavaMail API 的完整架構。
受限於 MIDP 2.0 僅支援 HTTP 協定,以及電信業者常限制裝置直接存取外部埠號,我們採用三層架構:
這樣不只解決協定不支援的問題,也提升部署彈性——未來若要支援新協定,只需修改 Servlet 端,客戶端無需更新。
| 套件 | 主要類別 | 職責 |
|---|---|---|
| ui | MailMIDlet, AccountForm, AccountsList, MessageList, SendMessage, WriteContent, ProgressForm, Confirm, ConfirmListener | 所有畫面與互動邏輯 |
| utility | DBOperator, HeadParser, NetAdapter, Networker, ParserListener | RMS 資料庫、XML 解析、HTTP 通訊封裝 |
| MailAccount, MessageHead | 郵件帳號與郵件標題資料模型 |
單一 MailAgent.java Servlet,處理三種請求類型:RECEIVE_LIST (收信列表)、RECEIVE_MESSAGE (單封內容)、SEND_MESSAGE (寄信)。
MicroMail Beta.zip (含原始碼與編譯後類別檔)。$TMP。$WTK/apps 建立 MicroMail 目錄,複製 $TMP/src/client/* 至 $WTK/apps/MicroMail/src/。$TMP/bin/server/mail.war 部署至 $TOMCAT/webapps/。https://server/mail/MailAgent。主選單 MailMIDlet 繼承 List,採用 Choice.IMPLICIT 模式,提供「新增帳號」「編輯帳號」「收信」「寄信」四大功能入口。關鍵程式碼片段:
mainList = new List("MicroMail 0.1", Choice.IMPLICIT);
cmOK = new Command("確定", Command.OK, 1);
cmExit = new Command("離開", Command.EXIT, 1);
mainList.append("新增帳號", null);
mainList.append("編輯帳號", null);
mainList.append("收取郵件", null);
mainList.append("寄送郵件", null);
mainList.addCommand(cmOK);
mainList.addCommand(cmExit);
mainList.setCommandListener(this);
AccountForm 以六個 TextField 收集帳號名稱、E-mail 位址、用戶名、密碼 (採 PASSWORD 模式)、POP3/SMTP 伺服器位址,並支援新增與編輯兩種模式。
J2ME 無檔案系統,改用 javax.microedition.rms.RecordStore。DBOperator 封裝 addRecord/getRecord/deleteRecord/setRecord,並同步維護一個 Vector<MailAccount> 快取。新增帳號範例:
public void addRecord(String str) {
try {
byte[] rec = str.getBytes();
int id = rs.addRecord(rec, 0, rec.length);
MailAccount acc = MailAccount.createMailAccountFromString(id, str);
accounts.addElement(acc);
} catch (RecordStoreException e) { e.printStackTrace(); }
}
欄位以空白分隔,順序固定:account address user password pop3 smtp。
Networker 採非同步執行緒,避免阻塞 UI。以寄信 sendMessage 為例:
public void sendMessage(final String url, final String formData) {
new Thread() {
public void run() {
HttpConnection http = null;
try {
http = (HttpConnection) Connector.open(url);
http.setRequestMethod(HttpConnection.POST);
http.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
http.setRequestProperty("Content-Length", String.valueOf(formData.getBytes().length));
OutputStream os = http.openOutputStream();
os.write(formData.getBytes());
os.close();
// 處理回應...
} catch (IOException e) { /* 錯誤處理 */ }
finally { if (http != null) try { http.close(); } catch (IOException ignored) {} }
}
}.start();
}
請求參數採 application/x-www-form-urlencoded 格式,Servlet 端以 request.getParameter() 取得。
MailAgent.doPost() 依 type 參數分派:
Session → Store.connect(pop3, user, pass) → 開啟 INBOX → folder.getMessages() → 序列化為 XML 輸出。Message 物件,解析 getContent()、附件等。MimeMessage,設定收件者、主旨、內容,透過 Transport.send() 送出。關鍵片段 (收信列表):
Session session = Session.getDefaultInstance(props, null);
Store store = session.getStore("pop3");
store.connect(pop3Server, username, password);
Folder inbox = store.getFolder("INBOX");
inbox.open(Folder.READ_ONLY);
Message[] msgs = inbox.getMessages();
writeXML(msgs, out); // 產生 XML
inbox.close(false);
store.close();
為避免特殊字元干擾,Servlet 端產生 CDATA 區塊:
<?xml version="1.0"?>
<mail>
<message>
<from><![CDATA[寄件者 <a@b.com>]]></from>
<subject><![CDATA[主旨含特殊符號 & %]]></subject>
<date>2024-01-15 10:30:00</date>
<index>42</index>
</message>
...
</mail>
客戶端採用輕量級 kXML (可從 kxml.enhydra.org 取得) 解析,HeadParser 實作 ParserListener 介面,逐筆回呼 itemParsed(from, subject, date, index) 更新 UI 列表。
教學文件包含多張架構圖與畫面截圖,已保留原始圖片連結:
https://code.yunyiwangluo.com/bianchen/UploadFiles_4619/201701/2017011209311898.jpg本專案完整示範了 J2ME 開發的六大核心技能:
參考資源: