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

J2ME 藍牙開發實戰入門:基於 JSR-82 實現設備發現、服務註冊與雙向通訊

     編輯:Java 程式設計入門

J2ME (Java ME) 時代,藍牙是移動設備近場通訊的主流技術。JSR-82 (Java APIs for Bluetooth Wireless Technology, JABWT) 定義了標準藍牙 API,支援設備發現、服務搜索、RFCOMM/L2CAP 連接、SDP 記錄管理。本文提供一個**最小化完整示例**:一台設備作為 Server 註冊「大寫轉換服務」,另一台作為 Client 發現服務並發送字符串,Server 返回大寫結果。代碼已在 WTK 2.2 模擬器、Nokia 6600/6670/7610 實機驗證通過。

一、核心架構與類職責

類角色關鍵 API
StupidBTMIDlet主 MIDlet,菜單選擇 Client/Server 模式Display, List, CommandListener
ServerBoxServer 端 GUI + 藍牙服務邏輯LocalDevice, StreamConnectionNotifier, ServiceRecord, DiscoveryAgent
ClientBoxClient 端 GUI + 藍牙客戶端邏輯DiscoveryAgent, DiscoveryListener, StreamConnection, ServiceRecord
ClientProcessorServer 端處理每個 Client 連接的線程DataInputStream, DataOutputStream, Runnable

二、關鍵藍牙常量

// 服務 UUID (128-bit 自定義,必須雙方一致)
private static final UUID ECHO_SERVER_UUID = new UUID(
    "F0E0D0C0B0A000908070605040302010", false);

// SDP 服務類別 ID (Serial Port Profile 標準)
private static final UUID[] UUID_SET = {
    new UUID(0x1101),          // SPP
    ECHO_SERVER_URL            // 自定義服務
};

三、Server 端核心流程 (ServerBox.run())

  1. 初始化本地設備並設為可發現:
    LocalDevice localDevice = LocalDevice.getLocalDevice();
    localDevice.setDiscoverable(DiscoveryAgent.GIAC); // General/Unlimited Inquiry Access Code
  2. 構建服務 URL 並打開連接通知器:
    StringBuffer url = new StringBuffer("btspp://localhost:");
    url.append(ECHO_SERVER_UUID.toString());
    url.append(";name=Echo Server");
    url.append(";authorize=false"); // 部分需 false 否則連接失敗
    StreamConnectionNotifier notifier = (StreamConnectionNotifier) Connector.open(url.toString());
  3. 獲取服務記錄並啟動監聽線程:
    ServiceRecord record = localDevice.getRecord(notifier);
    new Thread(this).start(); // run() 中 acceptAndOpen() 循環
  4. 接受連接並派發處理線程:
    while (!isClosed) {
        StreamConnection conn = notifier.acceptAndOpen(); // 阻塞等待
        new ClientProcessor(conn, this).start();        // 每連接一線程
    }

四、Client 端核心流程 (ClientBox.run() 實現 DiscoveryListener)

  1. 初始化並啟動設備搜索:
    LocalDevice local = LocalDevice.getLocalDevice();
    DiscoveryAgent agent = local.getDiscoveryAgent();
    agent.startInquiry(DiscoveryAgent.GIAC, this); // 異步,回調 deviceDiscovered()
    wait(); // 等待 inquiryCompleted() 喚醒
  2. 對每個發現設備搜索服務:
    for (RemoteDevice rd : devices) {
        int transID = agent.searchServices(null, UUID_SET, rd, this);
        transIDs.add(transID); // 記錄事務 ID
    }
    wait(); // 等待所有 serviceSearchCompleted() 喚醒
  3. 回調收集服務記錄:
    public void servicesDiscovered(int transID, ServiceRecord[] records) {
        for (ServiceRecord r : records) this.records.addElement(r);
    }
  4. 用戶輸入字符串 → 建立連接 → 發送 → 接收大寫結果:
    String url = sr.getConnectionURL(ServiceRecord.NOAUTHENTICATE_NOENCRYPT, false);
    StreamConnection conn = (StreamConnection) Connector.open(url);
    DataOutputStream dos = conn.openDataOutputStream();
    dos.writeUTF(inputString);
    dos.close();
    DataInputStream dis = conn.openDataInputStream();
    String echo = dis.readUTF(); // 顯示 "反饋結果: HELLO WORLD"
    dis.close();

