Android藍牙開發技術指南

Android藍牙開發技術指南 java



Android 藍牙開發技術 android

要學習藍牙先認識 編程

一 RFCOMM 通道: 數組

RFCOMM 協議 安全

一個基於歐洲電信標準協會 ETSI07.10 規程的串行線性仿真協議。此協議提供 RS232 控制和狀態信號,如基帶上的損壞,CTS 以及數據信號等,爲上層業務(如傳統的串行線纜 應用)提供了傳送能力。 服務器

 RFCOMM 是一個簡單傳輸協議,其目的是針對如何在兩個不一樣設備上的應用之間保證一 條完整的通訊路徑,並在它們之間保持一通訊段。 網絡

RFCOMM 協議概述 app

RFCOMM 通訊段 框架

RFCOMM 是爲了兼容傳統的串口應用,同時取代有線的通訊方式,藍牙協議棧須要提供 與有線串口一致的通訊接口而開發出的協議。RFCOMM 協議提供對基於 L2CAP 協議的串口仿 真,基於 ETSI07.10。可支持在兩個 BT 設備之間同時保持高達 60 路的通訊鏈接。 socket

目的:

在兩個不一樣設備(通訊設備的兩端)上的應用之間保證一條完整的通訊路徑, 並在他們之 間保持一通訊段。下圖是一條完整的通訊路徑。 RFCOMM 只針對直接互連設備之間的鏈接,或者是設備與網絡接入設備之間的互連。通 信兩端設備必須兼容於 RFCOMM 協議,有兩類設備:DTE (Data Terminal Endpoint,通訊終 端,如 PC,PRINTER)和 DCE (Data Circuit Endpoint,通訊段的一部分,如 Modem)。此兩 類設備不做區分。

RFCOMM 服務 RFCOMM 仿真 RS232 串口,仿真過程包括非數據通路狀態的傳輸,RFCOMM 內置空 Modem 仿真標準框架。 RFCOMM 中的仿真

 RS-232 通路 多串口仿真

兩個採用 RFCOMM 通訊的 BT 設備有可能同時打開多個串口,RFCOMM 支持同時打開 60 個 端口。

認識二:MAC 硬件地址

