http://blog.csdn.net/pwei007/article/details/6015907html
Android平臺支持藍牙網絡協議棧,實現藍牙設備之間數據的無線傳輸。java
本文檔描述了怎樣利用android平臺提供的藍牙API去實現藍牙設備之間的通訊,藍牙設備之間的通訊主要包括了四個步驟:設置藍牙設備、尋找局域網內可能或者匹配的設備、鏈接設備和設備之間的數據傳輸。如下是創建藍牙鏈接的所須要的一些基本類:android
BluetoothAdapter類:表明了一個本地的藍牙適配器。他是全部藍牙交互的的入口點。利用它你能夠發現其餘藍牙設備,查詢綁定了的設備,使用已知的MAC地址實例化一個藍牙設備和創建一個BluetoothServerSocket(做爲服務器端)來監聽來自其餘設備的鏈接。編程
BluetoothDevice類:表明了一個遠端的藍牙設備,使用它請求遠端藍牙設備鏈接或者獲取遠端藍牙設備的名稱、地址、種類和綁定狀態。(其信息是封裝在bluetoothsocket中)。windows
Bluetoothsocket類:表明了一個藍牙套接字的接口(相似於tcp中的套接字),他是應用程序經過輸入、輸出流與其餘藍牙設備通訊的鏈接點。數組
Blueboothserversocket類:表明打開服務鏈接來監聽可能到來的鏈接請求(屬於server端),爲了鏈接兩個藍牙設備必須有一個設備做爲服務器打開一個服務套接字。當遠端設備發起鏈接鏈接請求的時候,而且已經鏈接到了的時候,Blueboothserversocket類將會返回一個bluetoothsocket。安全
Bluetoothclass類:描述了一個藍牙設備的通常特色和能力。他的只讀屬性集定義了設備的主、次設備類和一些相關服務。然而,他並無準確的描述全部該設備所支持的藍牙文件和服務,而是做爲對設備種類來講的一個小小暗示。服務器
下面說說具體的編程實現:網絡
必須肯定你的設備支持藍牙,並保證他能夠用。若是你的設備支持藍牙,將它使能。固然,有兩種方法,一種是在你的系統設置裏開啓藍牙,另一中是在你的應用程序裏啓動藍牙功能,第一種方法就不講了,具體講一個第二種方法:app
首先經過調用靜態方法getDefaultAdapter()獲取藍牙適配器bluetoothadapter,之後你就可使用該對象了。若是返回爲空,the story is over。
Eg:BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (mBluetoothAdapter == null) {
// Device does not support Bluetooth
}
其次,調用isEnabled()來查詢當前藍牙設備的狀態,若是返回爲false,則表示藍牙設備沒有開啓,接下來你須要封裝一個ACTION_REQUEST_ENABLE請求到intent裏面,調用startActivityForResult()方法使能藍牙設備,例如:
if (!mBluetoothAdapter.isEnabled()) {
Intent enableBtIntent = newIntent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}
至此,如不出意外,恭喜你的藍牙設備已經開啓了,接下來須要查找周邊可能存在的藍牙設備了。
查找設備:
使用bluetoothadapter類裏的方法,你能夠查找遠端設備(不過藍牙查找的範圍好像是在十米之內吧)或者查詢在你手機上已經匹配(或者說綁定)的其餘手機了。固然須要肯定對方藍牙設備已經開啓或者已經開啓了「被發現使能「功能(對方設備是能夠被發現的是你可以發起鏈接的前提條件)。若是該設備是能夠被發現的,會反饋回來一些對方的設備信息,好比名字、MAC地址等,利用這些信息,你的設備就能夠選擇去向對方初始化一個鏈接。
若是你是第一次與該設備鏈接,那麼一個配對的請求就會自動的顯示給用戶。當設備配對好以後,他的一些基本信息(主要是名字和MAC)被保存下來並可使用藍牙的API來讀取。使用已知的MAC地址就能夠對遠端的藍牙設備發起鏈接請求。
匹配好的設備和鏈接上的設備的不一樣點:匹配好只是說明對方設備發現了你的存在,並擁有一個共同的識別碼,而且能夠鏈接。鏈接上:表示當前設備共享一個RFCOMM信道而且二者之間能夠交換數據。也就是是說藍牙設備在創建RFCOMM信道以前,必須是已經配對好了的。
怎麼查詢匹配好的設備:
在創建鏈接以前你必須先查詢配對好了的藍牙設備集(你周圍的藍牙設備可能不止一個),以便你選取哪個設備進行通訊,例如你能夠你能夠查詢全部配對的藍牙設備,並使用一個數組適配器將其打印顯示出來:
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());
}
創建一個藍牙鏈接只須要MAC地址就已經足夠了。
掃描設備:
掃描設備,只須要簡單的調用startDiscovery()方法,這個掃描的過程大概持續是12秒,應用程序爲了ACTION_FOUND動做須要註冊一個BroadcastReceiver來接受設備掃描到的信息。對於每個設備,系統都會廣播ACTION_FOUND動做。例如:
// 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
注意:掃描的過程是一個很耗費資源的過程,一旦你找到你須要的設備以後,在發起鏈接請求以前,確保你的程序調用cancelDiscovery()方法中止掃描。顯然,若是你已經鏈接上一個設備,啓動掃描會減小你的通訊帶寬。
使能被發現:Enabling discoverability
若是你想使你的設備可以被其餘設備發現,將ACTION_REQUEST_DISCOVERABLE動做封裝在intent中並調用startActivityForResult(Intent, int)方法就能夠了。他將在不使你應用程序退出的狀況下使你的設備可以被發現。缺省狀況下的使能時間是120秒,固然你能夠能夠經過添加EXTRA_DISCOVERABLE_DURATION字段來改變使能時間(最大不超過300秒,這是出於對你設備上的信息安全考慮)。例如:
Intent discoverableIntent = new
Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
startActivity(discoverableIntent);
運行該段代碼以後,系統會彈出一個對話框來提示你啓動設備使能被發現(次過程當中若是你的藍牙功能沒有開啓,系統會幫你開啓),而且若是你準備對該遠端設備發現一個鏈接,你不須要開啓使能設備被發現功能,覺得該功能只是在你的應用程序做爲服務器端的時候才須要。
鏈接設備:
在你的應用程序中,想創建兩個藍牙設備之間的鏈接,你必須實現客戶端和服務器端的代碼(由於任何一個設備都必須能夠做爲服務端或者客戶端)。一個開啓服務來監聽,一個發起鏈接請求(使用服務器端設備的MAC地址)。當他們都擁有一個藍牙套接字在同一RFECOMM信道上的時候,能夠認爲他們之間已經鏈接上了。服務端和客戶端經過不一樣的方式或其餘們的藍牙套接字。當一個鏈接監聽到的時候,服務端獲取到藍牙套接字。當客戶可打開一個FRCOMM信道給服務器端的時候,客戶端獲取到藍牙套接字。
注意:在此過程當中,若是兩個藍牙設備尚未配對好的,android系統會經過一個通知或者對話框的形式來通知用戶。RFCOMM鏈接請求會在用戶選擇以前阻塞。以下圖:
服務端的鏈接:
當你想要鏈接兩臺設備時,一個必須做爲服務端(經過持有一個打開的bluetoothserversocket),目的是監聽外來鏈接請求,當監聽到之後提供一個鏈接上的bluetoothsocket給客戶端,當客戶端從bluetoothserversocket獲得bluetoothsocket之後就能夠銷燬bluetoothserversocket,除非你還想監聽更多的鏈接請求。
創建服務套接字和監聽鏈接的基本步驟:
首先經過調用listenUsingRfcommWithServiceRecord(String, UUID)方法來獲取bluetoothserversocket對象,參數string表明了該服務的名稱,UUID表明了和客戶端鏈接的一個標識(128位格式的字符串ID,至關於pin碼),UUID必須雙方匹配才能夠創建鏈接。其次調用accept()方法來監聽可能到來的鏈接請求,當監聽到之後,返回一個鏈接上的藍牙套接字bluetoothsocket。最後,在監聽到一個鏈接之後,須要調用close()方法來關閉監聽程序。(通常藍牙設備之間是點對點的傳輸)
注意:accept()方法不該該放在主Acitvity裏面,由於他是一種阻塞調用(在沒有監聽到鏈接請求之間程序就一直停在那裏)。解決方法是新建一個線程來管理。例如:
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 = mAdapter.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) { }
}
}
客戶端的鏈接:
爲了初始化一個與遠端設備的鏈接,須要先獲取表明該設備的一個bluetoothdevice對象。經過bluetoothdevice對象來獲取bluetoothsocket並初始化鏈接:
具體步驟:
使用bluetoothdevice對象裏的方法createRfcommSocketToServiceRecord(UUID)來獲取bluetoothsocket。UUID就是匹配碼。而後,調用connect()方法來。若是遠端設備接收了該鏈接,他們將在通訊過程當中共享RFFCOMM信道,而且connect()方法返回。例如:
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
mAdapter.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) { }
}
注意:conncet()方法也是阻塞調用,通常創建一個獨立的線程中來調用該方法。在設備discover過程當中不該該發起鏈接connect(),這樣會明顯減慢速度以致於鏈接失敗。且數據傳輸完成只有調用close()方法來關閉鏈接,這樣能夠節省系統內部資源。
管理鏈接(主要涉及數據的傳輸):
當設備鏈接上之後,每一個設備都擁有各自的bluetoothsocket。如今你就能夠實現設備之間數據的共享了。
1. 首先經過調用getInputStream()和getOutputStream()方法來獲取輸入輸出流。而後經過調用read(byte[]) 和 write(byte[]).方法來讀取或者寫數據。
2. 實現細節:覺得讀取和寫操做都是阻塞調用,須要創建一個專用現成來管理。
3. 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) { } }}