1. 程式人生 > >Android藍芽開發之低功耗藍芽(藍芽4.0)開發全記錄

Android藍芽開發之低功耗藍芽(藍芽4.0)開發全記錄

主要內容概況

前面我記錄了開發藍芽2.0的過程,今天準備介紹現在的主流藍芽技術,藍芽4.0的開發,該藍芽技術是在Android4.3(API級別18)版本引入的API。

官方文件

具體的區別主要以下幾點:

1.新的藍芽技術提供了連線服務的方法,以前是沒有提供連線藍芽的方法的。

2.掃描和連線裝置,通過回掉的方式來進行通知,不再是以前通過系統廣播的方式。

3.低功耗說明使用4.0的藍芽能降低電量消耗。

4.資料互動的方式也不再是通過stock的方式,而是通過GATT協議。

開始介紹主要的開發內容

開發過程主要還是參考谷歌的官方案例,samples很簡單主要就四個類。

官方連線

下面開始正式上程式碼

  1. 常規的,開啟藍芽,許可權新增,設定裝置可見等都不介紹了,可以看一下我以前那一篇文章。

    我們從裝置掃描開始介紹,程式碼如下:

//獲取一下藍芽介面卡
final BluetoothManager bluetoothManager =
                (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
        mBluetoothAdapter = bluetoothManager.getAdapter();
//開始進行掃描裝置了,傳遞進去一個監聽
mBluetoothAdapter.startLeScan(mLeScanCallback); //再回掉裡去接收掃描到的裝置 private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() { @Override public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
runOnUiThread(new Runnable() { @Override public void run() { //這裡要注意,會掃描到相同的裝置,需要對列表進行去重複 mLeDeviceListAdapter.addDevice(device); mLeDeviceListAdapter.notifyDataSetChanged(); } }); } }; //在add的方法中需要進行判斷 public void addDevice(BluetoothDevice device) { if (!mLeDevices.contains(device)) { mLeDevices.add(device); } }

以上基本上就可以完成掃描裝置,並且顯示刀列表上了。是不是很簡單,連線裝置部分會比較麻煩一些。

  1. 連線裝置部分主要可以分為兩個部分,一部分先是進行裝置匹配;另一部分則是進行連線服務。

官方的demo上繫結一個服務,然後一些連線操作都放到服務中進行,實際我們可以直接呼叫連線裝置的方法。

//連線設定,並傳入一個連線的回掉。
mBluetoothGatt = device.connectGatt(this, false, mBluetoothGattCallback);
//6.0的裝置中添加了新的引數,註解意思是首選連線的遠端裝置的型別,這裡我們選擇的是低功率藍芽裝置
mBluetoothGatt = device.connectGatt(this, false, mBluetoothGattCallback,BluetoothDevice.TRANSPORT_LE);

然後主要的程式碼邏輯都是在我們的監聽事件上,收到連線成功的監聽,要去解析服務,這裡要做的事情還有要檢視連結的裝置是否支援有對應的服務在BluetoothGatt裡面。

下面這裡上具體的程式碼,還是參照官方例子,稍微改進一下。

private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {

        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
            if (newState == BluetoothProfile.STATE_CONNECTED) {
                LogUtil.i("連結上");
               //連線上裝置更新一下UI相關
            } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
                LogUtil.i("斷開連結");
                //斷開裝置也更行一下,如果需要重連線,可以在這裡進行。
            }
        }

        @Override
        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
            if (status == BluetoothGatt.GATT_SUCCESS) {
                LogUtil.i("解析服務");
			//獲取到支援的服務列表,下面程式碼塊進行解析                               	 		
             displayGattServices(mBluetoothLeService.getSupportedGattServices());

            }

        }

        @Override
        public void onCharacteristicRead(BluetoothGatt gatt,
                                         BluetoothGattCharacteristic characteristic,
                                         int status) {
            if (status == BluetoothGatt.GATT_SUCCESS) {
                broadcastUpdate(characteristic);
            }
        }

        @Override
        public void onCharacteristicChanged(BluetoothGatt gatt,
                                            BluetoothGattCharacteristic characteristic) {
            mConnectionState = STATE_CONNECTED;
            broadcastUpdate(characteristic);
        }
    };
  1. 具體解析服務的地方主要是對裝置的一個匹配,中心裝置和外圍裝置有相同的UUID然後就可以識別上了。有一些常見的UUID可以瞭解一下。
//這裡解析服務相關。主要是兩個迴圈,一個是匹配服務的UUID,另外一個則是匹配特徵的UUID,如果我們只匹配特定的裝置,這裡的UUID可以是一個固定的String(UUID)字串。
for (BluetoothGattService gattService : gattServices) {
            HashMap<String, String> currentServiceData = new HashMap<String, String>();
           //獲取每個服務的uuid
    		uuid = gattService.getUuid().toString();
            //匹配我們的uuid,只要不匹配的姐跳過繼續匹配
    		if (!"某一個uuid".equals(s.getUuid().toString())) {
                continue;
            }

            // 上面服務匹配成功後,在匹配特徵,其實和上面一樣,拿到特定的uuid,匹配上後就可以傳送資料了
            for (BluetoothGattCharacteristic gattCharacteristic : gattCharacteristics) {
                uuid = gattCharacteristic.getUuid().toString();
                //匹配我們的uuid,只要不匹配的姐跳過繼續匹配
    		if (!"某一個uuid".equals(s.getUuid().toString())) {
                continue;
            	}
                //傳送特徵通知,是否啟用還特徵裝置通知。
                 setCharacteristicNotification(bc, true);
            }
        }

這裡連線成功後,再把我們的具有我們指定特徵的設備註冊到遠端的裝置上去,這裡我理解是給遠端裝置發放了一個通行證,這樣你的資料才能傳送到我(手機或中心裝置)的上面。

/**
     * Enables or disables notification on a give characteristic.
     *
     * @param characteristic Characteristic to act on.
     * @param enabled If true, enable notification.  False otherwise.
     */
    public void setCharacteristicNotification(BluetoothGattCharacteristic characteristic,
                                              boolean enabled) {
        if (mBluetoothAdapter == null || mBluetoothGatt == null) {
            Log.w(TAG, "BluetoothAdapter not initialized");
            return;
        }
        mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);
        // This is specific to Heart Rate Measurement.
        if (UUID_HEART_RATE_MEASUREMENT.equals(characteristic.getUuid())) {
            //構造出一個gatt的描述符號,註冊到
            BluetoothGattDescriptor descriptor = characteristic.getDescriptor(
                    UUID.fromString(SampleGattAttributes.CLIENT_CHARACTERISTIC_CONFIG));
            descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
           //Write the value of a given descriptor to the associated remote device.
           //這裡就是類似的把描述符寫到遠端的裝置上了。
            mBluetoothGatt.writeDescriptor(descriptor);
        }
    }
  1. 完成上面所有的步驟,基本上中心裝置和遠端裝置已經可以進行資料的傳遞了。

    當發生資料變化的時候回去回掉我們最開始註冊的mGattCallback方法中的實現。然後收到通知或者是read的訊息的時候,獲取到收到的資料。

     //mGattCallback中的以下兩個方法在發生資料變化的時候會被呼叫。然後我們在這裡就可一拿到傳送過來的資料了。
    @Override
            public void onCharacteristicRead(BluetoothGatt gatt,
                                             BluetoothGattCharacteristic characteristic,
                                             int status) {
                if (status == BluetoothGatt.GATT_SUCCESS) {
                    broadcastUpdate(characteristic);
                }
            }
    
            @Override
            public void onCharacteristicChanged(BluetoothGatt gatt,
                                                BluetoothGattCharacteristic characteristic) {
                mConnectionState = STATE_CONNECTED;
                broadcastUpdate(characteristic);
            }
    
    
    
  2. 呼叫broadcastUpdate方法統一處理收到的通知或者資料,下面我們其實只關注自己指定的UUID裝置就可以了,因為我們自己的應用也只是收集自己的遠端裝置的訊息資料。

     private void broadcastUpdate(final String action,
                                     final BluetoothGattCharacteristic characteristic) {
            final Intent intent = new Intent(action);
    
            // This is special handling for the Heart Rate Measurement profile.  Data parsing is
            // carried out as per profile specifications:
            // http://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.heart_rate_measurement.xml
            if (UUID_HEART_RATE_MEASUREMENT.equals(characteristic.getUuid())) {
                int flag = characteristic.getProperties();
                int format = -1;
                if ((flag & 0x01) != 0) {
                    format = BluetoothGattCharacteristic.FORMAT_UINT16;
                    Log.d(TAG, "Heart rate format UINT16.");
                } else {
                    format = BluetoothGattCharacteristic.FORMAT_UINT8;
                    Log.d(TAG, "Heart rate format UINT8.");
                }
                final int heartRate = characteristic.getIntValue(format, 1);
                Log.d(TAG, String.format("Received heart rate: %d", heartRate));
                intent.putExtra(EXTRA_DATA, String.valueOf(heartRate));
            } else {
                // For all other profiles, writes the data formatted in HEX.
                //對於其他的遠端裝置,我們不關心,這裡可以不用實現
                }
            }
    //這裡我們直接拿到上面heartRate資料就可了。拿到資料後就可以回到我們正常的業務邏輯。
            sendBroadcast(intent);
        }
    

完成上述的幾個大的步驟基本上你的遠端裝置和手機已經連線上了,總體來說整個過程還是比較容易。

藍芽的內容也就到此結束了,如果有問題歡迎反饋給我,其實這個過程就是參考谷歌的官方demo來機率。當我們接觸一個新的開發領域的時候,首先想到的應該就是跟著官方文件和demo走,防止自己誤入歧途