在 Java ME (Micro Edition) 平台上開發藍牙應用,核心流程包含三大階段:裝置發現、服務搜尋、資料傳輸。本文將透過一個完整的 MIDlet 範例,詳細解析如何利用 JSR 82 API 實現裝置配對、尋找 OBEX Push 服務,並傳送文字訊息到目標裝置(如支援藍牙的 Windows Vista 筆電)。
發現流程的起點是取得本地藍牙裝置的 DiscoveryAgent,並啟動查詢:
// 取得本地發現代理
DiscoveryAgent agent = LocalDevice.getLocalDevice().getDiscoveryAgent();
// 開始一般無限制查詢 (GIAC),this 實作 DiscoveryListener 接收回呼
agent.startInquiry(DiscoveryAgent.GIAC, this);
DiscoveryListener 介面需實作四個方法,其中兩個對發現階段關鍵:
deviceDiscovered(RemoteDevice btDevice, DeviceClass cod):發現新裝置時觸發,此處將裝置友善名稱加入 UI 列表,並以雜湊表對應索引與裝置物件。inquiryCompleted(int discType):查詢結束,根據結果更新顯示(裝置列表或「無裝置」提示)。找到裝置後,必須查詢其支援的服務。為了傳送檔案,我們尋找 OBEX Object Push Profile,其 UUID 為 0x1105:
// 針對特定裝置搜尋 OBEX Push 服務
agent.searchServices(
null, // 不指定屬性集合
new UUID[] { new UUID(0x1105L) }, // 目標服務 UUID
remoteDevice, // 目標裝置
this // DiscoveryListener 回呼
);
服務發現完成時,servicesDiscovered(int transID, ServiceRecord[] servRecord) 會被呼叫。關鍵步驟是從 ServiceRecord 取得連線 URL:
// 取得無需認證/加密的連線 URL
String connURL = servRecord[i].getConnectionURL(
ServiceRecord.NOAUTHENTICATE_NOENCRYPT,
false
);
此 URL 封裝了協定、目標裝置位址、通道號等資訊,是後續建立連線的關鍵。
拿到連線 URL 後,即可建立 ClientSession 並推送資料:
// 1. 開啟用戶端會話
ClientSession clientSession = (ClientSession) Connector.open(connURL);
// 2. 連線 (無標頭)
HeaderSet connectReply = clientSession.connect(null);
if (connectReply.getResponseCode() != ResponseCodes.OBEX_HTTP_OK) {
throw new IOException("連線失敗,回應碼:" + connectReply.getResponseCode());
}
// 3. 準備傳輸標頭
HeaderSet headers = clientSession.createHeaderSet();
headers.setHeader(HeaderSet.LENGTH, new Long(message.length()));
headers.setHeader(HeaderSet.NAME, "myNote.txt");
headers.setHeader(HeaderSet.TYPE, "text/plain");
// 4. 建立 PUT 操作並寫入資料
Operation putOp = clientSession.put(headers);
OutputStream out = putOp.openOutputStream();
out.write(message.getBytes());
out.close();
putOp.close();
clientSession.disconnect(null);
注意事項:
InterruptedIOException。public class BluetoothChatMIDlet extends MIDlet implements DiscoveryListener, CommandListener {
// UI 元件: List (裝置列表), TextBox (輸入訊息), Alert/Form (狀態顯示)
// 狀態機: IDLE -> INQUIRING -> SERVICE_SEARCH -> CONNECTING -> SENDING
// 核心欄位: DiscoveryAgent agent, Hashtable devices, Vector foundServices, String connURL
// 實作 DiscoveryListener 4 個方法
// 實作 CommandListener 處理使用者操作 (搜尋、傳送、離開)
// 內部類別 SenderThread 執行實際 OBEX 傳輸避免阻塞 UI 執行緒
}
| 現象 | 可能原因 | 解決方向 |
|---|---|---|
startInquiry 擲出 BluetoothStateException | 藍牙關閉或硬體不支援 | 檢查 LocalDevice.getLocalDevice() 是否拋出例外;確認手機藍牙已開啟 |
| 搜尋不到服務 | 目標裝置未開啟 OBEX Push / 可被發現模式 | Windows:「控制台 > 藍牙裝置 > 選項 > 開啟傳入連線」;手機:開啟「接收檔案」模式 |
連線逾時 / ConnectionNotFoundException | URL 格式錯誤、通道號不對、防火牆阻擋 | 印出 connURL 檢查格式;嘗試 NOAUTHENTICATE_NOENCRYPT 降低安全性需求 |
| 傳送中斷 | 訊號弱、目標裝置儲存空間不足、對方取消接收 | 捕獲 IOException 並給予使用者友善提示;大檔案分段傳輸 |
掌握「發現 → 服務搜尋 → OBEX 傳輸」三部曲,即可構建基礎的 Java ME 藍牙傳檔應用。進階方向可包含:
雖然 Java ME 已漸退出歷史舞台,但 JSR 82 的設計思維(裝置/服務/連線三層抽象)對理解現代 Android 藍牙 API (BluetoothAdapter, BluetoothDevice, BluetoothSocket) 仍有極高參考價值。