在 android 系統(tǒng)上通過藍(lán)牙獲取通訊錄
*本篇文章已授權(quán)微信公眾號(hào) guolin_blog (郭霖)獨(dú)家發(fā)布
前言:
最近在研究通過藍(lán)牙通訊協(xié)議中的 PhoneBookAccessProfile(簡(jiǎn)稱 PBAP) 來獲取其它智能手機(jī)中的通訊錄。我們的目標(biāo)是,在 android 端運(yùn)行程序,該程序通過某個(gè)藍(lán)牙協(xié)議通過無線的方式獲取另一個(gè)手機(jī)上的通訊錄。這里,“另一個(gè)手機(jī)”可以是 android 設(shè)備也可以是 ios 設(shè)備。這也是我們選擇采用藍(lán)牙協(xié)議的一個(gè)原因:在某種程度上跨平臺(tái)。
幾種常見的藍(lán)牙協(xié)議:
| Protocol | Abbreviation | Benefit |
|---|---|---|
| Advanced Audio Distribution Protocol | A2DP | Audio streaming |
| Audio/Video Remote Control Protocol | AVRCP | Control over music playback directly from the stereo |
| Hands-free Profile | HFP | Hands-free calling through the stereo |
| Object Push Profile | OPP | Uploading of contact info to the stereo |
| Phone Book Access Profile | PBAP | Access to contact list from the stereo |
表格中的 stereo 其實(shí)泛指我們使用的藍(lán)牙設(shè)備。根據(jù)表格中的描述,想利用藍(lán)牙協(xié)議來獲取其他設(shè)備中的通訊錄,用 PhoneBookAccessProfile(PBAP) 協(xié)議就可以了。先不考慮代碼邏輯,通訊錄的獲取流程如下:
- 兩個(gè)藍(lán)牙設(shè)備配對(duì)
- 一端發(fā)起獲取通訊錄的請(qǐng)求
- 另一端會(huì)以某種方式(最常見的是彈窗)來請(qǐng)求本人授權(quán)
- 發(fā)起請(qǐng)求端獲取通訊錄數(shù)據(jù)
事實(shí)上,藍(lán)牙連接中,兩個(gè)設(shè)備間是不對(duì)等、不對(duì)稱的。一定會(huì)出現(xiàn)一個(gè)設(shè)備扮演客戶端的角色,另一個(gè)設(shè)備扮演服務(wù)端的角色??蛻舳诵枰龅木褪侨グl(fā)出某種請(qǐng)求,然后接受并處理服務(wù)端的返回?cái)?shù)據(jù);服務(wù)端需要做的就是一直監(jiān)聽是否有請(qǐng)求發(fā)過來,然后對(duì)請(qǐng)求做出響應(yīng)。整個(gè)機(jī)制跟 web 開發(fā)的流程是極其相似的,是可以無縫類比的。
android 對(duì)于藍(lán)牙開發(fā)的支持
android 本身對(duì)于藍(lán)牙協(xié)議進(jìn)行了高度的封裝。不過有的代碼可以在 android 官方文檔中查看到相關(guān)解釋,有的確只能自己翻代碼去看。
The BluetoothAdapter is the entry-point for all Bluetooth interaction. 正如文檔中所言,首先我們需要調(diào)用
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
來獲得 BluetoothAdapter 實(shí)例,然后需要通過這個(gè)實(shí)例獲取 BluetoothDevice 實(shí)例。這里做類似的調(diào)用:
Set<BluetoothDevice> pairedDevices = adapter.getBondedDevices();
BluetoothDevice device = (BluetoothDevice) pairedDevices.toArray()[0];
首先通過 getBondedDevices() 來獲得一個(gè)已配對(duì)的設(shè)備的集合 pairedDevices。然后可以調(diào)用paredDevices.toArray()將集合轉(zhuǎn)化成數(shù)組。數(shù)組里面的每一項(xiàng)都是一個(gè) BluetoothDevice 實(shí)例。針對(duì) android 開發(fā),一般情況下會(huì)顯示一個(gè)列表,列表里面的數(shù)據(jù)源自device.getAddress() 以及 device.getName(),分別代表藍(lán)牙設(shè)備的地址以及名字。然后讓用戶手動(dòng)選擇一個(gè)藍(lán)牙設(shè)備去連接。在 onClick() 事件中,可以調(diào)用 adapter.getRemoteDevie(String address) 來通過藍(lán)牙設(shè)備地址來訪問該設(shè)備。
獲取到 BluetoothDevice 實(shí)例后,可以調(diào)用
BluetoothPbapClient client = new BluetoothPbapClient(device, yourHandler);
client.connect();
client.pullPhoneBook(BluetoothPbapClient.PB_PATH);
client.disconnect();
來完成通訊錄的獲取。其中,new BluetoothPbapClient(device, yourHandler);的 yourHandler 參數(shù)需要傳一個(gè) Handler 實(shí)例。利用 Handler 處理信息在 android 開發(fā)中已經(jīng)非常常見了。在傳入 yourHandler 實(shí)例之后,你就可以在它的 handleMessage(Message msg)函數(shù)中處理 BluetoothPbapClient 實(shí)例在工作的過程中發(fā)出的消息。比如,接收到 EVENT_PULL_PHONE_BOOK_DONE 信息后,輸出一下通訊錄的內(nèi)容或者基于通訊錄做一些其他事情等。代碼如下:
public static class BluetoothServiceHandler extends Handler {
@Override
public void handleMessage(Message msg) {
LogUtil.d(msg);
switch (msg.what) {
case BluetoothPbapClient.EVENT_PULL_PHONE_BOOK_DONE: {
LogUtil.d("EVENT_PULL_PHONE_BOOK_DONE");
sPbapClient.disconnect();
}
break;
default: {
}
}
}
}
注意:關(guān)于 BluetoothPbapClient 相關(guān)的類,需要在 android 源代碼中找,找到后直接 copy 到我們的工程下就好。需要用到的有:android.bluetooth.client.pbap.*;,com.android.vcard.*,javax.obex.*。而且,這里有個(gè)小坑,就是低版本的 sdk 里面,相關(guān)的源代碼寫的有問題,需要到高版本的里面找,才能正確編譯。本人用的是 android-23 里面的文件。