時(shí)間好快,轉(zhuǎn)眼又一個(gè)月了,每天工作很忙,也沒(méi)有什么時(shí)間學(xué)習(xí),下一篇打算研究一下 framwork 的一些東西。
藍(lán)牙
Android 平臺(tái)包含藍(lán)牙網(wǎng)絡(luò)堆棧支持,憑借此項(xiàng)支持,設(shè)備能以無(wú)線方式與其他藍(lán)牙設(shè)備交換數(shù)據(jù)。應(yīng)用框架提供了通過(guò) Android Bluetooth API 訪問(wèn)藍(lán)牙功能的途徑。 這些 API 允許應(yīng)用以無(wú)線方式連接到其他藍(lán)牙設(shè)備,從而實(shí)現(xiàn)點(diǎn)到點(diǎn)和多點(diǎn)無(wú)線功能。
傳統(tǒng)藍(lán)牙
使用藍(lán)牙進(jìn)行通信的四項(xiàng)主要任務(wù):
- 設(shè)置藍(lán)牙
- 查找局部區(qū)域內(nèi)的配對(duì)設(shè)備或可用設(shè)備
- 連接設(shè)備
- 在設(shè)備之間傳輸數(shù)據(jù)
1、聲明權(quán)限
<manifest ... >
<uses-permission android:name="android.permission.BLUETOOTH" />
...
</manifest>
2、設(shè)置藍(lán)牙
- a、獲取 BluetoothAdapter
BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (mBluetoothAdapter == null) {
// Device does not support Bluetooth
}
- b、啟用藍(lán)牙
if (!mBluetoothAdapter.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}
您的應(yīng)用還可以選擇偵聽(tīng) ACTION_STATE_CHANGED 廣播 Intent,每當(dāng)藍(lán)牙狀態(tài)發(fā)生變化時(shí),系統(tǒng)都會(huì)廣播此 Intent。 此廣播包含額外字段
- EXTRA_STATE : 新的藍(lán)牙狀態(tài)
- EXTRA_PREVIOUS_STATE : 舊的藍(lán)牙狀態(tài)
這些額外字段可能的值包括 :
- STATE_TURNING_ON
- STATE_ON
- STATE_TURNING_OFF
- STATE_OFF
偵聽(tīng)此廣播適用于檢測(cè)在您的應(yīng)用運(yùn)行期間對(duì)藍(lán)牙狀態(tài)所做的更改。
3、查找設(shè)備
設(shè)備發(fā)現(xiàn)是一個(gè)掃描過(guò)程,它會(huì)搜索局部區(qū)域內(nèi)已啟用藍(lán)牙功能的設(shè)備,然后請(qǐng)求一些關(guān)于各臺(tái)設(shè)備的信息。
但局部區(qū)域內(nèi)的藍(lán)牙設(shè)備僅在其當(dāng)前已啟用可檢測(cè)性時(shí)才會(huì)響應(yīng)發(fā)現(xiàn)請(qǐng)求。
利用此信息,執(zhí)行發(fā)現(xiàn)的設(shè)備可以選擇發(fā)起到被發(fā)現(xiàn)設(shè)備的連接。
請(qǐng)記住,被配對(duì)與被連接之間存在差別。被配對(duì)意味著兩臺(tái)設(shè)備知曉彼此的存在,具有可用于身份驗(yàn)證的共享鏈路密鑰,并且能夠與彼此建立加密連接。 被連接意味著設(shè)備當(dāng)前共享一個(gè) RFCOMM 通道,并且能夠向彼此傳輸數(shù)據(jù)。 當(dāng)前的 Android Bluetooth API 要求對(duì)設(shè)備進(jìn)行配對(duì),然后才能建立 RFCOMM 連接。
- a、查詢配對(duì)的設(shè)備
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());
}
}
- b、發(fā)現(xiàn)設(shè)備
要開(kāi)始發(fā)現(xiàn)設(shè)備,只需調(diào)用 startDiscovery()。該進(jìn)程為異步進(jìn)程,并且該方法會(huì)立即返回一個(gè)布爾值,指示是否已成功啟動(dòng)發(fā)現(xiàn)操作。
針對(duì) ACTION_FOUND Intent 注冊(cè)一個(gè) BroadcastReceiver,以便接收每臺(tái)發(fā)現(xiàn)的設(shè)備的相關(guān)信息。 針對(duì)每臺(tái)設(shè)備,系統(tǒng)將會(huì)廣播 ACTION_FOUND Intent。此 Intent 將攜帶額外字段
- EXTRA_DEVICE // 包含 BluetoothDevice
- EXTRA_CLASS // 包含 BluetoothClass
// 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
- c、啟用可檢測(cè)性
如果您希望將本地設(shè)備設(shè)為可被其他設(shè)備檢測(cè)到,請(qǐng)使用
ACTION_REQUEST_DISCOVERABLE
操作 Intent ,您可以通過(guò)添加 :
EXTRA_DISCOVERABLE_DURATION
Intent Extra 來(lái)定義不同的持續(xù)時(shí)間。 應(yīng)用可以設(shè)置的最大持續(xù)時(shí)間為 3600 秒,值為 0 則表示設(shè)備始終可檢測(cè)到。 任何小于 0 或大于 3600 的值都會(huì)自動(dòng)設(shè)為 120 秒。 例如,以下片段會(huì)將持續(xù)時(shí)間設(shè)為 300 秒:
Intent discoverableIntent = new
Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
如果您希望在可檢測(cè)到模式發(fā)生變化時(shí)收到通知,您可以針對(duì)
ACTION_SCAN_MODE_CHANGED Intent
注冊(cè) BroadcastReceiver。 它將包含額外字段
- EXTRA_SCAN_MODE
- EXTRA_PREVIOUS_SCAN_MODE
二者分別告知您新的和舊的掃描模式。 每個(gè)字段可能的值包括:
- SCAN_MODE_CONNECTABLE_DISCOVERABLE :可檢測(cè)到模式
- SCAN_MODE_CONNECTABLE :未處于可檢測(cè)到模式但仍能接收連接
- SCAN_MODE_NONE :未處于可檢測(cè)到模式并且無(wú)法接收連接
4、連接設(shè)備
要在兩臺(tái)設(shè)備上的應(yīng)用之間創(chuàng)建連接,必須同時(shí)實(shí)現(xiàn)服務(wù)器端和客戶端機(jī)制,因?yàn)槠渲幸慌_(tái)設(shè)備必須開(kāi)放服務(wù)器套接字,而另一臺(tái)設(shè)備必須發(fā)起連接(使用服務(wù)器設(shè)備的 MAC 地址發(fā)起連接)。 當(dāng)服務(wù)器和客戶端在同一 RFCOMM 通道上分別擁有已連接的 BluetoothSocket 時(shí),二者將被視為彼此連接。
藍(lán)牙套接字接口(與 TCP Socket 相似)。這是允許應(yīng)用通過(guò) InputStream 和 OutputStream 與其他藍(lán)牙設(shè)備交換數(shù)據(jù)的連接點(diǎn)。
- a、連接為服務(wù)器
以下是設(shè)置服務(wù)器套接字并接受連接的基本過(guò)程:
1、通過(guò)調(diào)用 listenUsingRfcommWithServiceRecord(String, UUID) 獲取 BluetoothServerSocket。
2、通過(guò)調(diào)用 accept() 開(kāi)始偵聽(tīng)連接請(qǐng)求。
3、除非您想要接受更多連接,否則請(qǐng)調(diào)用 close()。
這將釋放服務(wù)器套接字及其所有資源,但不會(huì)關(guān)閉 accept() 所返回的已連接的 BluetoothSocket。
accept() 調(diào)用不應(yīng)在主 Activity UI 線程中執(zhí)行,因?yàn)樗亲枞{(diào)用,并會(huì)阻止與應(yīng)用的任何其他交互。 在您的應(yīng)用所管理的新線程中使用 BluetoothServerSocket 或 BluetoothSocket 完成所有工作。
- b、連接為客戶端
以下是基本過(guò)程:
1、使用 BluetoothDevice,通過(guò)調(diào)用 createRfcommSocketToServiceRecord(UUID) 獲取 BluetoothSocket。
2、通過(guò)調(diào)用 connect() 發(fā)起連接。
系統(tǒng)將會(huì)在遠(yuǎn)程設(shè)備上執(zhí)行 SDP 查找,以便匹配 UUID。 如果查找成功并且遠(yuǎn)程設(shè)備接受了該連接,它將共享 RFCOMM 通道以便在連接期間使用,并且 connect() 將會(huì)返回。 此方法為阻塞調(diào)用。
- c、管理連接
在成功連接兩臺(tái)(或更多臺(tái))設(shè)備后,每臺(tái)設(shè)備都會(huì)有一個(gè)已連接的 BluetoothSocket。 這一點(diǎn)非常有趣,因?yàn)檫@表示您可以在設(shè)備之間共享數(shù)據(jù)。 利用 BluetoothSocket,傳輸任意數(shù)據(jù)的一般過(guò)程非常簡(jiǎn)單:
獲取 InputStream 和 OutputStream,二者分別通過(guò)套接字以及 getInputStream() 和 getOutputStream() 來(lái)處理數(shù)據(jù)傳輸。
使用 read(byte[]) 和 write(byte[]) 讀取數(shù)據(jù)并寫入到流式傳輸。
就這么簡(jiǎn)單。
5、使用配置文件
從 Android 3.0 開(kāi)始,Bluetooth API 便支持使用藍(lán)牙配置文件。 藍(lán)牙配置文件是適用于設(shè)備間藍(lán)牙通信的無(wú)線接口規(guī)范。
對(duì)于連接到無(wú)線耳機(jī)的手機(jī),兩臺(tái)設(shè)備都必須支持 “免提配置文件”。
Android Bluetooth API 提供了以下藍(lán)牙配置文件的實(shí)現(xiàn):
- 耳機(jī)
耳機(jī)配置文件提供了藍(lán)牙耳機(jī)支持,以便與手機(jī)配合使用。
- A2DP
高級(jí)音頻分發(fā)配置文件 (A2DP) 定義了高質(zhì)量音頻如何通過(guò)藍(lán)牙連接和流式傳輸,從一個(gè)設(shè)備傳輸?shù)搅硪粋€(gè)設(shè)備。 Android 提供了 BluetoothA2dp 類,它是用于通過(guò) IPC 來(lái)控制藍(lán)牙 A2DP 服務(wù)的代理。
- 健康設(shè)備
Android 4.0(API 級(jí)別 14)引入了對(duì)藍(lán)牙健康設(shè)備配置文件 (HDP) 的支持。 這允許您創(chuàng)建應(yīng)用,使用藍(lán)牙與支持藍(lán)牙功能的健康設(shè)備進(jìn)行通信,例如心率監(jiān)測(cè)儀、血糖儀、溫度計(jì)、臺(tái)秤等等。
以下是使用配置文件的基本步驟:
1、獲取默認(rèn)適配器(請(qǐng)參閱設(shè)置藍(lán)牙)。
2、使用 getProfileProxy() 建立到配置文件所關(guān)聯(lián)的配置文件代理對(duì)象的連接。在以下示例中,配置文件代理對(duì)象是一個(gè) BluetoothHeadset 的實(shí)例。
3、設(shè)置 BluetoothProfile.ServiceListener。此偵聽(tīng)程序會(huì)在 BluetoothProfile IPC 客戶端連接到服務(wù)或斷開(kāi)服務(wù)連接時(shí)向其發(fā)送通知。
4、在 onServiceConnected() 中,獲取配置文件代理對(duì)象的句柄。
5、獲得配置文件代理對(duì)象后,可以立即將其用于監(jiān)視連接狀態(tài)和執(zhí)行其他與該配置文件相關(guān)的操作。
以下代碼片段顯示了如何連接到 BluetoothHeadset 代理對(duì)象,以便能夠控制耳機(jī)配置文件:
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);
供應(yīng)商特定的 AT 命令
從 Android 3.0 開(kāi)始,應(yīng)用可以注冊(cè)接收耳機(jī)所發(fā)送的預(yù)定義的供應(yīng)商特定 AT 命令的系統(tǒng)廣播(例如 Plantronics +XEVENT 命令)。
例如,應(yīng)用可以接收指示所連接設(shè)備的電池電量的廣播,并根據(jù)需要通知用戶或采取其他操作。 為
ACTION_VENDOR_SPECIFIC_HEADSET_EVENT intent
創(chuàng)建廣播接收器,以處理耳機(jī)的供應(yīng)商特定 AT 命令。
下面是獲取藍(lán)牙耳機(jī)電池電量:
/**
* Handle {@link BluetoothHeadset#ACTION_VENDOR_SPECIFIC_HEADSET_EVENT} intent
* @param intent must be {@link BluetoothHeadset#ACTION_VENDOR_SPECIFIC_HEADSET_EVENT} intent
*/
@VisibleForTesting
void onVendorSpecificHeadsetEvent(Intent intent) {
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
if (device == null) {
Log.e(TAG, "onVendorSpecificHeadsetEvent() remote device is null");
return;
}
String cmd =
intent.getStringExtra(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD);
if (cmd == null) {
Log.e(TAG, "onVendorSpecificHeadsetEvent() command is null");
return;
}
int cmdType = intent.getIntExtra(
BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE, -1);
// Only process set command
if (cmdType != BluetoothHeadset.AT_CMD_TYPE_SET) {
debugLog("onVendorSpecificHeadsetEvent() only SET command is processed");
return;
}
Object[] args = (Object[]) intent.getExtras().get(
BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS);
if (args == null) {
Log.e(TAG, "onVendorSpecificHeadsetEvent() arguments are null");
return;
}
int batteryPercent = BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
switch (cmd) {
case BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_XEVENT:
batteryPercent = getBatteryLevelFromXEventVsc(args);
break;
case BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_IPHONEACCEV:
batteryPercent = getBatteryLevelFromAppleBatteryVsc(args);
break;
}
if (batteryPercent != BluetoothDevice.BATTERY_LEVEL_UNKNOWN) {
updateBatteryLevel(device, batteryPercent);
infoLog("Updated device " + device + " battery level to "
+ String.valueOf(batteryPercent) + "%");
}
}
健康設(shè)備配置文件
源設(shè)備:在 HDP 中定義的角色。源設(shè)備是將醫(yī)療數(shù)據(jù)傳輸?shù)?Android 手機(jī)或平板電腦等智能設(shè)備的健康設(shè)備(體重秤、血糖儀、溫度計(jì)等)。
匯集設(shè)備:在 HDP 中定義的角色。在 HDP 中,匯集設(shè)備是接收醫(yī)療數(shù)據(jù)的智能設(shè)備。 在 Android HDP 應(yīng)用中,匯集設(shè)備表示為 BluetoothHealthAppConfiguration 對(duì)象。
注冊(cè):指的是注冊(cè)特定健康設(shè)備的匯集設(shè)備。
連接:指的是開(kāi)放健康設(shè)備與 Android 手機(jī)或平板電腦等智能設(shè)備之間的通道。
創(chuàng)建 HDP 應(yīng)用
以下是創(chuàng)建 Android HDP 應(yīng)用所涉及的基本步驟:
1、獲取 BluetoothHealth 代理對(duì)象的引用。
2、與常規(guī)耳機(jī)和 A2DP 配置文件設(shè)備相似,您必須使用 BluetoothProfile.ServiceListener 和 HEALTH 配置文件類型來(lái)調(diào)用 getProfileProxy(),以便與配置文件代理對(duì)象建立連接。
3、創(chuàng)建 BluetoothHealthCallback 并注冊(cè)充當(dāng)健康匯集設(shè)備的應(yīng)用配置 (BluetoothHealthAppConfiguration)。
4、建立到健康設(shè)備的連接。一些設(shè)備將會(huì)發(fā)起該連接。 對(duì)于這類設(shè)備,無(wú)需執(zhí)行該步驟。
成功連接到健康設(shè)備后,使用文件描述符對(duì)健康設(shè)備執(zhí)行讀/寫操作。
5、接收的數(shù)據(jù)需要使用實(shí)現(xiàn)了 IEEE 11073-xxxxx 規(guī)范的健康管理器進(jìn)行解釋。
完成后,關(guān)閉健康通道并取消注冊(cè)該應(yīng)用。該通道在長(zhǎng)期閑置時(shí)也會(huì)關(guān)閉。