Android藍芽開發全面總結

jcodecraeer發表於2016-01-31

基本概念

安卓平臺提供對藍芽的通訊棧的支援,允許設別和其他的裝置進行無線傳輸資料。應用程式層通過安卓API來呼叫藍芽的相關功能,這些API使程式無線連線到藍芽裝置,並擁有P2P或者多端無線連線的特性。

藍芽的功能:

1、掃描其他藍芽裝置

2、為可配對的藍芽裝置查詢藍芽介面卡

3、建立RFCOMM通道(其實就是尼瑪的認證)

4、通過服務搜尋來連結其他的裝置

5、與其他的裝置進行資料傳輸

6、管理多個連線

藍芽建立連線必須要求:

1、開啟藍芽

2、查詢附近已配對或可用裝置

3、連線裝置

4、裝置間資料互動

首先,要操作藍芽,先要在AndroidManifest.xml里加入許可權

<uses-permissionandroid:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permissionandroid:name="android.permission.BLUETOOTH" />

Android所有關於藍芽開發的類都在android.bluetooth包下,只有8個類

1.BluetoothAdapter 藍芽介面卡

直到我們建立bluetoothSocket連線之前,都要不斷操作它BluetoothAdapter裡的方法很多,常用的有以下幾個:

  cancelDiscovery() 根據字面意思,是取消發現,也就是說當我們正在搜尋裝置的時候呼叫這個方法將不再繼續搜尋
  disable()關閉藍芽
  enable()開啟藍芽,這個方法開啟藍芽不會彈出提示,更多的時候我們需要問下使用者是否開啟,一下這兩行程式碼同樣是開啟藍芽,不過會提示使用者:
  Intemtenabler=new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
  startActivityForResult(enabler,reCode);//同startActivity(enabler);
  getAddress()獲取本地藍芽地址
  getDefaultAdapter()獲取預設BluetoothAdapter,實際上,也只有這一種方法獲取BluetoothAdapter
  getName()獲取本地藍芽名稱
  getRemoteDevice(String address)根據藍芽地址獲取遠端藍芽裝置
  getState()獲取本地藍芽介面卡當前狀態(感覺可能除錯的時候更需要)
  isDiscovering()判斷當前是否正在查詢裝置,是返回true
  isEnabled()判斷藍芽是否開啟,已開啟返回true,否則,返回false
  listenUsingRfcommWithServiceRecord(String name,UUID uuid)根據名稱,UUID建立並返回BluetoothServerSocket,這是建立BluetoothSocket伺服器端的第一步
  startDiscovery()開始搜尋,這是搜尋的第一步

  使用BluetoothAdapter的startDiscovery()方法來搜尋藍芽裝置
  startDiscovery()方法是一個非同步方法,呼叫後會立即返回。該方法會進行對其他藍芽裝置的搜尋,該過程會持續12秒。該方法呼叫後,搜尋過程實際上是在一個System Service中進行的,所以可以呼叫cancelDiscovery()方法來停止搜尋(該方法可以在未執行discovery請求時呼叫)。
  請求Discovery後,系統開始搜尋藍芽裝置,在這個過程中,系統會傳送以下三個廣播:
  ACTION_DISCOVERY_START:開始搜尋
  ACTION_DISCOVERY_FINISHED:搜尋結束
  ACTION_FOUND:找到裝置,這個Intent中包含兩個extra fields:EXTRA_DEVICE和EXTRA_CLASS,分別包含BluetooDevice和BluetoothClass。
  我們可以自己註冊相應的BroadcastReceiver來接收響應的廣播,以便實現某些功能

2.BluetoothDevice 描述了一個藍芽裝置

   createRfcommSocketToServiceRecord(UUIDuuid)根據UUID建立並返回一個BluetoothSocket
   getState() 藍芽狀態這裡要說一下,只有在 BluetoothAdapter.STATE_ON 狀態下才可以監聽,具體可以看andrid api;
   這個方法也是我們獲取BluetoothDevice的目的――建立BluetoothSocket
   這個類其他的方法,如getAddress(),getName(),同BluetoothAdapter

3.BluetoothServerSocket

這個類一種只有三個方法兩個過載的accept(),accept(inttimeout)兩者的區別在於後面的方法指定了過時時間,需要注意的是,執行這兩個方法的時候,直到接收到了客戶端的請求(或是過期之後),都會阻塞執行緒,應該放在新執行緒裡執行!

