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

藍牙開發實戰(二):手機間藍牙通信完整實現與代碼詳解

     編輯:Java 相關知識

專案概覽

單 JAR 包實現 C/S 雙模式手機藍牙通信:Server 監聽連接、Client 搜索設備發現服務並連接、雙向文本消息收發。採用模組化分層架構,代碼結構清晰,適合作為藍牙應用開發模板。

一、運行流程

  1. 啟動 → 主界面選擇 Server 或 Client
  2. Server 路徑:Setup → 開啟服務註冊 → 循環 acceptAndOpen() 等待連接 → 收到消息回显 "server echo: msg"
  3. Client 路徑:Connect → startInquiry() 搜索設備 → 遍歷設備 searchServices() 發現目標 UUID → 連接 → 發送文本 → 接收回显

二、工程結構

src/
├── core/
│   └── BlueMessage.java          # MIDlet 入口:生命周期、界面導航、退出控制
├── components/
│   ├── MainForm.java             # 模式選擇界面:ChoiceGroup(Server/Client) + Command
│   ├── BlueClient.java           # 客戶端界面:輸入框、發送按鈕、消息列表
│   └── BlueServer.java           # 服務端界面:狀態顯示、消息日誌
└── bluetooth/
    ├── BlueClientService.java    # 客戶端藍牙邏輯:DiscoveryListener 實現
    └── BlueServerService.java    # 服務端藍牙邏輯:Runnable 服務線程

三、核心類代碼深度解析

1. BlueMessage.java - MIDlet 骨架

public class BlueMessage extends MIDlet {
    public BlueMessage() {
        MainForm form = new MainForm(this);
        Display.getDisplay(this).setCurrent(form);
    }
    public void ExitMidlet() { destroyApp(true); notifyDestroyed(); }
    public void NavigateTo(Displayable d) { Display.getDisplay(this).setCurrent(d); }
    // 生命周期方法留空...
}

設計亮點:提供 ExitMidlet()、NavigateTo() 供子界面調用,實現「界面無 MIDlet 依賴」。

2. MainForm.java - 模式選擇

public class MainForm extends Form implements CommandListener {
    private ChoiceGroup choiceGp; // EXCLUSIVE 單選:client/server
    private Command cmdSelect, cmdExit;
    private BlueMessage parent;

    public void commandAction(Command c, Displayable d) {
        if (c == cmdExit) parent.ExitMidlet();
        else if (c == cmdSelect) {
            switch (choiceGp.getSelectedIndex()) {
                case 0: parent.NavigateTo(new BlueClient(parent, this)); break;
                case 1: parent.NavigateTo(new BlueServer(parent, this)); break;
            }
        }
    }
}

3. BlueServerService.java - 服務端核心 (完整代碼)

public class BlueServerService implements Runnable {
    private static final UUID SERVER_UUID = new UUID("F0E0D0C0B0A000908070605040302010", false);
    private StreamConnectionNotifier notifier;
    private BlueServer serverForm;
    private ServiceRecord serviceRecord;

    public void run() {
        // 1. 獲取本地設備、設置可發現
        LocalDevice local = LocalDevice.getLocalDevice();
        local.setDiscoverable(DiscoveryAgent.GIAC);

        // 2. 打開服務連接器
        notifier = (StreamConnectionNotifier) Connector.open(getConnectionString());
        serviceRecord = local.getRecord(notifier);

        serverForm.appendInfo("Service ready, waiting...");
        serverForm.changeForm(); // 切換到等待界面

        // 3. 循環接受連接
        while (true) {
            StreamConnection conn = notifier.acceptAndOpen(); // 阻塞
            new Thread(new ProcessConnection(conn)).start();  // 每連接一線程
        }
    }

    private String getConnectionString() {
        return "btspp://localhost:" + SERVER_UUID + ";name=BlueMessage;authorize=false";
    }

