JSR 172 為 J2ME 提供 **JAX-RPC 子集** (Stub-based 調用) + **SAX/StAX 解析器**,支援 SOAP 1.1 over HTTP/HTTPS。需 MIDP 2.0 + CLDC 1.1 + 設備廠商實現 (WTK 2.2+ 內建,S60 3rd/FP1+ 真機支援)。
| 方式 | 優點 | 缺點 | 適用場景 |
|---|---|---|---|
| JSR 172 Stub | 強類型、IDE 代碼生成、自動序列化 | JAR 體積大 (Stub+序列化器 ~50KB)、需 WSDL 預處理 | 企業級應用、參數複雜、長期維護 |
| 手工 HTTP + KXML/NanoXML | 輕量、完全可控、無額外依賴 | 手寫 SOAP 封包/解析易出錯、維護成本高 | 簡單 RPC、極限 JAR 體積、無 JSR 172 設備 |
| REST + JSON (手工) | 輕量、人類可讀、現代 API 風格 | 需自行 JSON 解析 (org.json.me / kJSON) | 新項目首選、後端可控 |
https://example.com/ws/auth?wsdl
wscompile -gen:client -d generated -classpath %WTK_HOME%\lib\j2mews.jar -f:wsi -features:documentliteral config.xml
config.xml 指定 WSDL 位置、包名、服務名。生成:AuthService_Stub、AuthServiceIF、序列化器類。
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
import javax.xml.rpc.*;
import java.rmi.*;
import generated.*;
public class WSLoginMidlet extends MIDlet implements CommandListener {
private Display display;
private Form form;
private TextField tfUser, tfPass;
private Command cmdLogin, cmdExit;
private AuthServiceIF service;
public WSLoginMidlet() {
display = Display.getDisplay(this);
form = new Form("WebService 登錄");
tfUser = new TextField("用戶名", "", 20, TextField.ANY);
tfPass = new TextField("密碼", "", 20, TextField.PASSWORD);
cmdLogin = new Command("登錄", Command.OK, 1);
cmdExit = new Command("退出", Command.EXIT, 1);
form.append(tfUser); form.append(tfPass);
form.addCommand(cmdLogin); form.addCommand(cmdExit);
form.setCommandListener(this);
// 初始化 Stub (耗時操作,放線程)
new Thread(() -> {
try {
AuthService_Stub stub = new AuthService_Stub();
stub._setProperty(Stub.ENDPOINT_ADDRESS_PROPERTY,
"https://example.com/ws/auth");
service = stub;
} catch (Exception e) { e.printStackTrace(); }
}).start();
}
protected void startApp() { display.setCurrent(form); }
protected void pauseApp() {}
protected void destroyApp(boolean u) {}
public void commandAction(Command c, Displayable d) {
if (c == cmdExit) { notifyDestroyed(); return; }
if (c == cmdLogin) { new Thread(this::doLogin).start(); }
}
private void doLogin() {
if (service == null) { showAlert("服務未就緒"); return; }
try {
String token = service.login(tfUser.getString(), tfPass.getString());
// token 非空即成功
showAlert("登錄成功", "Token: " + token);
} catch (RemoteException e) {
showAlert("網絡錯誤", e.getMessage());
} catch (Exception e) {
showAlert("異常", e.toString());
}
}
private void showAlert(String title, String msg) {
Alert a = new Alert(title, msg, null, AlertType.INFO);
a.setTimeout(Alert.FOREVER);
display.setCurrent(a, form);
}
private void showAlert(String msg) { showAlert("提示", msg); }
}
generated/ 所有類複製進專案 src/。-keep class generated.** { *; }。| 現象 | 原因 | 解決 |
|---|---|---|
| Stub 初始化極慢 (5~10 秒) | 類載入 + 反射註冊序列化器 | 啟動畫面顯示「正在連接服務...」,異步初始化 |
| 真機報 "UnsupportedContentType" | 伺服器返回非 text/xml (如 HTML 錯誤頁) | 檢查 Endpoint URL、伺服器日誌、防火牆/攔截器 |
| JAR 體積超限 (> 64 KB/128 KB) | Stub + 序列化器 + XML 解析器 | 改用 REST+JSON (kJSON ~15KB) 或精簡 WSDL 僅保留必要操作 |
| SSL 握手失敗 | 設備無根證書/不支援 TLS 1.2 | HTTP 內網測試;生產需設備支援 JSSE (JSR 177 SATSA) |
JSR 172 的 **Stub 生成、序列化映射、異步調用** 模式直接映射到現代:
核心思想不變:**契約優先、代碼生成、類型安全、網絡細節封裝**。