還有一點需要注意的是,這兩個方法都返回一個BluetoothSocket,最後的連線也是伺服器端與客戶端的兩個BluetoothSocket的連線

   void	close()
   Closes the object and release any system resources it holds.
   void	connect()
   Attempt to connect to a remote device.
   InputStream	getInputStream()
   Get the input stream associated with this socket.
   OutputStream	getOutputStream()
   Get the output stream associated with this socket.
   BluetoothDevice	getRemoteDevice()
   Get the remote device this socket is connecting, or connected, to.
   獲取遠端裝置,該套接字連線,或連線到---。
   boolean	isConnected()
   Get the connection status of this socket, ie, whether there is an active connection with remote device.
   判斷當前的連線狀態

4.BluetoothSocket

跟BluetoothServerSocket相對,是客戶端一共5個方法,不出意外,都會用到

  close(),關閉
  connect()連線
  getInptuStream()獲取輸入流
  getOutputStream()獲取輸出流
  getRemoteDevice()獲取遠端裝置,這裡指的是獲取bluetoothSocket指定連線的那個遠端藍芽裝置

5、BluetoothClass

代表一個描述了裝置通用特性和功能的藍芽類。比如,一個藍芽類會指定皆如電話、計算機或耳機的通用裝置型別,可以提供皆如音訊或者電話的服務。

每個藍芽類都是有0個或更多的服務類,以及一個裝置類組成。裝置類將被分解成主要和較小的裝置類部分。

BluetoothClass 用作一個能粗略描述一個裝置(比如關閉使用者介面上一個圖示的裝置)的線索,但當藍芽服務事實上是被一個裝置所支撐的時候,BluetoothClass的 介紹則不那麼可信任。精確的服務搜尋通過SDP請求來完成。當運用createRfcommSocketToServiceRecord(UUID) 和listenUsingRfcommWithServiceRecord(String, UUID)來建立RFCOMM埠的時候,SDP請求就會自動執行。

使用getBluetoothClass()方法來獲取為遠端裝置所提供的類。

兩個內部類:

class   BluetoothClass.Device

定義所有裝置類的常量

class   BluetoothClass.Service

定義所有服務類的常量

公共方法:

public int describeContents ()

描述包含在可封裝編組的表示中所有特殊物件的種類。

返回值 

一個指示被Parcelabel所排列的特殊物件型別集合的位掩碼。

public boolean equals (Object o)

比較帶有特定目標的常量。如果他們相等則標示出來。 為了保證其相等,o必須代表相同的物件,該物件作為這個使用類依賴比較的常量。通常約定,該比較既要可移植又需靈活。

當且僅當o是一個作為接收器(使用==操作符來做比較)的精確相同的物件是,這個物件的實現才返回true值。子類通常實現equals(Object)方法,這樣它才會重視這兩個物件的型別和狀態。

通常約定,對於equals(Object)和hashCode() 方法,如果equals對於任意兩個物件返回真值,那麼hashCode()必須對這些物件返回相同的紙。這意味著物件的子類通常都覆蓋或者都不覆蓋這兩個方法。

引數

o   需要對比常量的物件

返回值

如果特定的物件和該物件相等則返回true,否則返回false。

public int getDeviceClass ()

返回BluetoothClass中的裝置類部分(主要的和較小的)

從函式中返回的值可以和在BluetoothClass.Device中的公共常量做比較,從而確定哪個裝置類在這個藍芽類中是被編碼的。

返回值

裝置類部分

public int getMajorDeviceClass ()

返回BluetoothClass中裝置類的主要部分

從函式中返回的值可以和在BluetoothClass.Device.Major中的公共常量做比較,從而確定哪個主要類在這個藍芽類中是被編碼的。

返回值

主要裝置類部分

public boolean hasService (int service)

如果該指定服務類被BluetoothClass所支援,則返回true

在BluetoothClass.Service中,合法的服務類是公共常量,比如AUDIO類。

引數

service 合法服務類

返回值

如果該服務類可被支援,則返回true

public int hashCode ()

返回這個物件的整型雜湊碼。按約定,任意兩個在equals(Object)中返回true的物件必須返回相同的雜湊碼。這意味著物件的子類通常通常覆蓋或者都不覆蓋這兩個方法。

注意:除非同等對比資訊發生改變,否則雜湊碼不隨時間改變而改變。

public String toString ()  

返回這個物件的字串,該字串包含精確且可讀的介紹。系統鼓勵子類去重寫該方法,並且提供了能對該物件的型別和資料進行重視的實現方法。預設的實現方法只是簡單地把類名、“@“符號和該物件hashCode()方法的16進位制數連線起來(如下列所示的表示式):

public void writeToParcel (Parcel out, int flags)
     將類的資料寫入外部提供的Parcel中
    引數
out     物件需要被寫入的Parcel
flags  和物件需要如何被寫入有關的附加標誌。可能是0,或者可能是PARCELABLE_WRITE_RETURN_VALUE。