MAC(Medium/MediaAccess Control, 介質訪問控制)MAC 地址是燒錄在 NetworkInterfaceCard(網卡,NIC)裏的.MAC 地址,也叫硬件地址,是由 48 比特長(6 字節),16 進制的數字組成.0-23 位叫作組織惟一標誌符(organizationally unique, 是識別 LAN(局域 網)節點的標識.24-47 位是由廠家本身分配。其中第 40 位是組播地址標誌位。網卡的物理 地址一般是由網卡生產廠家燒入網卡的 EPROM(一種閃存芯片,一般能夠經過程序擦寫),它 存儲的是傳輸數據時真正賴以標識發出數據的電腦和接收數據的主機的地址。

也就是說,在網絡底層的物理傳輸過程當中,是經過物理地址來識別主機的,它通常也是 全球惟一的。好比,著名的以太網卡,其物理地址是 48bit(比特位)的整數,如: 44-45-53-54-00-00,以機器可讀的方式存入主機接口中。以太網地址管理機構(除了管這個 外還管別的) (IEEE)(IEEE:電氣和電子工程師協會)將以太網地址,也就是 48 比特的不一樣組合,分 爲若干獨立的連續地址組,生產以太網網卡的廠家就購買其中一組,具體生產時,逐個將惟 一地址賦予以太網卡。

形象的說,MAC 地址就如同咱們身份證上的身份證號碼,具備全球惟一性。

步驟一:Setting Up Bluetooth 經過 BluetoothAdapter 獲得藍牙的 Activity 發送藍牙鏈接意圖 經過 e onActivityResult()獲得藍牙鏈接意圖

步驟二: Finding Devices 經過獲得開啓藍牙用戶名和 MAC 地址 配對藍牙

步驟三:鏈接藍牙

就像 java 的聊天系統同樣用一個藍牙手機當服務器,一個當客戶端,在用一個類當作 鏈接的管理類就好了

android 平臺藍牙編程

Android 平臺支持藍牙網絡協議棧,實現藍牙設備之間數據的無線傳輸。

本文檔描述了怎樣利用 android 平臺提供的藍牙 API 去實現藍牙設備之間的通訊,藍牙設備 之間的通訊主要包括了四個步驟:設置藍牙設備、尋找局域網內可能或者匹配的設備、鏈接 設備和設備之間的數據傳輸。如下是創建藍牙鏈接的所須要的一些基本類: BluetoothAdapter 類:表明了一個本地的藍牙適配器。他是全部藍牙交互的的入口點。利 用它你能夠發現其餘藍牙設備,查詢綁定了的設備,使用已知的 MAC 地址實例化一個藍牙 設備和創建一個 BluetoothServerSocket(做爲服務器端)來監聽來自其餘設備的鏈接。

BluetoothDevice 類: 表明了一個遠端的藍牙設備, 使用它請求遠端藍牙設備鏈接或者獲取 遠端藍牙設備的名稱、地址、種類和綁定狀態。 (其信息是封裝在 bluetoothsocket 中) 。 Bluetoothsocket 類:表明了一個藍牙套接字的接口(相似於 tcp 中的套接字) ,他是應用程 序經過輸入、輸出流與其餘藍牙設備通訊的鏈接點。 Blueboothserversocket 類: 表明打開服務鏈接來監聽可能到來的鏈接請求 (屬於 server 端) , 爲了鏈接兩個藍牙設備必須有一個設備做爲服務器打開一個服務套接字。 當遠端設備發起連 接鏈接請求的時候,而且已經鏈接到了的時候,Blueboothserversocket 類將會返回一個 bluetoothsocket。 Bluetoothclass 類:描述了一個藍牙設備的通常特色和能力。他的只讀屬性集定義了設備 的主、次設備類和一些相關服務。然而,他並無準確的描述全部該設備所支持的藍牙文件 和服務,而是做爲對設備種類來講的一個小小暗示。 下面說說具體的編程實現: 必須肯定你的設備支持藍牙,並保證他能夠用。若是你的設備支持藍牙,將它使能。固然, 有兩種方法, 一種是在你的系統設置裏開啓藍牙, 另一中是在你的應用程序裏啓動藍牙功 能,第一種方法就不講了,具體講一個第二種方法: 首先經過調用靜態方法 getDefaultAdapter()獲取藍牙適配器 bluetoothadapter, 之後你就能夠 使用該對象了。若是返回爲空,the story is over。 Eg:BluetoothAdaptermBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); if (mBluetoothAdapter== null) { // Device does not support Bluetooth } 其次,調用 isEnabled()來查詢當前藍牙設備的狀態,若是返回爲 false,則表示藍牙設備沒有 開啓,接下來你須要封裝一個 ACTION_REQUEST_ENABLE 請求到 intent 裏面,調用 startActivityForResult()方法使能藍牙設備,例如: if (!mBluetoothAdapter.isEnabled()) { Intent enableBtIntent= new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT); } 至此,如不出意外,恭喜你的藍牙設備已經開啓了,接下來須要查找周邊可能存在的藍牙設 備了。 查找設備: 使用 bluetoothadapter 類裏的方法,你能夠查找遠端設備(不過藍牙查找的範圍好像是在 十米之內吧)或者查詢在你手機上已經匹配(或者說綁定)的其餘手機了。固然須要肯定對 方藍牙設備已經開啓或者已經開啓了「被發現使能「功能(對方設備是能夠被發現的是你能 夠發起鏈接的前提條件) 。若是該設備是能夠被發現的,會反饋回來一些對方的設備信息, 好比名字、MAC 地址等,利用這些信息,你的設備就能夠選擇去向對方初始化一個鏈接。 若是你是第一次與該設備鏈接, 那麼一個配對的請求就會自動的顯示給用戶。 當設備配對好 以後,他的一些基本信息(主要是名字和 MAC) 被保存下來並可使用藍牙的 API 來讀取。 使用已知的 MAC 地址就能夠對遠端的藍牙設備發起鏈接請求。 匹配好的設備和鏈接上的設備的不一樣點: 匹配好只是說明對方設備發現了你的存在, 並擁 有一個共同的識別碼,而且能夠鏈接。鏈接上:表示當前設備共享一個 RFCOMM 信道而且 二者之間能夠交換數據。也就是是說藍牙設備在創建 RFCOMM 信道以前,必須是已經配對 好了的。 怎麼查詢匹配好的設備: 在創建鏈接以前你必須先查詢配對好了的藍牙設備集(你周圍的藍牙設備可能不止一個) , 以便你選取哪個設備進行通訊, 例如你能夠你能夠查詢全部配對的藍牙設備, 並使用一個 數組適配器將其打印顯示出來: Set<BluetoothDevice>pairedDevices = mBluetoothAdapter.getBondedDevices(); // If there are paireddevices if (pairedDevices.size() > 0) { // Loop through paired devices for(BluetoothDevice device : pairedDevices) { // Add the name and address to anarray 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 finalBroadcastReceiver mReceiver = new BroadcastReceiver() { public voidonReceive(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 deviceintent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); // Add the name andaddress to an array adapter to show in a ListViewmArrayAdapter.add(device.getName() + "\n" + device.getAddress()); } }}; // Register the BroadcastReceiver IntentFilter filter = newIntentFilter(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 AcceptThreadextends Thread { private final BluetoothServerSocket mmServerSocket; publicAcceptThread() { // Use a temporary object that is later assigned tommServerSocket, // because mmServerSocket is final BluetoothServerSocket tmp =null; try { // MY_UUID is the app's UUID string, also used by the client codetmp = mAdapter.listenUsingRfcommWithServiceRecord(NAME, MY_UUID); } catch(IOException e) { } mmServerSocket = tmp; } public void run() { BluetoothSocketsocket = null; // Keep listening until exception occurs or a socket is returnedwhile (true) { try { socket = mmServerSocket.accept(); } catch (IOException e){ break; } // If a connection was accepted if (socket != null) { // Do work tomanage the connection (in a separate thread) manageConnectedSocket(socket);mmServerSocket.close(); break; } } } /** Will cancel the listening socket, andcause 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 ConnectThreadextends Thread { private final BluetoothSocket mmSocket; private finalBluetoothDevice mmDevice; public ConnectThread(BluetoothDevice device) { // Usea temporary object that is later assigned to mmSocket, // because mmSocket isfinal BluetoothSocket tmp = null; mmDevice = device; // Get a BluetoothSocketto connect with the given BluetoothDevice try { // MY_UUID is the app's UUIDstring, 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 itsucceeds or throws an exception mmSocket.connect(); } catch (IOExceptionconnectException) { // Unable to connect; close the socket and get out try {mmSocket.close(); } catch (IOException closeException) { } return; } // Do workto 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 classConnectedThread extends Thread { private final BluetoothSocket mmSocket;private final InputStream mmInStream; private final OutputStream mmOutStream;public ConnectedThread(BluetoothSocket socket) { mmSocket = socket; InputStreamtmpIn = 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 voidrun() { byte[] buffer = new byte[1024]; // buffer store for the stream intbytes; // bytes returned from read() // Keep listening to the InputStream untilan exception occurs while (true) { try { // Read from the InputStream bytes =mmInStream.read(buffer); // Send the obtained bytes to the UI ActivitymHandler.obtainMessage(MESSAGE_READ, bytes, -1, buffer) .sendToTarget(); }catch (IOException e) { break; } } } /* Call this from the main Activity tosend data to the remote device */ public void write(byte[] bytes) { try {mmOutStream.write(bytes); } catch (IOException e) { } } /* Call this from themain Activity to shutdown the connection */ public void cancel() { try {mmSocket.close(); } catch (IOException e) { } } } 

相關文章
相關標籤/搜索