Android 平臺包含藍牙網(wǎng)絡堆棧支持酣衷,憑借此項支持辞友,設備能以無線方式與其他藍牙設備交換數(shù)據(jù)帝璧。應用框架提供了通過 Android Bluetooth API
訪問藍牙功能的途徑滥壕。 這些 API 允許應用以無線方式連接到其他藍牙設備蒿偎,從而實現(xiàn)點到點和多點無線功能朽们。
使用 Bluetooth API,Android 應用可執(zhí)行以下操作:
- 掃描其他藍牙設備
- 查詢本地藍牙適配器的配對藍牙設備
- 建立 RFCOMM 通道
- 通過服務發(fā)現(xiàn)連接到其他設備
- 與其他設備進行雙向數(shù)據(jù)傳輸
- 管理多個連接
本文將介紹如何使用傳統(tǒng)藍牙诉位。傳統(tǒng)藍牙適用于電池使用強度較大的操作骑脱,例如 Android 設備之間的流式傳輸和通信等。 針對具有低功耗要求的藍牙設備苍糠,Android 4.3(API 級別 18)中引入了面向低功耗藍牙的 API 支持叁丧。 如需了解更多信息,請參閱低功耗藍牙岳瞭。
基礎知識
本文將介紹如何使用 Android Bluetooth API
來完成使用藍牙進行通信的四項主要任務:設置藍牙拥娄、查找局部區(qū)域內(nèi)的配對設備或可用設備、連接設備瞳筏,以及在設備之間傳輸數(shù)據(jù)稚瘾。
android.bluetooth 包中提供了所有 Bluetooth API。 下面概要列出了創(chuàng)建藍牙連接所需的類和接口:
BluetoothAdapter
表示本地藍牙適配器(藍牙無線裝置)姚炕。 BluetoothAdapter是所有藍牙交互的入口點摊欠。 利用它可以發(fā)現(xiàn)其他藍牙設備,查詢綁定(配對)設備的列表钻心,使用已知的 MAC 地址實例化 BluetoothDevice凄硼,以及創(chuàng)建 BluetoothServerSocket`以偵聽來自其他設備的通信铅协。
BluetoothDevice
表示遠程藍牙設備捷沸。利用它可以通過 BluetoothSocket 請求與某個遠程設備建立連接,或查詢有關該設備的信息狐史,例如設備的名稱痒给、地址说墨、類和綁定狀態(tài)等。
BluetoothSocket
表示藍牙套接字接口(與 TCP Socket 相似)苍柏。這是允許應用通過 InputStream 和 OutputStream 與其他藍牙設備交換數(shù)據(jù)的連接點尼斧。
BluetoothServerSocket
表示用于偵聽傳入請求的開放服務器套接字(類似于 TCP ServerSocket)。 要連接兩臺 Android 設備试吁,其中一臺設備必須使用此類開放一個服務器套接字棺棵。 當一臺遠程藍牙設備向此設備發(fā)出連接請求時,BluetoothServerSocket將會在接受連接后返回已連接的 BluetoothSocket熄捍。
BluetoothClass
描述藍牙設備的一般特征和功能烛恤。 這是一組只讀屬性,用于定義設備的主要和次要設備類及其服務余耽。 不過缚柏,它不能可靠地描述設備支持的所有藍牙配置文件和服務,而是適合作為設備類型提示碟贾。
BluetoothProfile
表示藍牙配置文件的接口币喧。 藍牙配置文件是適用于設備間藍牙通信的無線接口規(guī)范。 免提配置文件便是一個示例袱耽。 如需了解有關配置文件的詳細討論杀餐,請參閱使用配置文件
BluetoothHeadset
提供藍牙耳機支持,以便與手機配合使用朱巨。 其中包括藍牙耳機和免提(1.5 版)配置文件怜浅。
BluetoothA2dp
定義高質量音頻如何通過藍牙連接和流式傳輸,從一臺設備傳輸?shù)搅硪慌_設備蔬崩《褡“A2DP”代表高級音頻分發(fā)配置文件。
BluetoothHealth
表示用于控制藍牙服務的健康設備配置文件代理沥阳。
BluetoothHealthCallback
用于實現(xiàn) BluetoothHealth回調的抽象類跨琳。您必須擴展此類并實現(xiàn)回調方法,以接收關于應用注冊狀態(tài)和藍牙通道狀態(tài)變化的更新內(nèi)容桐罕。
BluetoothHealthAppConfiguration
表示第三方藍牙健康應用注冊的應用配置脉让,以便與遠程藍牙健康設備通信。
BluetoothProfile.ServiceListener
在 BluetoothProfile
IPC 客戶端連接到服務(即功炮,運行特定配置文件的內(nèi)部服務)或斷開服務連接時向其發(fā)送通知的接口溅潜。
藍牙權限
要在應用中使用藍牙功能,必須聲明藍牙權限 BLUETOOTH薪伏。您需要此權限才能執(zhí)行任何藍牙通信滚澜,例如請求連接、接受連接和傳輸數(shù)據(jù)等嫁怀。
如果您希望您的應用啟動設備發(fā)現(xiàn)或操作藍牙設置设捐,則還必須聲明 BLUETOOTH_ADMIN 權限借浊。 大多數(shù)應用需要此權限僅僅為了能夠發(fā)現(xiàn)本地藍牙設備。 除非該應用是將要應用戶請求修改藍牙設置的“超級管理員”萝招,否則不應使用此權限所授予的其他能力蚂斤。 注:如果要使用 BLUETOOTH_ADMIN 權限,則還必須擁有 BLUETOOTH權限槐沼。
在您的應用清單文件中聲明藍牙權限曙蒸。例如:
<manifest ... >
<uses-permission android:name="android.permission.BLUETOOTH" />
...
</manifest>
如需了解有關聲明應用權限的更多信息,請參閱 <uses-permission> 參考資料岗钩。
設置藍牙
圖 1:啟用藍牙對話框逸爵。
您需要驗證設備支持藍牙,并且如果支持確保將其啟用凹嘲,然后您的應用才能通過藍牙進行通信师倔。
如果不支持藍牙,則應正常停用任何藍牙功能周蹭。 如果支持藍牙但已停用此功能趋艘,則可以請求用戶在不離開應用的同時啟用藍牙。 可使用 BluetoothAdapter凶朗,分兩步完成此設置:
1. 獲取 BluetoothAdapter
所有藍牙 Activity 都需要 BluetoothAdapter瓷胧。 要獲取 BluetoothAdapter,請調用靜態(tài) getDefaultAdapter()方法棚愤。這將返回一個表示設備自身的藍牙適配器(藍牙無線裝置)的 BluetoothAdapter搓萧。 整個系統(tǒng)有一個藍牙適配器,并且您的應用可使用此對象與之交互宛畦。 如果 getDefaultAdapter() 返回 null瘸洛,則該設備不支持藍牙,您的操作到此為止次和。 例如:
BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (mBluetoothAdapter == null) {
// Device does not support Bluetooth
}
2. 啟用藍牙
下一步反肋,您需要確保已啟用藍牙。調用 isEnabled() 以檢查當前是否已啟用藍牙踏施。 如果此方法返回 false石蔗,則表示藍牙處于停用狀態(tài)。要請求啟用藍牙畅形,請使用 ACTION_REQUEST_ENABLE 操作 Intent 調用 startActivityForResult()养距。這將通過系統(tǒng)設置發(fā)出啟用藍牙的請求(無需停止您的應用)。 例如:
if (!mBluetoothAdapter.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}
如圖 1 所示日熬,將顯示對話框棍厌,請求用戶允許啟用藍牙。如果用戶響應“Yes”,系統(tǒng)將開始啟用藍牙定铜,并在該進程完成(或失斞粢骸)后將焦點返回到您的應用怕敬。
傳遞給 startActivityForResult() 的 REQUEST_ENABLE_BT
常量是在局部定義的整型(必須大于 0)揣炕,系統(tǒng)會將其作為 requestCode
參數(shù)傳遞回您的 onActivityResult()實現(xiàn)。
如果成功啟用藍牙东跪,您的 Activity 將會在 onActivityResult()回調中收到 RESULT_OK結果代碼畸陡。 如果由于某個錯誤(或用戶響應“No”)而沒有啟用藍牙,則結果代碼為 RESULT_CANCELED虽填。
您的應用還可以選擇偵聽 ACTION_STATE_CHANGED 廣播 Intent丁恭,每當藍牙狀態(tài)發(fā)生變化時,系統(tǒng)都會廣播此 Intent斋日。 此廣播包含額外字段 EXTRA_STATE 和 EXTRA_PREVIOUS_STATE牲览,二者分別包含新的和舊的藍牙狀態(tài)。 這些額外字段可能的值包括 STATE_TURNING_ON恶守、STATE_ON第献、STATE_TURNING_OFF 和 STATE_OFF。偵聽此廣播適用于檢測在您的應用運行期間對藍牙狀態(tài)所做的更改兔港。
提示:啟用可檢測性將會自動啟用藍牙庸毫。 如果您計劃在執(zhí)行藍牙 Activity 之前一直啟用設備的可檢測性,則可以跳過上述步驟 2衫樊。 閱讀下面的啟用可檢測性飒赃。
查找設備
使用 BluetoothAdapter,您可以通過設備發(fā)現(xiàn)或通過查詢配對(綁定)設備的列表來查找遠程藍牙設備科侈。
設備發(fā)現(xiàn)是一個掃描過程载佳,它會搜索局部區(qū)域內(nèi)已啟用藍牙功能的設備,然后請求一些關于各臺設備的信息(有時也被稱為“發(fā)現(xiàn)”臀栈、“查詢”或“掃描”)刚盈。但局部區(qū)域內(nèi)的藍牙設備僅在其當前已啟用可檢測性時才會響應發(fā)現(xiàn)請求。 如果設備可檢測到挂脑,它將通過共享一些信息(例如設備名稱藕漱、類及其唯一 MAC 地址)來響應發(fā)現(xiàn)請求。 利用此信息崭闲,執(zhí)行發(fā)現(xiàn)的設備可以選擇發(fā)起到被發(fā)現(xiàn)設備的連接肋联。
在首次與遠程設備建立連接后,將會自動向用戶顯示配對請求刁俭。 設備完成配對后橄仍,將會保存關于該設備的基本信息(例如設備名稱、類和 MAC 地址),并且可使用 Bluetooth API 讀取這些信息侮繁。 利用遠程設備的已知 MAC 地址可隨時向其發(fā)起連接虑粥,而無需執(zhí)行發(fā)現(xiàn)操作(假定該設備處于有效范圍內(nèi))。
請記住宪哩,被配對與被連接之間存在差別娩贷。被配對意味著兩臺設備知曉彼此的存在,具有可用于身份驗證的共享鏈路密鑰锁孟,并且能夠與彼此建立加密連接彬祖。 被連接意味著設備當前共享一個 RFCOMM 通道,并且能夠向彼此傳輸數(shù)據(jù)品抽。 當前的 Android Bluetooth API 要求對設備進行配對储笑,然后才能建立 RFCOMM 連接。 (在使用 Bluetooth API 發(fā)起加密連接時圆恤,會自動執(zhí)行配對)突倍。
以下幾節(jié)介紹如何查找已配對的設備,或使用設備發(fā)現(xiàn)來發(fā)現(xiàn)新設備盆昙。
注:Android 設備默認處于不可檢測到狀態(tài)羽历。 用戶可通過系統(tǒng)設置將設備設為在有限的時間內(nèi)處于可檢測到狀態(tài),或者弱左,應用可請求用戶在不離開應用的同時啟用可檢測性窄陡。 下面討論如何啟用可檢測性。
查詢配對的設備
在執(zhí)行設備發(fā)現(xiàn)之前拆火,有必要查詢已配對的設備集跳夭,以了解所需的設備是否處于已知狀態(tài)。 為此们镜,請調用 getBondedDevices()币叹。 這將返回表示已配對設備的一組 BluetoothDevice。 例如模狭,您可以查詢所有已配對設備颈抚,然后使用 ArrayAdapter 向用戶顯示每臺設備的名稱:
Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices();
// If there are paired devices
if (pairedDevices.size() > 0) {
// Loop through paired devices
for (BluetoothDevice device : pairedDevices) {
// Add the name and address to an array adapter to show in a ListView
mArrayAdapter.add(device.getName() + "\n" + device.getAddress());
}
}
要發(fā)起連接,BluetoothDevice 對象僅僅需要提供 MAC 地址嚼鹉。 在此示例中贩汉,它將保存為顯示給用戶的 ArrayAdapter 的一部分。 之后可提取該 MAC 地址锚赤,以便發(fā)起連接匹舞。 您可以在有關連接設備的部分詳細了解如何創(chuàng)建連接。
發(fā)現(xiàn)設備
要開始發(fā)現(xiàn)設備线脚,只需調用 startDiscovery()赐稽。該進程為異步進程叫榕,并且該方法會立即返回一個布爾值,指示是否已成功啟動發(fā)現(xiàn)操作姊舵。 發(fā)現(xiàn)進程通常包含約 12 秒鐘的查詢掃描晰绎,之后對每臺發(fā)現(xiàn)的設備進行頁面掃描,以檢索其藍牙名稱括丁。
您的應用必須針對 ACTION_FOUND Intent 注冊一個 BroadcastReceiver荞下,以便接收每臺發(fā)現(xiàn)的設備的相關信息。 針對每臺設備躏将,系統(tǒng)將會廣播 ACTION_FOUND Intent锄弱。此 Intent 將攜帶額外字段 EXTRA_DEVICE 和 EXTRA_CLASS考蕾,二者分別包含BluetoothDevice和 BluetoothClass祸憋。 例如,下面說明了在發(fā)現(xiàn)設備時如何注冊以處理廣播肖卧。
// Create a BroadcastReceiver for ACTION_FOUND
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
// When discovery finds a device
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
// Get the BluetoothDevice object from the Intent
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
// Add the name and address to an array adapter to show in a ListView
mArrayAdapter.add(device.getName() + "\n" + device.getAddress());
}
}
};
// Register the BroadcastReceiver
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
registerReceiver(mReceiver, filter); // Don't forget to unregister during onDestroy
要發(fā)起連接蚯窥,BluetoothDevice對象僅僅需要提供 MAC 地址。 在此示例中塞帐,它將保存為顯示給用戶的 ArrayAdapter 的一部分拦赠。 之后可提取該 MAC 地址,以便發(fā)起連接葵姥。 您可以在有關連接設備的部分詳細了解如何創(chuàng)建連接荷鼠。
注意:執(zhí)行設備發(fā)現(xiàn)對于藍牙適配器而言是一個非常繁重的操作過程,并且會消耗大量資源榔幸。 在找到要連接的設備后允乐,確保始終使用 cancelDiscovery() 停止發(fā)現(xiàn),然后再嘗試連接削咆。 此外牍疏,如果您已經(jīng)保持與某臺設備的連接,那么執(zhí)行發(fā)現(xiàn)操作可能會大幅減少可用于該連接的帶寬拨齐,因此不應該在處于連接狀態(tài)時執(zhí)行發(fā)現(xiàn)操作鳞陨。
啟用可檢測性
如果您希望將本地設備設為可被其他設備檢測到,請使用 ACTION_REQUEST_DISCOVERABLE 操作 Intent 調用 startActivityForResult(Intent, int)瞻惋。 這將通過系統(tǒng)設置發(fā)出啟用可檢測到模式的請求(無需停止您的應用)厦滤。 默認情況下,設備將變?yōu)榭蓹z測到并持續(xù) 120 秒鐘歼狼。 您可以通過添加 EXTRA_DISCOVERABLE_DURATION Intent Extra 來定義不同的持續(xù)時間掏导。 應用可以設置的最大持續(xù)時間為 3600 秒,值為 0 則表示設備始終可檢測到蹂匹。 任何小于 0 或大于 3600 的值都會自動設為 120 秒碘菜。 例如,以下片段會將持續(xù)時間設為 300 秒:
Intent discoverableIntent = new
Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
startActivity(discoverableIntent);
圖 2:啟用可檢測性對話框。
如圖 2 所示忍啸,將顯示對話框仰坦,請求用戶允許將設備設為可檢測到。如果用戶響應“Yes”计雌,則設備將變?yōu)榭蓹z測到并持續(xù)指定的時間量悄晃。 然后,您的 Activity 將會收到對 onActivityResult()) 回調的調用凿滤,其結果代碼等于設備可檢測到的持續(xù)時間妈橄。 如果用戶響應“No”或出現(xiàn)錯誤,結果代碼將為 RESULT_CANCELED翁脆。
注:如果尚未在設備上啟用藍牙眷蚓,則啟用設備可檢測性將會自動啟用藍牙。
設備將在分配的時間內(nèi)以靜默方式保持可檢測到模式反番。如果您希望在可檢測到模式發(fā)生變化時收到通知沙热,您可以針對 ACTION_SCAN_MODE_CHANGED Intent 注冊 BroadcastReceiver。 它將包含額外字段 EXTRA_SCAN_MODE 和EXTRA_PREVIOUS_SCAN_MODE罢缸,二者分別告知您新的和舊的掃描模式篙贸。 每個字段可能的值包括SCAN_MODE_CONNECTABLE_DISCOVERABLE、 SCAN_MODE_CONNECTABLE 或 SCAN_MODE_NONE枫疆,這些值分別指示設備處于可檢測到模式爵川、未處于可檢測到模式但仍能接收連接,或未處于可檢測到模式并且無法接收連接息楔。
如果您將要發(fā)起到遠程設備的連接寝贡,則無需啟用設備可檢測性。 僅當您希望您的應用托管將用于接受傳入連接的服務器套接字時钞螟,才有必要啟用可檢測性兔甘,因為遠程設備必須能夠發(fā)現(xiàn)該設備,然后才能發(fā)起連接鳞滨。
連接設備
要在兩臺設備上的應用之間創(chuàng)建連接洞焙,必須同時實現(xiàn)服務器端和客戶端機制,因為其中一臺設備必須開放服務器套接字拯啦,而另一臺設備必須發(fā)起連接(使用服務器設備的 MAC 地址發(fā)起連接)澡匪。 當服務器和客戶端在同一 RFCOMM 通道上分別擁有已連接的 BluetoothSocket 時,二者將被視為彼此連接褒链。 這種情況下唁情,每臺設備都能獲得輸入和輸出流式傳輸,并且可以開始傳輸數(shù)據(jù)甫匹,在有關管理連接的部分將會討論這一主題甸鸟。 本部分介紹如何在兩臺設備之間發(fā)起連接惦费。
服務器設備和客戶端設備分別以不同的方法獲得需要的 BluetoothSocket。服務器將在傳入連接被接受時收到套接字抢韭。 客戶端將在其打開到服務器的 RFCOMM 通道時收到該套接字薪贫。
圖 3:藍牙配對對話框。
一種實現(xiàn)技術是自動將每臺設備準備為一個服務器刻恭,從而使每臺設備開放一個服務器套接字并偵聽連接瞧省。然后任一設備可以發(fā)起與另一臺設備的連接,并成為客戶端鳍贾。 或者鞍匾,其中一臺設備可顯式“托管”連接并按需開放一個服務器套接字,而另一臺設備則直接發(fā)起連接骑科。
注:如果兩臺設備之前尚未配對橡淑,則在連接過程中,Android 框架會自動向用戶顯示配對請求通知或對話框(如圖 3 所示)纵散。因此梳码,在嘗試連接設備時隐圾,您的應用無需擔心設備是否已配對伍掀。 您的 RFCOMM 連接嘗試將被阻塞,直至用戶成功完成配對或配對失斚静亍(包括用戶拒絕配對蜜笤、配對失敗或超時)。
連接為服務器
當您需要連接兩臺設備時盐碱,其中一臺設備必須通過保持開放的 BluetoothServerSocket 來充當服務器把兔。 服務器套接字的用途是偵聽傳入的連接請求,并在接受一個請求后提供已連接的 BluetoothSocket瓮顽。 從 BluetoothServerSocket 獲取 BluetoothSocket 后县好,可以(并且應該)舍棄 BluetoothServerSocket,除非您需要接受更多連接暖混。
關于 UUID
通用唯一標識符 (UUID) 是用于唯一標識信息的字符串 ID 的 128 位標準化格式缕贡。 UUID 的特點是其足夠龐大,因此您可以選擇任意隨機值而不會發(fā)生沖突拣播。 在此示例中晾咪,它被用于唯一標識應用的藍牙服務。 要獲取 UUID 以用于您的應用贮配,您可以使用網(wǎng)絡上的眾多隨機 UUID 生成器之一谍倦,然后使用 fromString(String) 初始化一個 UUID。
以下是設置服務器套接字并接受連接的基本過程:
1. 通過調用 listenUsingRfcommWithServiceRecord(String, UUID) 獲取 BluetoothServerSocket泪勒。
該字符串是您的服務的可識別名稱昼蛀,系統(tǒng)會自動將其寫入到設備上的新服務發(fā)現(xiàn)協(xié)議 (SDP) 數(shù)據(jù)庫條目(可使用任意名稱宴猾,也可直接使用您的應用名稱)。 UUID 也包含在 SDP 條目中叼旋,并且將作為與客戶端設備的連接協(xié)議的基礎鳍置。 也就是說,當客戶端嘗試連接此設備時送淆,它會攜帶能夠唯一標識其想要連接的服務的 UUID税产。 兩個 UUID 必須匹配,在下一步中偷崩,連接才會被接受辟拷。
2. 通過調用 accept() 開始偵聽連接請求。
這是一個阻塞調用阐斜。它將在連接被接受或發(fā)生異常時返回衫冻。 僅當遠程設備發(fā)送的連接請求中所包含的 UUID 與向此偵聽服務器套接字注冊的 UUID 相匹配時,連接才會被接受谒出。 操作成功后隅俘,accept()將會返回已連接的 BluetoothSocket。
3. 除非您想要接受更多連接笤喳,否則請調用 close()为居。
這將釋放服務器套接字及其所有資源,但不會關閉 accept() 所返回的已連接的 BluetoothSocket杀狡。 與 TCP/IP 不同蒙畴,RFCOMM 一次只允許每個通道有一個已連接的客戶端扛伍,因此大多數(shù)情況下较幌,在接受已連接的套接字后立即在 BluetoothServerSocket 上調用 close() 是行得通的腐晾。
accept()調用不應在主 Activity UI 線程中執(zhí)行芥颈,因為它是阻塞調用拇惋,并會阻止與應用的任何其他交互对雪。 在您的應用所管理的新線程中使用 BluetoothServerSocket 或 BluetoothSocket完成所有工作弓候,這通常是一種行之有效的做法库说。 要終止 accept() 等被阻塞的調用休玩,請通過另一個線程在 BluetoothServerSocket(或 BluetoothSocket)上調用 close()著淆,被阻塞的調用將會立即返回。 請注意哥捕,BluetoothServerSocket 或 BluetoothSocket中的所有方法都是線程安全的方法牧抽。
<devsite-heading text="示例" id="示例" level="h4" link="" class="" style="box-sizing: inherit; display: block; margin-right: -40px !important; padding-right: 40px !important;">#### 示例</devsite-heading>
以下是一個用于接受傳入連接的服務器組件的簡化線程:
private class AcceptThread extends Thread {
private final BluetoothServerSocket mmServerSocket;
public AcceptThread() {
// Use a temporary object that is later assigned to mmServerSocket,
// because mmServerSocket is final
BluetoothServerSocket tmp = null;
try {
// MY_UUID is the app's UUID string, also used by the client code
tmp = mBluetoothAdapter.listenUsingRfcommWithServiceRecord(NAME, MY_UUID);
} catch (IOException e) { }
mmServerSocket = tmp;
}
public void run() {
BluetoothSocket socket = null;
// Keep listening until exception occurs or a socket is returned
while (true) {
try {
socket = mmServerSocket.accept();
} catch (IOException e) {
break;
}
// If a connection was accepted
if (socket != null) {
// Do work to manage the connection (in a separate thread)
manageConnectedSocket(socket);
mmServerSocket.close();
break;
}
}
}
/** Will cancel the listening socket, and cause the thread to finish */
public void cancel() {
try {
mmServerSocket.close();
} catch (IOException e) { }
}
}
在此示例中,只需要一個傳入連接遥赚,因此在接受連接并獲取 BluetoothSocket 之后扬舒,應用會立即將獲取的 BluetoothSocket 發(fā)送到單獨的線程,關閉 BluetoothServerSocket 并中斷循環(huán)凫佛。
請注意讲坎,當 accept()返回 BluetoothSocket 時孕惜,表示套接字已連接好,因此您不應該像在客戶端那樣調用 connect()晨炕。
manageConnectedSocket()
是應用中的虛構方法衫画,它將啟動用于傳輸數(shù)據(jù)的線程,在有關管理連接的部分將會討論這一主題瓮栗。
在完成傳入連接的偵聽后削罩,通常應立即關閉您的 BluetoothServerSocket。 在此示例中费奸,獲取 BluetoothSocket 后立即調用 close()弥激。 您也可能希望在您的線程中提供一個公共方法,以便在需要停止偵聽服務器套接字時關閉私有BluetoothSocket愿阐。
連接為客戶端
要發(fā)起與遠程設備(保持開放的服務器套接字的設備)的連接微服,必須首先獲取表示該遠程設備的 BluetoothDevice 對象。(在前面有關查找設備的部分介紹了如何獲取 BluetoothDevice)缨历。 然后必須使用 BluetoothDevice來獲取 BluetoothSocket并發(fā)起連接以蕴。
以下是基本過程:
1. 使用 BluetoothDevice,通過調用 createRfcommSocketToServiceRecord(UUID) 獲取 BluetoothSocket辛孵。
這將初始化將要連接到 BluetoothDevice 的 BluetoothSocket`丛肮。 此處傳遞的 UUID 必須與服務器設備在使用 listenUsingRfcommWithServiceRecord(String, UUID) 開放其 BluetoothServerSocket 時所用的 UUID 相匹配。 要使用相同的 UUID觉吭,只需將該 UUID 字符串以硬編碼方式編入應用腾供,然后通過服務器代碼和客戶端代碼引用該字符串。
2. 通過調用 connect() 發(fā)起連接鲜滩。
執(zhí)行此調用時,系統(tǒng)將會在遠程設備上執(zhí)行 SDP 查找节值,以便匹配 UUID徙硅。 如果查找成功并且遠程設備接受了該連接,它將共享 RFCOMM 通道以便在連接期間使用搞疗,并且 connect() 將會返回嗓蘑。 此方法為阻塞調用。 如果由于任何原因連接失敗或 connect() 方法超時(大約 12 秒之后)匿乃,它將會引發(fā)異常桩皿。
由于 connect() 為阻塞調用,因此該連接過程應始終在主 Activity 線程以外的線程中執(zhí)行幢炸。
注:在調用 connect() 時泄隔,應始終確保設備未在執(zhí)行設備發(fā)現(xiàn)。 如果正在進行發(fā)現(xiàn)操作宛徊,則會大幅降低連接嘗試的速度佛嬉,并增加連接失敗的可能性逻澳。
示例
以下是發(fā)起藍牙連接的線程的基本示例:
private class ConnectThread extends Thread {
private final BluetoothSocket mmSocket;
private final BluetoothDevice mmDevice;
public ConnectThread(BluetoothDevice device) {
// Use a temporary object that is later assigned to mmSocket,
// because mmSocket is final
BluetoothSocket tmp = null;
mmDevice = device;
// Get a BluetoothSocket to connect with the given BluetoothDevice
try {
// MY_UUID is the app's UUID string, also used by the server code
tmp = device.createRfcommSocketToServiceRecord(MY_UUID);
} catch (IOException e) { }
mmSocket = tmp;
}
public void run() {
// Cancel discovery because it will slow down the connection
mBluetoothAdapter.cancelDiscovery();
try {
// Connect the device through the socket. This will block
// until it succeeds or throws an exception
mmSocket.connect();
} catch (IOException connectException) {
// Unable to connect; close the socket and get out
try {
mmSocket.close();
} catch (IOException closeException) { }
return;
}
// Do work to manage the connection (in a separate thread)
manageConnectedSocket(mmSocket);
}
/** Will cancel an in-progress connection, and close the socket */
public void cancel() {
try {
mmSocket.close();
} catch (IOException e) { }
}
}
請注意,在建立連接之前會調用 cancelDiscovery()暖呕。 在進行連接之前應始終執(zhí)行此調用斜做,而且調用時無需實際檢查其是否正在運行(但如果您確實想要執(zhí)行檢查,請調用 isDiscovering())湾揽。
manageConnectedSocket()
是應用中的虛構方法瓤逼,它將啟動用于傳輸數(shù)據(jù)的線程,在有關管理連接的部分將會討論這一主題库物。
在完成 BluetoothSocket 后抛姑,應始終調用 close() 以執(zhí)行清理操作。這樣做會立即關閉已連接的套接字并清理所有內(nèi)部資源艳狐。
管理連接
在成功連接兩臺(或更多臺)設備后定硝,每臺設備都會有一個已連接的 BluetoothSocket。 這一點非常有趣毫目,因為這表示您可以在設備之間共享數(shù)據(jù)蔬啡。 利用 BluetoothSocket,傳輸任意數(shù)據(jù)的一般過程非常簡單:
1. 獲取 InputStream 和 OutputStream镀虐,二者分別通過套接字以及 getInputStream() 和 getOutputStream() 來處理數(shù)據(jù)傳輸箱蟆。
2. 使用read(byte[])和 write(byte[])讀取數(shù)據(jù)并寫入到流式傳輸。
就這么簡單刮便。
當然空猜,還需要考慮實現(xiàn)細節(jié)。首要的是恨旱,應該為所有流式傳輸讀取和寫入操作使用專門的線程辈毯。 這一點很重要,因為 read(byte[])和 write(byte[]) 方法都是阻塞調用搜贤。read(byte[])將會阻塞谆沃,直至從流式傳輸中讀取內(nèi)容。write(byte[])通常不會阻塞仪芒,但如果遠程設備沒有足夠快地調用 read(byte[])并且中間緩沖區(qū)已滿唁影,則其可能會保持阻塞狀態(tài)以實現(xiàn)流量控制。因此掂名,線程中的主循環(huán)應專門用于讀取 InputStream据沈。 可使用線程中單獨的公共方法來發(fā)起對 OutputStream 的寫入操作。
示例
以下是可能的顯示內(nèi)容示例:
private class ConnectedThread extends Thread {
private final BluetoothSocket mmSocket;
private final InputStream mmInStream;
private final OutputStream mmOutStream;
public ConnectedThread(BluetoothSocket socket) {
mmSocket = socket;
InputStream tmpIn = null;
OutputStream tmpOut = null;
// Get the input and output streams, using temp objects because
// member streams are final
try {
tmpIn = socket.getInputStream();
tmpOut = socket.getOutputStream();
} catch (IOException e) { }
mmInStream = tmpIn;
mmOutStream = tmpOut;
}
public void run() {
byte[] buffer = new byte[1024]; // buffer store for the stream
int bytes; // bytes returned from read()
// Keep listening to the InputStream until an exception occurs
while (true) {
try {
// Read from the InputStream
bytes = mmInStream.read(buffer);
// Send the obtained bytes to the UI activity
mHandler.obtainMessage(MESSAGE_READ, bytes, -1, buffer)
.sendToTarget();
} catch (IOException e) {
break;
}
}
}
/* Call this from the main activity to send data to the remote device */
public void write(byte[] bytes) {
try {
mmOutStream.write(bytes);
} catch (IOException e) { }
}
/* Call this from the main activity to shutdown the connection */
public void cancel() {
try {
mmSocket.close();
} catch (IOException e) { }
}
}
構造函數(shù)獲取必要的流式傳輸饺蔑,并且一旦執(zhí)行锌介,線程將會等待通過 InputStream 傳入的數(shù)據(jù)。 當 read(byte[])返回流式傳輸中的字節(jié)時膀钠,將使用來自父類的成員處理程序將數(shù)據(jù)發(fā)送到主 Activity掏湾。 然后該方法返回并等待流式傳輸提供更多字節(jié)裹虫。
發(fā)送傳出數(shù)據(jù)不外乎從主 Activity 調用線程的 write()
方法,并傳入要發(fā)送的字節(jié)融击。 然后筑公,此方法直接調用 write(byte[]),將數(shù)據(jù)發(fā)送到遠程設備尊浪。
線程的 cancel()
方法很重要匣屡,它能通過關閉 BluetoothSocket隨時終止連接。當您結束藍牙連接的使用時拇涤,應始終調用此方法捣作。
有關使用 Bluetooth API 的演示,請參閱藍牙聊天示例應用鹅士。
使用配置文件
從 Android 3.0 開始券躁,Bluetooth API 便支持使用藍牙配置文件。 藍牙配置文件是適用于設備間藍牙通信的無線接口規(guī)范掉盅。 免提配置文件便是一個示例也拜。 對于連接到無線耳機的手機,兩臺設備都必須支持免提配置文件趾痘。
您可以實現(xiàn)接口 BluetoothProfile慢哈,通過寫入自己的類來支持特定的藍牙配置文件。 Android Bluetooth API 提供了以下藍牙配置文件的實現(xiàn):
- 耳機永票。耳機配置文件提供了藍牙耳機支持卵贱,以便與手機配合使用。 Android 提供了 BluetoothHeadset 類侣集,它是用于通過進程間通信 (IPC) 來控制藍牙耳機服務的代理键俱。 這包括藍牙耳機和免提(1.5 版)配置文件。BluetoothHeadset 類包含 AT 命令支持肚吏。 有關此主題的詳細討論方妖,請參閱供應商特定的 AT 命令
- A2DP。高級音頻分發(fā)配置文件 (A2DP) 定義了高質量音頻如何通過藍牙連接和流式傳輸罚攀,從一個設備傳輸?shù)搅硪粋€設備。 Android 提供了 BluetoothA2dp類雌澄,它是用于通過 IPC 來控制藍牙 A2DP 服務的代理斋泄。
- 健康設備。Android 4.0(API 級別 14)引入了對藍牙健康設備配置文件 (HDP) 的支持镐牺。 這允許您創(chuàng)建應用炫掐,使用藍牙與支持藍牙功能的健康設備進行通信,例如心率監(jiān)測儀睬涧、血糖儀募胃、溫度計旗唁、臺秤等等。 有關支持的設備列表及其相應的設備數(shù)據(jù)專業(yè)化代碼痹束,請參閱 www.bluetooth.org 上的藍牙分配編號检疫。 請注意,這些值在 ISO/IEEE 11073-20601 [7] 規(guī)范的“命名法規(guī)附錄”中也被稱為 MDC_DEV_SPEC_PROFILE_*祷嘶。 有關 HDP 的詳細討論屎媳,請參閱健康設備配置文件。
以下是使用配置文件的基本步驟:
1. 獲取默認適配器(請參閱設置藍牙)论巍。
2. 使用 getProfileProxy() 建立到配置文件所關聯(lián)的配置文件代理對象的連接烛谊。在以下示例中,配置文件代理對象是一個 BluetoothHeadset的實例嘉汰。
3. 設置 BluetoothProfile.ServiceListener丹禀。此偵聽程序會在 BluetoothProfile IPC 客戶端連接到服務或斷開服務連接時向其發(fā)送通知。
4. 在 onServiceConnected()中鞋怀,獲取配置文件代理對象的句柄双泪。
5. 獲得配置文件代理對象后,可以立即將其用于監(jiān)視連接狀態(tài)和執(zhí)行其他與該配置文件相關的操作接箫。
例如攒读,以下代碼片段顯示了如何連接到 BluetoothHeadset 代理對象,以便能夠控制耳機配置文件:
BluetoothHeadset mBluetoothHeadset;
// Get the default adapter
BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
// Establish connection to the proxy.
mBluetoothAdapter.getProfileProxy(context, mProfileListener, BluetoothProfile.HEADSET);
private BluetoothProfile.ServiceListener mProfileListener = new BluetoothProfile.ServiceListener() {
public void onServiceConnected(int profile, BluetoothProfile proxy) {
if (profile == BluetoothProfile.HEADSET) {
mBluetoothHeadset = (BluetoothHeadset) proxy;
}
}
public void onServiceDisconnected(int profile) {
if (profile == BluetoothProfile.HEADSET) {
mBluetoothHeadset = null;
}
}
};
// ... call functions on mBluetoothHeadset
// Close proxy connection after use.
mBluetoothAdapter.closeProfileProxy(mBluetoothHeadset);
從 Android 3.0 開始辛友,應用可以注冊接收耳機所發(fā)送的預定義的供應商特定 AT 命令的系統(tǒng)廣播(例如 Plantronics +XEVENT 命令)薄扁。 例如,應用可以接收指示所連接設備的電池電量的廣播废累,并根據(jù)需要通知用戶或采取其他操作邓梅。 為 ACTION_VENDOR_SPECIFIC_HEADSET_EVENT intent 創(chuàng)建廣播接收器,以處理耳機的供應商特定 AT 命令邑滨。
健康設備配置文件
Android 4.0(API 級別 14)引入了對藍牙健康設備配置文件 (HDP) 的支持日缨。 這允許您創(chuàng)建應用,使用藍牙與支持藍牙功能的健康設備進行通信掖看,例如心率監(jiān)測儀匣距、血糖儀、溫度計哎壳、臺秤等等毅待。 Bluetooth Health API 包括類 BluetoothHealth、BluetoothHealthCallback 和 BluetoothHealthAppConfiguration归榕,在基礎知識部分介紹了這些類尸红。
在使用 Bluetooth Health API 時,了解以下關鍵 HDP 概念很有幫助:
| 概念 | 說明 |
| 源設備 | 在 HDP 中定義的角色。源設備是將醫(yī)療數(shù)據(jù)傳輸?shù)?Android 手機或平板電腦等智能設備的健康設備(體重秤外里、血糖儀怎爵、溫度計等)。 |
| 匯集設備 | 在 HDP 中定義的角色盅蝗。在 HDP 中鳖链,匯集設備是接收醫(yī)療數(shù)據(jù)的智能設備。 在 Android HDP 應用中风科,匯集設備表示為 BluetoothHealthAppConfiguration 對象撒轮。 |
| 注冊 | 指的是注冊特定健康設備的匯集設備。 |
| 連接 | 指的是開放健康設備與 Android 手機或平板電腦等智能設備之間的通道贼穆。 |
創(chuàng)建 HDP 應用
以下是創(chuàng)建 Android HDP 應用所涉及的基本步驟:
1. 獲取 BluetoothHealth代理對象的引用题山。
與常規(guī)耳機和 A2DP 配置文件設備相似,您必須使用 BluetoothProfile.ServiceListener 和HEALTH 配置文件類型來調用 getProfileProxy()故痊,以便與配置文件代理對象建立連接顶瞳。
2. 創(chuàng)建 BluetoothHealthCallback 并注冊充當健康匯集設備的應用配置 (BluetoothHealthAppConfiguration)。
3. 建立到健康設備的連接愕秫。一些設備將會發(fā)起該連接慨菱。 對于這類設備,無需執(zhí)行該步驟戴甩。
4. 成功連接到健康設備后符喝,使用文件描述符對健康設備執(zhí)行讀/寫操作。
接收的數(shù)據(jù)需要使用實現(xiàn)了 IEEE 11073-xxxxx 規(guī)范的健康管理器進行解釋甜孤。
5. 完成后协饲,關閉健康通道并取消注冊該應用。該通道在長期閑置時也會關閉缴川。
有關描述上述步驟的完整代碼示例茉稠,請參閱藍牙 HDP(健康設備配置文件)。