首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> J2ME

J2ME 調用 .NET Web Services 實戰:JSR 172 Stub 生成、異步調用與 kSOAP 替代方案對比

     編輯:J2ME

技術選型:JSR 172 vs kSOAP

維度JSR 172 (標準)kSOAP (第三方)
標準化JCP 標準規範開源庫,非標準
工具支持WTK 內建 Stub Generator手動編寫 SOAP 請求/解析響應
.NET 兼容性優秀 (WSDL 標準互操作)需手動處理命名空間、數組、DataSet 等 .NET 特有類型
代碼體積生成 Stub 較大核心庫較小 (~30KB)
維護性WSDL 變更重新生成即可手動維護映射代碼

結論:優先使用 **JSR 172**,僅在設備不支持 JSR 172 (極舊機型) 或 JAR 體積極其敏感時考慮 kSOAP。

開發環境

  • J2ME WTK 2.2 (含 Stub Generator)
  • Java Build 9 (JB9) IDE
  • .NET Framework 2003 (ASMX Web Service)
  • 依賴庫:kXML-min.zip、ksoap-midp.zip (Stub 運行時依賴)

步驟 1:編寫並部署 .NET Web Service (ASMX)


[WebService(Namespace = "https://tempuri.org/")]
public class MyServices : WebService {
    [WebMethod(Description = "User Login")]
    public bool Login(string sLoginUserID, string sLoginPwd) {
        if (string.IsNullOrEmpty(sLoginUserID)) return false;
        
        string connStr = "Server=.;Database=TestDB;Uid=sa;Pwd=123;";
        using (SqlConnection conn = new SqlConnection(connStr)) {
            string sql = "SELECT Pwd FROM Users WHERE UserID = @UserID";
            SqlCommand cmd = new SqlCommand(sql, conn);
            cmd.Parameters.AddWithValue("@UserID", sLoginUserID);
            conn.Open();
            object result = cmd.ExecuteScalar();
            if (result == null) return false;
            return result.ToString() == sLoginPwd;
        }
    }
}

部署至 IIS,驗證 https://server/MyServices.asmx?wsdl 可訪問。

步驟 2:WTK Stub Generator 生成客戶端代碼

  1. 啟動 WTK → Utilities → Stub Generator
  2. WSDL URL:https://192.168.10.101/Service/MyServices.asmx?wsdl (必須帶 ?wsdl)
  3. Output Path:項目 src 目錄
  4. Output Package:helloworld.ws (對應 src/helloworld/ws/)
  5. CLDC Version:1.1 (支持浮點)
  6. 點擊 OK,生成 MyServices_Stub.java、MyServices_Soap_Stub.java 等文件
  7. 刷新 IDE 項目,編譯修復可能的包路徑問題

步驟 3:MIDlet 中調用 Web Service


package helloworld;

import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
import helloworld.ws.*;  // 生成的 Stub 包

public class LoginMIDlet extends MIDlet implements CommandListener {
    private Display display;
    private Form loginForm;
    private TextField txtUser, txtPwd;
    private Command cmdLogin, cmdExit;
    private MyServices_Soap_Stub service;  // 核心:生成的 SOAP Stub

    protected void startApp() throws MIDletStateChangeException {
        display = Display.getDisplay(this);
        loginForm = new Form("Login");
        txtUser = new TextField("UserID:", "", 20, TextField.ANY);
        txtPwd = new TextField("Password:", "", 20, TextField.PASSWORD);
        cmdLogin = new Command("Login", Command.OK, 1);
        cmdExit = new Command("Exit", Command.EXIT, 1);
        loginForm.append(txtUser);
        loginForm.append(txtPwd);
        loginForm.addCommand(cmdLogin);
        loginForm.addCommand(cmdExit);
        loginForm.setCommandListener(this);
        display.setCurrent(loginForm);
        
        // 初始化 Service Stub
        service = new MyServices_Soap_Stub();
        service._setProperty(MyServices_Soap_Stub.SESSION_MAINTAIN_PROPERTY, Boolean.TRUE);
    }

    public void commandAction(Command c, Displayable d) {
        if (c == cmdLogin) {
            doLogin();
        } else if (c == cmdExit) {
            destroyApp(false);
            notifyDestroyed();
        }
    }

    private void doLogin() {
        final String user = txtUser.getString();
        final String pwd = txtPwd.getString();
        if (user.length() == 0 || pwd.length() == 0) {
            showAlert("Error", "Please input user/pwd");
            return;
        }

        // 必須在新線程中調用,避免阻塞 UI 線程導致系統假死
        new Thread() {
            public void run() {
                try {
                    boolean result = service.login(user, pwd);  // 同步調用
                    if (result) {
                        // 登入成功,切換到主界面 (需在 UI 線程操作)
                        display.callSerially(new Runnable() {
                            public void run() {
                                display.setCurrent(mainForm); // 假設有主界面
                            }
                        });
                    } else {
                        showAlert("Login Failed", "Invalid user or password");
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    showAlert("Connection Error", "Check network/proxy settings");
                }
            }
        }.start();
    }

    private void showAlert(final String title, final String msg) {
        display.callSerially(new Runnable() {
            public void run() {
                Alert a = new Alert(title, msg, null, AlertType.ERROR);
                a.setTimeout(Alert.FOREVER);
                display.setCurrent(a, loginForm);
            }
        });
    }

    protected void pauseApp() {}
    protected void destroyApp(boolean unconditional) throws MIDletStateChangeException {}
}

關鍵技術點解析

常見問題排查

  • Stub 編譯報錯「package kxml not found」:將 kXML-min.zip 解壓後的 org/kxml2/ 複製到項目 src 或構建路徑
  • 調用拋出「Namespace mismatch」:.NET 默認命名空間 https://tempuri.org/,確保 Stub 生成時 WSDL 正確、未手動修改包名
  • 參數為複雜類型 (DataSet、ArrayList):JSR 172 支持有限,建議服務端改為簡單類型 (String、int、String[]) 或自定義簡單 Bean
  • HTTPS 調用失敗:設備需信任服務器證書 CA,或導入客戶端證書 (JSR 177 SATSA 支持)

kSOAP 替代方案速覽 (僅參考)


// kSOAP 手動構造 SOAP 請求
SoapObject request = new SoapObject(NAMESPACE, "Login");
request.addProperty("sLoginUserID", user);
request.addProperty("sLoginPwd", pwd);
SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);
envelope.dotNet = true;  // 關鍵:標識調用 .NET 服務
envelope.setOutputSoapObject(request);
HttpTransport transport = new HttpTransport(URL);
transport.call(SOAP_ACTION, envelope);
SoapObject result = (SoapObject) envelope.getResponse();
boolean loginResult = Boolean.parseBoolean(result.getProperty(0).toString());

總結

JSR 172 為 J2ME 調用 .NET Web Services 提供了**標準化、工具化、可維護**的路徑:WSDL → Stub Generator → 類安全調用 → 異步線程 + UI 線程回調。掌握此流程,即可快速構建企業級移動客戶端,對接現有 .NET 服務體系。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
技術點說明
_setProperty(SESSION_MAINTAIN_PROPERTY, Boolean.TRUE)啟用 HTTP Session 保持,支持 .NET 服務端 Session 狀態
新線程調用 service.login()網絡調用阻塞,必須離開 UI 線程,否則屏幕凍結、甚至系統看門狗重啟
display.callSerially(Runnable)網絡線程完成後,安全地在 UI 線程更新界面 (MIDP 2.0 標準)
異常處理捕獲 RemoteException、IOException、XMLParserException 等,友好提示用戶