1.簡介
通過藍(lán)牙API拖叙,可以實現(xiàn)以下內(nèi)容:
掃描其他藍(lán)牙設(shè)備
查詢配對藍(lán)牙設(shè)備的本地藍(lán)牙適配器
創(chuàng)建RFCOMM通道
通過服務(wù)發(fā)現(xiàn)連接其他設(shè)備
與其他設(shè)備進(jìn)行數(shù)據(jù)交互
管理多連接
2.基本要素
藍(lán)牙相關(guān)的API都存在android.bluetooth包內(nèi),主要包括以下幾個類和接口:
代表本地藍(lán)牙適配器慕购。BluetoothAdapter是所有藍(lán)牙設(shè)備交互的入口繁莹≌芍龋可以實現(xiàn)查找設(shè)備狮辽、遍歷配對設(shè)備仅乓、通過已知的MAC地址實例化BluetoothDevice赖舟、創(chuàng)建BluetoothServerSocket與其他設(shè)備進(jìn)行通信。
代表一個遠(yuǎn)程藍(lán)牙設(shè)備方灾〗ㄌ悖可用于,通過BluetoothSocket請求與遠(yuǎn)程設(shè)備的連接裕偿,或者查詢設(shè)備信息(名字洞慎、地址、級別與配對狀態(tài))嘿棘。
代表藍(lán)牙socket接口(類似于TCP Socket)劲腿。這是允許應(yīng)用與其他藍(lán)牙設(shè)備進(jìn)行數(shù)據(jù)交換的連接點,采用InputStream和OutputStream鸟妙。
代表監(jiān)聽外來請求的開放服務(wù)socket(類似于TCP ServerSocket)焦人。為連接兩個Android設(shè)備挥吵,其中一個設(shè)備用這個類必須開放服務(wù)socket。當(dāng)遠(yuǎn)程藍(lán)牙設(shè)備向此設(shè)備進(jìn)行連接請求時花椭,當(dāng)連接被接受時忽匈,BluetoothServerSocket會返回一個連接的BluetoothSocket。
描述一個藍(lán)牙設(shè)備的基本特點和性能矿辽。它是一個只讀的屬性集合丹允,定義了設(shè)備主要和次要的級別和服務(wù)。然而袋倔,它并沒有完全描述設(shè)備所支持的所有的藍(lán)牙配置文件和服務(wù)雕蔽,但是,對于設(shè)備類型來說是非常有用的宾娜。
代表藍(lán)牙協(xié)議的接口批狐。是設(shè)備間基于藍(lán)牙通信的無線接口說明。例如Hands-Free Profile (HFP)前塔。
提供用于手機(jī)的藍(lán)牙耳機(jī)支持嚣艇。包括Bluetooth Headset和Hands-Free (v1.5) 協(xié)議。
定義高質(zhì)量音頻可以從一個設(shè)備通過藍(lán)牙連接傳輸?shù)搅硪粋€設(shè)備嘱根。"A2DP" 代表Advanced Audio Distribution Profile.
代表健康設(shè)備配置文件協(xié)議髓废,控制藍(lán)牙設(shè)備。HDP Health Device Profile.
一個抽象類该抒,用于實現(xiàn)BluetoothHealth回調(diào)慌洪。必須繼承這個類并實現(xiàn)回調(diào)方法來接受應(yīng)用注冊狀態(tài)和藍(lán)牙通道狀態(tài)改變。
BluetoothHealthAppConfiguration
代表一個藍(lán)牙健康的第三方應(yīng)用與遠(yuǎn)程藍(lán)牙健康設(shè)備注冊通信應(yīng)用配置凑保。
BluetoothProfile.ServiceListener
一個當(dāng)服務(wù)(運行特殊配置文件的內(nèi)部服務(wù))連接與斷開時用于通知BluetoothProfileIPC代理的接口冈爹。
3.權(quán)限
<manifest...>
<uses-permissionandroid:name="android.permission.BLUETOOTH"/>
<uses-permissionandroid:name="android.permission. BLUETOOTH_ADMIN"/>
</manifest>
4.配置藍(lán)牙
使用藍(lán)牙進(jìn)行通信前,必須驗證設(shè)備支持藍(lán)牙欧引,并且藍(lán)牙可用频伤。主要分為兩步:
獲取BluetoothAdapter
BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (mBluetoothAdapter == null) {
// Device does not support Bluetooth
}
使藍(lán)牙可用
private static final int REQUEST_ENABLE_BT = 1024;
if (!mBluetoothAdapter.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}
protect void onActivityResult(int requestCode, int resultCode, Intent data){
if(requestCode == REQUEST_ENABLE_BT){
if(resultCode == RESULT_OK)? // YES 用戶允許
if(resultCode == RESULT_CANCELED)? // NO 用戶取消
}
}
監(jiān)聽藍(lán)牙狀態(tài)改變
String ACTION_STATE_CHANGED = "android.bluetooth.adapter.action.STATE_CHANGED"
String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.headset.profile.action.CONNECTION_STATE_CHANGED"
String ACTION_AUDIO_STATE_CHANGED =
"android.bluetooth.headset.profile.action.AUDIO_STATE_CHANGED"
常量在BluetoothAdapter中定義。
當(dāng)本地藍(lán)牙適配器的狀態(tài)改變時芝此,采用此廣播發(fā)送憋肖。intent中包含兩個狀態(tài)EXTRA_STATE和EXTRA_PREVIOUS_STATE,分別表示當(dāng)前狀態(tài)和上一個狀態(tài)婚苹。
可能包含 的值為:STATE_OFF,STATE_TURNING_ON,STATE_ON,STATE_TURNING_OFF岸更。
5.查找設(shè)備
用BluetoothAdapter可以查詢遠(yuǎn)程藍(lán)牙設(shè)備,也可以獲取配對設(shè)備列表膊升。
獲取配對列表
SetpairedDevices = 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());
}
}
搜索設(shè)備
調(diào)用boolean startDiscovery ()開始搜索怎炊,正確開始搜索時返回true。然后采用廣播監(jiān)聽的形式獲取藍(lán)牙設(shè)備。
調(diào)用boolean cancelDiscovery ()停止搜索评肆,一般在有遠(yuǎn)程設(shè)備連接時調(diào)用债查。
// 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ā)現(xiàn)
private static final int REQUEST_BT_DISCOVERABLE = 102;
Intent discoverableIntent = new
Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
startActivityForResult(discoverableIntent,REQUEST_BT_DISCOVERABLE);
最大可查找時間為1小時,如果設(shè)為0時表示一直能被發(fā)現(xiàn)瓜挽。
在onActivityResult()中監(jiān)聽返回值盹廷,如果resultCode為間隔值,說明返回成功秸抚,如果返回碼為RESULT_CANCELED速和,返回失敗。
6.連接設(shè)備
要在兩個設(shè)備間創(chuàng)建連接剥汤,需要同時實現(xiàn)服務(wù)端和客戶端機(jī)制,因為一個設(shè)備必須打開服務(wù)socket排惨,另一個必須初始化連接(用服務(wù)端設(shè)備的MAC地址進(jìn)行初始化連接)吭敢。當(dāng)服務(wù)端和客戶端之間在同一個RFCOMM通道存在一個連接的BluetoothSocket時被認(rèn)為是相互連接的。只有這樣暮芭,每一個設(shè)備可以獲取輸入輸出流并數(shù)據(jù)交換鹿驼。
服務(wù)端和客戶端可以采用不同的方式獲取需要的BluetoothSocket。服務(wù)端接受外來連接辕宏,接收到BluetoothSocket畜晰。客戶端想服務(wù)端打開一個RFCOMM通道時瑞筐,接收到BluetoothSocket凄鼻。
作為服務(wù)端連接
1.調(diào)用listenUsingRfcommWithServiceRecord(String, UUID)獲取BluetoothServerSocket
String 表示服務(wù)的名字;UUID代表Universally Unique Identifier聚假。
2.調(diào)用accept()監(jiān)聽連接請求
如果成功块蚌,將返回一個連接的BluetoothSocket
3.要接受其他連接是,調(diào)用close()
4.樣例
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) { }
}
}
作為客戶端連接
1.調(diào)用BluetoothDevice的createRfcommSocketToServiceRecord(UUID)方法膘格,獲取BluetoothSocket峭范。
2.調(diào)用connect()初始化連接
3.樣例
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) { }
}
}
7.管理連接
調(diào)用socket的getInputStream()和getOutputStream()獲得輸入輸出流
通過read(byte[])和write(byte[])向流讀寫數(shù)據(jù)
樣例
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) { }
}
}
8.采用Profiles工作
android藍(lán)牙API提供下列協(xié)議的實現(xiàn)。
Headset:
BluetoothHeadset類瘪贱,通過IPC控制藍(lán)牙耳機(jī)服務(wù)的代理類纱控;包括Bluetooth Headset 和 Hands-Free (v1.5) profiles;包括對AT指令的支持菜秦,見第9甜害。
A2DP:
BluetoothA2dp類,通過IPC控制藍(lán)牙A2DP服務(wù)的代理類喷户。
Health Device:
BluetoothHealth類唾那,藍(lán)牙外接健康設(shè)備。
獲取默認(rèn)的藍(lán)牙適配器。
創(chuàng)建與協(xié)議對象的連接闹获。
設(shè)置BluetoothProfile.ServiceListener期犬。當(dāng)與服務(wù)連接或斷開時,監(jiān)聽器通知BluetoothProfileIPC代理避诽。
在onServiceConnected()中獲取協(xié)議代理對象龟虎。
一旦擁有了協(xié)議代理對象,就可以用它管理連接狀態(tài)沙庐,執(zhí)行與協(xié)議相關(guān)的其他操作鲤妥。
樣例
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);
Vendor-specific AT指令
從3.0開始,應(yīng)用可以注冊接收由headsets設(shè)備發(fā)送的預(yù)定義的AT指令系統(tǒng)廣播拱雏。例如棉安,應(yīng)用可以接收表明連接設(shè)備電量低的廣播并且通知用戶,或其他需要的行為铸抑」钡ⅲ可以創(chuàng)建一個廣播接收器用ACTION_VENDOR_SPECIFIC_HEADSET_EVENT作為intent的ACTION,處理headset設(shè)備的AT指令鹊汛。
String ACTION = “android.bluetooth.headset.action.VENDOR_SPECIFIC_HEADSET_EVENT”
Health Device Profile
創(chuàng)建一個HDP應(yīng)用
1.獲取BluetoothHealth協(xié)議對象的引用蒲赂。與HFP和A2DP類似,必須調(diào)用getProfileProxy()刁憋、BluetoothProfile.ServiceListener和HEALTH協(xié)議類型
滥嘴,建立協(xié)議代理對象的連接。
2.創(chuàng)建BluetoothHealthCallback回調(diào)和注冊應(yīng)用配置(BluetoothHealthAppConfiguration) 至耻。
3.建立與健康設(shè)備的連接若皱。
4.當(dāng)連接成功,采用文件描述器想設(shè)備進(jìn)行讀寫有梆。
5.結(jié)束后是尖,關(guān)閉通道,取消注冊應(yīng)用泥耀。