    private class ProcessConnection implements Runnable {
        private StreamConnection conn;
        public void run() {
            try {
                String input = readInputString(conn);
                serverForm.appendInfo("Received: " + input);
                String output = input.startsWith("connect...") ? "welcome..." : "server echo " + input;
                sendOutputString(output, conn);
                conn.close();
            } catch (Exception e) { /* log */ }
        }
        // readInputString/sendOutputString 使用 DataInput/OutputStream 讀寫 UTF
    }
}

關鍵點:

  • SERVER_UUID 必須與客戶端完全一致,標識唯一服務
  • authorize=false 跳過授權確認,簡化測試流程
  • 連接處理線程隔離,避免阻塞主監聽循環

4. BlueClientService.java - 客戶端核心 (關鍵邏輯)

public class BlueClientService implements Runnable, DiscoveryListener {
    private static final UUID TARGET_UUID = new UUID("F0E0D0C0B0A000908070605040302010", false);
    private Vector devices = new Vector(), records = new Vector();
    private DiscoveryAgent agent;
    private BlueClient clientForm;
    private int[] transIDs;
    private int activeIndex = -1;

    public synchronized void run() {
        // 1. 初始化本地設備、設置不可發現
        LocalDevice local = LocalDevice.getLocalDevice();
        agent = local.getDiscoveryAgent();
        local.setDiscoverable(DiscoveryAgent.NOT_DISCOVERABLE);

        // 2. 搜索設備
        UUID[] uuidSet = { new UUID(0x1101), TARGET_UUID }; // 0x1101 = SPP 協議
        agent.startInquiry(DiscoveryAgent.GIAC, this);
        wait(); // 阻塞等待 inquiryCompleted 回調

        // 3. 遍歷設備搜索服務
        transIDs = new int[devices.size()];
        for (int i=0; i= 0) clientForm.changeForm(); // 切換到聊天界面
    }

    // DiscoveryListener 回調實現
    public void deviceDiscovered(RemoteDevice d, DeviceClass c) { devices.addElement(d); }
    public void inquiryCompleted(int type) { synchronized(this){ notify(); } }
    public void servicesDiscovered(int transID, ServiceRecord[] srs) { for(ServiceRecord sr: srs) records.addElement(sr); }
    public void serviceSearchCompleted(int transID, int respCode) {
        // 標記完成,全部完成時 notify()
    }

    // 發送消息入口
    public void sendMsg(String msg) { accessService(records.elementAt(activeIndex), msg); }
}

核心機制:

  • 雙階段阻塞等待:wait() 配合 notify() 實現異步回調同步化
  • 事務 ID 追蹤:transIDs 數組管理多設備並發服務搜索
  • 服務驗證:getActiveService() 實際發送測試包確認服務可用

四、UUID 深度解析

  • 128-bit 長 UUID:F0E0D0C0B0A000908070605040302010 — 應用層服務唯一標識,開發者自定義
  • 16-bit 短 UUID:0x1101 — 標準服務類別(SPP 串口配置文件),由藍牙 SIG 分配
  • 客戶端搜索時組合使用:uuidSet = {SPP_UUID, APP_UUID},確保協議匹配且服務正確

五、常見問題與調試技巧

現象原因解決
搜索不到設備未設置可發現模式 / 藍牙關閉 / 距離過遠Server 必須 setDiscoverable(GIAC);確認硬件開啟
服務搜索失敗UUID 不匹配 / 協議不支持雙端 UUID 字節級一致;確認設備支持 SPP
連接斷開異常流讀寫未關閉 / 線程衝突finally 塊關閉流;單線程處理單連接
模擬器無法通信WTK 模擬器藍牙模擬限制必須真機測試;或使用 BlueCove 模擬 PC 端

六、總結

本專案確立了 J2ME 藍牙通信的標準化模板:DiscoveryListener 異步回調同步化、UUID 雙層標識、StreamConnection 線程隔離處理。掌握此模式,可快速構建任意藍牙數據交換應用。

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