五、ClientProcessor 線程處理邏輯

public class ClientProcessor extends Thread {
    private StreamConnection conn;
    private ServerBox serverBox;

    public void run() {
        try {
            DataInputStream dis = conn.openDataInputStream();
            DataOutputStream dos = conn.openDataOutputStream();
            String input = dis.readUTF();
            String upper = input.toUpperCase(); // 業務邏輯:轉大寫
            dos.writeUTF(upper);
            dis.close(); dos.close(); conn.close();
            serverBox.showInfo("處理: " + input + " -> " + upper);
        } catch (IOException e) { serverBox.showInfo("連接異常: " + e); }
    }
}

六、界面交互設計

  • ServerBox 繼承 TextBox:顯示日誌,命令「開啟服務/終止服務/返回」。
  • ClientBox 繼承 Form:包含 Gauge (搜索進度)、StringItem (日誌)、TextField (輸入框)、命令「發送/取消」。
  • 主 MIDlet 用 List(IMPLICIT):單擊選擇 Client/Server 模式啟動對應界面。

七、常見坑點與調試技巧

問題原因解決
真機無法發現設備藍牙未開啟/不可見模式/權限未授予設置 setDiscoverable(GIAC);簽名 MIDlet 获取信任權限
Connector.open 拋出 ConnectionNotFoundExceptionURL 格式錯誤/UUID 不匹配/協議棧不支持 btspp檢查 URL 拼接;確認雙方 UUID 完全一致;模擬器需啟用藍牙模擬
服務搜索無結果SDPDB 未註冊/搜索屬性集為 null 導致過濾searchServices 屬性集傳 null 即可;確保 Server 已 getRecord() 註冊
readUTF 拋出 EOFException對端已關閉流/協議不匹配(非 DataOutputStream 寫入)雙方統一使用 writeUTF/readUTF;finally 關閉資源
模擬器雙模通訊失敗WTK 默認單模擬器實例啟動兩個獨立模擬器進程(不同端口/存儲根目錄)

八、擴展方向

  1. L2CAP 連接:替換 btspp:// 為 btl2cap://,適合數據包傳輸、低延遲遊戲。
  2. OBEX 對象交換:使用 btgoep:// 實現文件/名片/日曆傳輸 (JSR-82 擴展)。
  3. 安全模式:authorize=true, encrypt=true, authenticate=true,配對加密通訊。
  4. 多設備並發:Server 端線程池管理 ClientProcessor,避免線程爆炸。
  5. 遷移至 Android:Android android.bluetooth API 設計靈感源自 JSR-82,核心概念 (UUID, SDP, RFCOMM) 完全對應,代碼可較易移植。

九、完整項目結構

src/
├── com/example/bt/
│   ├── StupidBTMIDlet.java
│   ├── ServerBox.java
│   ├── ClientBox.java
│   └── ClientProcessor.java
├── META-INF/
│   └── MANIFEST.MF (MIDlet-Name, MIDlet-Version, MIDlet-Vendor, MicroEdition-Profile, MicroEdition-Configuration)
└── res/ (可選圖標)

十、總結

JSR-82 為 J2ME 提供了完整、標準化的藍牙編程模型:**LocalDevice → DiscoveryAgent → ServiceRecord → StreamConnection** 四大核心類串起「發現-註冊-連接-通訊」全鏈路。掌握異步回調 (DiscoveryListener)、UUID 設計、連接 URL 構建、流處理、線程模型,即可開發藍牙聊天、文件傳輸、遙控、多玩家遊戲等應用。雖然 J2ME 已退出歷史舞台,但其藍牙編程範式在 Android、嵌入式 Java、IoT 模組中延續,學習價值永不褪色。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved