標籤:
Android藍芽系統
藍芽是一種支援裝置短距離通訊(一般10m內)的無線電技術,可以在眾多裝置之間進行無線資訊交換。
Android系統中的藍芽模組
Android包含了對藍芽網路通訊協定棧的支援,使藍牙裝置能夠無線串連其他藍牙裝置以便交換資料。
通過使用藍芽API,一個Android應用程式能夠實現如下功能:
- 掃描其他藍牙裝置
- 查詢本地藍芽適配器用於配對藍牙裝置
- 建立RFCOMM通道
- 通過服務發現串連其他裝置
- 資料通訊
- 管理多個串連
Android平台中藍芽系統從上到下主要包括Java架構中的BlueTooth類、Android適配庫、BlueZ庫、驅動程式和協議。
藍芽模組中的源碼
初始化藍芽晶片
通過BlueZ工具hciattach進行的,命令格式如下: hciattach [-n] [-p] [-b] [-t timeout] [-s initial_speed] <tty> <type | id> [speed] [flow|noflow] [bdaddr]type決定了要初始化的裝置的型號,可以使用hciattach -1來列出所支援的裝置型號。Hciattach命令內部的工作步驟是:首先開啟制定的tty裝置,再做一些通用的設定,如flow等, 之後設定傳輸速率為initial_speed,然後根據type調用各自的初始化代碼,最後將傳輸速率重新設定為speed。
藍芽服務
一般不需要自己定義,只需要使用初始化指令檔init.rc中的預設內容即可。
管理藍芽電源
可以調用rfkill介面來控制電源管理,如果已經實現了rfkill介面,則無需再進行配置。
和藍芽相關的類BluetoothSocket類
BluetoothSocket類基礎
Android的藍芽系統和Socket通訊端密切相關,藍芽端的監聽介面和TCP連接埠類似,都是使用 Socket和ServerSocket類。在伺服器端使用BluetoothServerSocket類來建立一個監聽服務連接埠。當一個串連被BluetoothServerSocket所接受,它會返回一個新的BluetoothSocket來管理該串連。在用戶端,使用一個單獨的BluetoothSocket類去初始化一個外接串連和管理該串連。
最通常使用的藍芽連接埠是RFCOMM,它是一個連線導向,通過藍芽模組進行資料流傳輸方式,也被稱為序列埠規範(SPP)。
建立一個BluetoothSocket去串連到一個已知裝置,使用方法BluetoothDevice.createRfcommSocketToServiceRecord()。然後調用connect()方法去嘗試一個面向遠程裝置的串連。這個調用將被阻塞指導一個串連已經建立或者該連結失效。
當連接埠串連成功後,通過getInputStream()和getOutputStream()來開啟I/O流,從而獲得各自的InputStream和OutputStream對象。
BluetoothSocket類的線程是安全的,因為close()方法會馬上放棄外界操作並關閉伺服器連接埠。
BluetoothSocket類的公用方法
- public void close()
- public void connect()
- public InputStream getInputStream()
- public OutputStream getOutputStream()
- public BluetoothDevice getRemoteDevice()
BluetoothServerSocket類
BluetoothAdapter類
BluetoothAdapter類基礎
代表本地的藍芽適配器裝置,通過此類可以讓使用者能執行基本的藍芽任務,如初始化裝置的搜尋、查詢可匹配的裝置集、使用一個已知的MAC地址來初始化一個BluetoothDevice類、建立一個BluetoothServerSocket類以監聽其他裝置對原生串連請求等。
調用靜態方法getDefaultAdapter()獲得,這是所有藍芽動作使用的第一步。當擁有本地適配器以後,使用者可以獲得一系列的BluetoothDevice對象,這些對象代表所有擁有getBondedDevice()方法的已經匹配的裝置;用startDiscovery()方法來開始裝置的搜尋;或者建立一個BluetoothServerSocket類,通過listenUsingRfcommWithServiceRecord(String,UUID)方法來監聽新來的串連請求。
大部分方法需要BLUETOOTH許可權,一些方法同時需要BLUETOOTH_ADMIN許可權。
BluetoothAdapter類的常量
String ACTION_DISCOVERY_FINISHED
廣播事件:本地藍芽適配器已經完成裝置的搜尋過程,需要BLUETOOTH許可權接收。常量值:android.bluetooth.adapter.action.DISCOVERY_FINISHED
String ACTION_DISCOVERY_STARTED
廣播事件:本地藍芽適配器已經開始對遠程裝置的搜尋過程。使用者可以通過cancelDiscovery()類來取消正在執行的尋找過程,需要BLUETOOTH許可權接收。常量值:android.bluetooth.adapter.action.DISCOVERY_STARTED
String ACTION_LOCAL_NAME_CHANGED
廣播事件:本地藍芽適配器已經更改了它的藍芽名稱,需要BLUETOOTH許可權接收。常量值:android.bluetooth.adapter.action.LOCAL_NAME_CHANGED
String ACTION_REQUEST_DISCOVERABLE
Activity活動:顯示一個請求被搜尋模式的系統活動。Android運用onActivityResult(int,int,Intent)回收方法來傳遞該活動結果的通知。需要BLUETOOTH許可權。常量值:android.bluetooth.adapter.action.REQUEST_DISCOVERYABLE
String ACTION_REQUEST_ENABLE
Activity活動:顯示一個允許使用者開啟藍芽模組的系統活動。Android運用onActivityResult(int,int,Intent)回收方法來傳遞該活動結果的通知。需要BLUETOOTH許可權。常量值:android.bluetooth.adapte.action.REQUEST_ENABLE
String ACTION_SCAN_MODE_CHANGED
廣播活動:指明藍芽掃描模組或者本地適配器已經發生變化,需要BLUETOOTH許可權。常量值:android.bluetooth.adapter.action.SCAN_MODE_CHANGED
String ACTION_STATE_CHANGED
廣播活動:本來的藍芽適配器的狀態已經改變,如藍芽模組已經被開啟或者關閉,需要BLUETOOTH許可權接收。常量值:android.bluetooth.adapter.action.STATE_CHANGED
int ERROR
功能:標記該類的錯誤值,確保和該類中的任意其他整數常量不相等。它為需要一個標記錯誤值的函數提供了便利,例如:Intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,BluetoothAdapter.ERROR);
String EXTRA_DISCOVERABLE_DURATION
功能:試圖在ACTION_REQUEST_DISCOVERABLE常量中作為一個可選的整型附加域,來為短時間內的裝置發現請求一個特定的期間,預設值為120秒,超過300秒的請求將被限制。常量值:android.bluetooth.adapter.extra.DISCOVERABLE_DURATION
String EXTRA_LOCAL_NAME
功能:試圖在ACTION_LOCAL_NAME_CHANGED常量中作為一個字串附加域,來請求本地藍芽的名稱。常量值:android.bluetooth.adapter.extra.LOCAL_NAME
String EXTRA_PREVIOUS_SCAN_MODE
功能:試圖在ACTION_SCAN_MODE_CHANGED常量中作為一個整型附加域,來請求以前的掃描模式,可能值如下: SCAN_MODE_NONE SCAN_MODE_CONNECTABLE SCAN_MODE_CONNECTABLE_DISCOVERABLE常量值:android.bluetooth.adapter.extra.PREVIOUS_SCAN_MODE
String EXTRA_PREVIOUS_STATE
功能:試圖在ACTION_STATE_CHANGED常量中作為一個整型附加域,來請求以前的供電狀態。可能值如下: STATE_OFF STATE_TURNING_ON STATE_ON STATE_TURNING_OFF常量:android.bluetooth.adapter.extra.PREVIOUS_STATE
String EXTRA_SCAN_MODE
功能:試圖在ACTION_SCAN_MODE_CHANGED常量作為一個整型附加域,來請求當前的掃描模式,可能值如下: SCAN_MODE_NONE SCAN_MODE_CONNECTABLE SCAN_MODE_CONNECTABLE_DISCOVERABLE常量值:android.bluetooth.adapter.extra.SCAN_MODE
String EXTRA_STATE
功能:試圖在ACTION_STATE_CHANGED常量中作為一個整型附加域,來請求當前供電狀態,可能值如下: STATE_OFF STATE_TURNING_ON STATE_ON STATE_TURNING_OFF常量值:android.bluetooth.adapter.extra.STATE
int SCAN_MODE_CONNECTABLE
功能:指明在本地藍芽適配器中,查詢掃描功能失效,但頁面掃描功能有效。因此,該裝置不能被遠程藍牙裝置發現,但如果以前曾經發現過該裝置,則遠程裝置可以對其進行串連。常量值:21(0x00000015)
int SCAN_MODE_CONNECTABLE_DISCOVERABLE
功能:指明在本地藍芽適配器中, 查詢掃描功能都有效,因此,該裝置既可以被遠程藍牙裝置發現,也可以被其串連。常量值:23(0x00000017)
int SCAN_MODE_NONE
功能:指明在本地藍芽適配器中,查詢掃描功能和頁面掃描功能都失效,因此,該裝置既不可以被遠程藍牙裝置發現,也不可以被串連。常量值:20(0x00000014)
int STATE_OFF
功能:指明本地藍芽適配器模式已經關閉常量值:10(0x0000000a)
int STATE_ON
功能:指明本地藍芽適配器模組已經開啟,並且準備被使用
int STATE_TURNING_OFF
功能:指明本地藍芽適配器模組正在關閉。本地用戶端可以立刻嘗試友好地斷開任意外部串連。常量值:13(0x0000000d)
int STATE_TURNING_ON
功能:指明本地藍芽適配器模組正在開啟,然而本地客戶在嘗試使用這個適配器之前需要為STATE_ON狀態而等待常量值:11(0x000000b)
BluetoothAdapter類的公用方法
- public boolean cancelDiscovery()
- public static boolean checkBluetoothAddress(String address)
- public boolean disable()
- public boolean enable()
- public String getAddress()
- public Set getBondedDevices()
- public static synchronized BluetoothAdapter getDefaultAdapter()
- public String getName()
- public BluetoothDevice getRemoteDevice(String address)
- public int getScanMode()
- public int getState()
- public boolean isDiscovering()
- public boolean isEnabled()
- public BluetoothServerSocket ListenUsingRfcommWithServiceRecord(String name,UUID uuid)
- public boolean setName(String name)
- public boolean startDiscovery()
BluetoothClass.Service類
用於定義所有的服務類常量,任意BluetoothClass由0或多個服務類編碼組成。在類BluetoothClass.Service中包含如下常量:
- int AUDIO
- int CAPTURE
- int INFORMATION
- int LIMITED_DISCOVERABILITY
- int NETWORKING
- int OBJECT_TRANSFER
- int POSITIONING
- int RENDER
- int TELEPHONY
BluetoothClass.Device類
用於定義所有的裝置類的常量,每個BluetoothClass有一個帶有主要和較小部分的裝置類進行編碼,裡面的常量代表主要和較小的裝置類部分(完整的裝置類)的組合。
在Android平台開發藍芽應用程式
1.設定許可權
<uses-permission android:name="android.permission.BLUETOOTH"/><uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
2.啟動藍芽
BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();if(mBluetoothAdapter == null){ //表示手機不支援藍芽 return;}if(!mBluetoothAdapter.isEnabled()){ //藍芽未開啟,則開啟藍芽 Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); startActivityForResult(enableIntent,REQUEST_ENABLE_BT);}//...public void onActivityResult(int requestCode,int resultCode,Intent data){ if(requestCode == REQUEST_ENABLE_BT){ if(resultCode == RESULT_OK){ //藍芽已經開啟 } }}
3.發現藍牙裝置
//使本機藍芽在300秒內可被搜尋private void ensureDiscoverable(){ if(mBluethAdapter.getScanMode() != BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE){ Intent discoverableIntent = new Intent(BluetoothAdapter.ATION_REQUEST_DISCOVERABLE); intent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION,300); startActivity(discoverableIntent); }}//尋找已經配對的藍牙裝置,即以前已經配對過的裝置Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices();if(pairedDevices.size() > 0){ findViewById(R.id.title_paired_devices).setVisibility(View.VISIBLE); for(BluetoothDevice device : pairedDevices){ //device.getName() + " " + device.getAddress(); }}else{ mPairedDevicesArrayAdapter.add("沒有找到已配對的裝置");}//註冊,當一個裝置被發現時調用onReceiveIntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);this.registerReceiver(mReceiver,filter);//當搜尋結束後調用onReceivefilter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);this.registerReceiver(mReceiver,filter);//...private BroadcastReceiver mReceiver = new BroadcastReceiver(){ @Override public void onReceiver(Context context,Intent intent){ String action = intent.getAction(); if(BluetoothDevice.ACTION_FOUND.equal(action)){ BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); //已經配對的則跳過 if(device.getBondState() != BluetoothDevice.BOND_BONDED){ mNewDevicesArrayAdapter.add(device.getName()+"\n"+device.getAddress()); } }else if(BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equal(action)){ //搜尋結束 if(mNewDevicesArrayAdapter.getCount() == 0){ mNewDevicesArrayAdapter.add("沒有搜尋到裝置"); } } }}
4.建立串連
//UUID可以看做一個連接埠號碼private static final UUID MY_UUID = UUID.fromString("fa87c0d0-afac-11de-8a39-0800200c9a66");//像一個伺服器一樣時刻監聽是否有串連建立private class AcceptThread extend Thread{ private BluetoothServerSocket serverSocket; public AcceptThread(boolean secure){ BluetoothServerSocket temp = null; try{ temp = mBluetoothAdapter.listenUsingRfcommWithServiceRecord(NAME_INSECURE,MY_UUID); }catch(IOException e){ Log.e("TAG","listen() failed "+e); } serverSocket = temp; } public void run(){ BluetoothSocket socket = null; while(true){ try{ socket = serverSocket.accept(); }catch(IOException e){ Log.e("TAG","accept() failed "+e); break; } } if(socket != null){ //此時可以建立一個資料交換線程,把此socket傳進去 } } //取消監聽 public void cancel(){ try{ serverSocket.close(); }catch(){ Log.e("TAG","Socket Type "+socketType+" close() of server failed "+e); } }}
5.交換資料
//另一個裝置去串連本機,相當於用戶端private class ConnectThread extends Thread{ private BluetoothSocket socket; private BluetoothDevice device; public ConnectThread(BluetoothDevice device,boolean secure){ this.device = device; BluetoothSocket tmp = null; try{ tmp = device.createRfcommSocketToServiceRecord(MY_UUID_SECURE); }catch(IOException e){ Log.e("TAG","create() failed "+e); } } public void run(){ mBluetoothAdapter.cancelDiscovery(); //取消裝置尋找 try{ socket.connect(); }catch(IOException e){ try{ socket.close(); }catch(IOException e1){ Log.e("TAG","unable to close "+"socket during connection failure "+e1); } connectionFailed(); //串連失敗 return; } //此時可以建立一個資料交換線程,把此socket傳進去 } public void cancel(){ try{ socket.close(); }catch(){ Log.e("TAG","close() of connect socket failed "+e); } }}
6.建立資料通訊線程
//建立串連後,進行資料通訊的線程private class ConnectedThread extend Thread{ private BluetoothSocket socket; private InputStream inStream; private OutputStream outStream; public ConnectedThread(BluetoothSocket socket){ this.socket = socket; try{ //擷取輸入/輸出流 inStream = socket.getInpuStream(); outStream = socket.getOutputStream(); }catch(IOException e){ Log.e("TAG","temp sockets not created "+e); } } public void run(){ byte[] buff = new byte[1024]; int len = 0; //讀取資料需不斷監聽,寫不需要 while(true){ try{ len = inStream.read(buff); //把讀取到的資料發送給UI進行顯示 Message msg = handler.obtainMessage(BluetoothChat.MESSAGE_READ,len,-1,buff); }catch(IOException e){ Log.e("TAG","disconnected "+e); connectionLost(); //失去串連 start(); //重新啟動伺服器 break; } } } public void write(byte[] buffer){ try{ outStream.write(buffer); handler.obtainMessage(BluetoothChat.MESSAGE_WRITE,-1,-1,buffer); msg.sendToTarget(); }catch(IOException e){ Log.e("TAG","Exception during write "+e); } } public void cancel(){ try{ socket.close(); }catch(IOException e){ Log.e("TAG","close() of connect socket failed "+e); } }}
Android藍芽開發簡介