以上是藍芽主要操作的類。

基本用法:

1、獲取本地藍芽介面卡

  BluetoothAdapter mAdapter= BluetoothAdapter.getDefaultAdapter();

2、開啟藍芽

  if(!mAdapter.isEnabled()){
  //彈出對話方塊提示使用者是後開啟
  Intent enabler = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
  startActivityForResult(enabler, REQUEST_ENABLE);
  //不做提示,強行開啟,此方法需要許可權<uses-permissionandroid:name="android.permission.BLUETOOTH_ADMIN" />
  // mAdapter.enable();
}

3、搜尋裝置

1)mAdapter.startDiscovery() 是第一步,可能你會發現沒有返回的藍芽裝置

2)定義BroadcastReceiver,程式碼如下

// 建立一個接收ACTION_FOUND廣播的BroadcastReceiver
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
  public void onReceive(Context context, Intent intent) {
	 String action = intent.getAction();
    // 發現裝置
    if (BluetoothDevice.ACTION_FOUND.equals(action)) {
        // 從Intent中獲取裝置物件
        BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
        // 將裝置名稱和地址放入array adapter,以便在ListView中顯示
        mArrayAdapter.add(device.getName() + "/n" + device.getAddress());
    }else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED  
                .equals(action)) {  
            if (mNewDevicesAdapter.getCount() == 0) {  
                Log.v(TAG, "find over");  
            }  
     }  
};
// 註冊BroadcastReceiver
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
registerReceiver(mReceiver, filter); // 不要忘了之後解除繫結

4、建立連線

首先Android sdk(2.0以上版本)支援的藍芽連線是通過BluetoothSocket建立連線,伺服器端(BluetoothServerSocket)和客戶端(BluetoothSocket)需指定同樣的UUID,才能建立連線,因為建立連線的方法會阻塞執行緒,所以伺服器端和客戶端都應啟動新執行緒連線

1)伺服器端:
//UUID格式一般是"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"可到
    //http://www.uuidgenerator.com 申請
BluetoothServerSocket serverSocket = mAdapter. listenUsingRfcommWithServiceRecord(serverSocketName,UUID);
serverSocket.accept();

2)客戶端:
//通過BroadcastReceiver獲取了BLuetoothDevice
BluetoothSocket clienSocket=dcvice. createRfcommSocketToServiceRecord(UUID);
clienSocket.connect();

5、資料傳遞

通過以上操作,就已經建立的BluetoothSocket連線了,資料傳遞是通過流

1)獲取流

inputStream = socket.getInputStream();
	outputStream = socket.getOutputStream();

2)寫出、讀入(JAVA常規操作)

補充一下,使裝置能夠被搜尋

Intent enabler = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
	startActivityForResult(enabler,REQUEST_DISCOVERABLE);

關於藍芽連線:

可以直接用以下程式碼進行連線:

final String SPP_UUID = "00001101-0000-1000-8000-00805F9B34FB";          
UUID uuid = UUID.fromString(SPP_UUID);          
BluetoothSocket socket;          
socket = device.createInsecureRfcommSocketToServiceRecord(uuid);      
adapter.cancelDiscovery();       
socket.connect();

1.startDiscovey有可能啟動失敗

一般程式中會有兩步:開啟藍芽、開始尋找裝置。順序執行了開啟藍芽-尋找裝置的步驟,但是由於藍芽還沒有完全開啟,就開始尋找裝置,導致尋找失敗。

解決辦法:

adapter = BluetoothAdapter.getDefaultAdapter();     
 if (adapter == null)      {        
  // 裝置不支援藍芽      
 }     
 // 開啟藍芽       
if (!adapter.isEnabled()){          
	adapter.enable();     
	adapter.cancelDiscovery(); 
}      
// 尋找藍芽裝置,android會將查詢到的裝置以廣播形式發出去       
while (!adapter.startDiscovery()){     
	Log.e("BlueTooth", "嘗試失敗");     
	try {         
		Thread.sleep(100);     
	} catch (InterruptedException e) {         
		e.printStackTrace();     
	} 
}

2.接收資料轉換

使用socket.getInputStream接收到的資料是位元組流,這樣的資料是沒法分析的,所以很多情況需要一個byte轉十六進位制String的函式:

public static String bytesToHex(byte[] bytes) { 
	char[] hexChars = new char[bytes.length * 2]; 
	for ( int j = 0; j < bytes.length; j++ ) {     
		int v = bytes[j] & 0xFF;     
		hexChars[j * 2] = hexArray[v >>> 4];     
		hexChars[j * 2 + 1] = hexArray[v & 0x0F]; 
	} 
	return new String(hexChars);
}

相關文章