低功耗藍牙開發之創建鏈接

1、前言

上一篇文章咱們講述了低功耗藍牙設備開發過程當中的廣播數據解析,對於常見的應用場景而言,作到解析廣播數據已經夠了,何況一旦中心設備和外圍設備創建鏈接的話,廣播就會終止。可是對於藍牙設備管理員而言,就須要鏈接上外圍設備,而後進行設備參數的修改;android

2、鏈接到GATT Server

鏈接到GATT Server,須要用到BluetoothGatt這個對象,它是藍牙GATT配置文件的公共API.咱們經過BluetoothGatt對象進行鏈接的創建和斷開,Gatt Services數據的獲取,Gatt Characteristics數據的獲取和讀寫操做,Gatt Descriptor數據的讀寫操做等;數組

咱們先看一下,BluetoothGatt的經常使用API:安全

  • connect():重連方法。當外圍設備和中心設備藍牙斷開鏈接時,用於進行從新鏈接。若是外圍設備超出範圍,則等在範圍內時就會觸發重連;
  • connect(Boolean autoConnect, BluetoothGattCallback callback,Handler handler):

與支持藍牙GATT的設備創建鏈接。第二個參數是否自動鏈接;bash

  • disconnect():斷開鏈接。能夠是已經創建的鏈接或取消正在創建的鏈接;
  • discoverServices():發現服務。通常用於當GATT鏈接創建以後,使用BluetoothGatt對象調用該方法來獲取外圍設備的Gatt Services,Gatt Characteristics,Gatt Descriptors數據。當服務數據發現完畢以後會回調BluetoothGattCallback的onServicesDiscovered方法,此時咱們使用getServices就能夠獲取外圍設備的Gatt Services數據。
  • getService(UUID uuid):返回特定UUID的BluetoothGattService。前提條件是必須針對特定外圍設備去獲取Gatt Service,也就是說創建鏈接,獲取到BluetoothGatt對象纔可使用此方法。同時須要注意可能返回null,若是存在多個具備相同UUID的Gatt Service,默認返回第一個。
  • readCharacteristic(BluetoothGattCharacteristic characteristic):讀取相關聯外圍設備的BluetoothGattCharacteristic.這是異步操做,該操做結果會在BluetoothGattCallback的onCharacteristicRead回調方法裏返回。
  • writeCharacteristic(BluetoothGattCharacteristic characteristic):將BluetoothGattCharacteristic 寫入相關聯外圍設備。一旦寫操做完成會在BluetoothGattCallback的onCharacteristicWrite方法中回調。
  • readDescriptor(BluetoothGattDescriptor descriptor):讀取相關聯外圍設備的BluetoothGattDescriptor。一旦讀操做完成會在BluetoothGattCallback的onDescriptorRead方法進行回調。
  • writeDescriptor(BluetoothGattDescriptor descriptor):將BluetoothGattDescriptor寫入相關聯外圍設備。一旦寫操做完成會在BluetoothGattCallback的onDescriptorWrite方法裏進行回調。
  • setCharacteristicNotification(BluetoothGattCharacteristic characteristic, boolean enable):

開啓或禁用給定Gatt Characteristic的notifications(通知)/indications(指示)。一旦notifications設置爲true的話,若是遠程設備的Gatt Characteristic發生改變則BluetoothGattCallback的onCharacteristicChanged方法就會觸發。app

  • readRemoteRssi():讀取已鏈接的遠程設備的RSSI。當RSSI的值被讀取以後,會回調BluetoothGattCallback的onReadRemoteRssi方法。
  • readPhy():讀取鏈接的當前發送器PHY和接收器PHY。最終結果會在BluetoothGattCallback的onPhyRead方法裏回調。
  • requestMtu(int mtu):針對當前鏈接請求MTU大小。當咱們使用BluetoothGatt對象調用writeCharacteristic()方法進行數據寫入操做時,若是數據大小超過MTU的默認大小時,會被分拆爲多個小包進行數據寫入的操做。該方法容許開發者修改MTU的大小。會觸發BluetoothGattCallback的onMtuChanged方法回調。


1.創建鏈接:

中心設備(手機等)有時候須要與外圍設備(低功耗藍牙)創建鏈接,進行參數的修改等操做。固然並非全部的低功耗藍牙設備都可以被鏈接上,這個就要看硬件開發者設計的產品允不容許被鏈接。這些咱們不在考慮。創建鏈接首先須要掃描到設備,設備的掃描不是本篇文章主要的討論對象,不清楚的童鞋能夠閱讀低功耗藍牙開發之設備掃描。掃描到設備以後拿到設備的信息,用來進行鏈接的創建。示例代碼以下:異步

/**

     * Connects to the GATT server hosted on the Bluetooth LE device.

     *

     * @param address The device address of the destination device.

     * @return Return true if the connection is initiated successfully. The connection result

     * is reported asynchronously through the

     * {@code BluetoothGattCallback#onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)}

     * callback.

     */

    @RequiresApi(Build.VERSION_CODES.JELLY_BEAN_MR2)

    public boolean connect(@NonNull String address) {

        MLogger.i(TAG, TAG + " .. connect()~");

        if (TextUtils.isEmpty(address)) {

            MLogger.e(TAG, "unspecify address");

            return false;

        }


        if (mBluetoothAdapter == null) {

            MLogger.d(TAG, "BluetoothAdapter is not initialized");

            return false;

        }


        // Previously connected device.  Try to reconnect.

        if (!TextUtils.isEmpty(mBluetoothDeviceAddress) && address.equals(mBluetoothDeviceAddress)

                && mBluetoothGatt != null) {

            MLogger.d(TAG, "Trying to use an existing mBluetoothGatt for connection.");

            if (mBluetoothGatt.connect()) {

                mConnectionState = BeaconContants.GATT_STATE_CONNECTING;

                return true;

            } else {

                return false;

            }

        }


        final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);

        if (device == null) {

            MLogger.w(TAG, "Device not found. Unable to connect.");

            return false;

        }


        // We want to directly connect to the device, so we are setting the autoConnect

        // parameter to false.

        mBluetoothGatt = device.connectGatt(this, false, mGattCallback);

        MLogger.d(TAG, "Trying to create a new connection.");

        mBluetoothDeviceAddress = address;

        mConnectionState = BeaconContants.GATT_STATE_CONNECTING;

        return true;

    }複製代碼

在這裏須要用到外圍設備的Mac地址用來鏈接Gatt服務。async

當鏈接創建成功以後,會觸發BluetoothGattCallback的onConnectionStateChange方法。在回調方法裏咱們就可使用BluetoothGatt的對象,調用mBluetoothGatt.discoverServices()就能夠發現外圍設備Gatt的各類數據信息。當服務數據發現完畢以後會回調BluetoothGattCallback的onServicesDiscovered方法,此時咱們使用getServices就能夠獲取外圍設備的Gatt Services數據。ui


2.讀取Gatt Characterstic

咱們提供一個BluetoothGattCharacteristic對象就能夠讀取它的特徵值。示例代碼以下:this

/**

     * Reads the requested characteristic from the associated remote device.

     *

     * <p>This is an asynchronous operation. The result of the read operation

     * is reported by the {@link BluetoothGattCallback#onCharacteristicRead}

     * callback.

     *

     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.

     *

     * @param characteristic The characteristic to read from.

     * @return true, if the read operation was initiated successfully

     * @see BluetoothGatt#readCharacteristic(BluetoothGattCharacteristic)

     */

    @RequiresApi(Build.VERSION_CODES.JELLY_BEAN_MR2)

    public boolean readCharacteristic(@NonNull BluetoothGattCharacteristic characteristic) {

        if (mBluetoothAdapter != null && mBluetoothGatt != null) {

            if (mConnectionState == BeaconContants.GATT_STATE_CONNECTED) {

                final boolean state = mBluetoothGatt.readCharacteristic(characteristic);

                MLogger.d(TAG, "readCharacteristic..state : " + state);

                return state;

            } else {

                MLogger.e(TAG, "GATT client has disconnected from GATT server!");

            }

        } else {

            MLogger.d(TAG, "BluetoothAdapter not initialized");

        }

        return false;

    }複製代碼

當咱們讀取操做成功以後,會回調BluetoothGattCallback的onCharacteristicRead方法。spa


3.寫入 Gatt Characteristics:

實際場景中咱們會有修改外圍設備參數的需求,咱們只要經過特定的服務的UUID拿到須要服務對象就能夠進行參數的修改。示例代碼以下:

/**

     * Writes a given characteristic and its values to the associated remote device.

     *

     * <p>Once the write operation has been completed, the

     * {@link BluetoothGattCallback#onCharacteristicWrite} callback is invoked,

     * reporting the result of the operation.

     *

     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.

     *

     * @param characteristic Characteristic to write on the remote device.

     * @return true, if the write operation was initiated successfully

     * @see BluetoothGatt#writeCharacteristic(BluetoothGattCharacteristic)

     */

    @RequiresApi(Build.VERSION_CODES.JELLY_BEAN_MR2)

    public boolean writeCharacteristic(@NonNull BluetoothGattCharacteristic characteristic) {

        if (mBluetoothAdapter != null && mBluetoothGatt != null) {


            if (mConnectionState == BeaconContants.GATT_STATE_CONNECTED) {

                final boolean state = mBluetoothGatt.writeCharacteristic(characteristic);

                MLogger.d(TAG, "writeCharacteristic..state : " + state

                        + "..writetype : " + characteristic.getWriteType());

                return state;

            } else {

                MLogger.e(TAG, "GATT client has disconnected from GATT server!");

            }


        } else {

            MLogger.d(TAG, "BluetoothAdapter not initialized");

        }

        return false;

    }複製代碼

當咱們的寫操做成功以後會觸發BluetoothGattCallback的onCharacteristicWrite回調方法,若是成功修改會回調onCharacteristicChanged方法。

iBeacon設備的參數修改就是基於該方法進行修改,不瞭解iBeacon設備的參數的童鞋,請閱讀:iBeacon參數。

4.打開通知功能:

若是咱們須要修改外圍設備的參數,最好仍是須要打開藍牙的notification功能,這樣的話修改特定的BluetoothGattCharacteristic就能夠接收到通知。同時安全起見,打開通知功能會觸發Android系統的輸入配對碼進行配對的功能。示例代碼以下:

/**

     * Enable or disable notifications/indications for a given characteristic.

     *

     * @param characteristic The characteristic for which to enable notifications

     * @param enable         Set to true to enable notifications

     * @param uuid           TO get a descriptor with a given UUID out of the list of

     *                       descriptors for this characteristic.

     * @return true, if the requested notification status was set successfully

     * @see BluetoothGatt#setCharacteristicNotification(BluetoothGattCharacteristic, boolean)

     */

    @RequiresApi(Build.VERSION_CODES.JELLY_BEAN_MR2)

    public boolean setCharacteristicNotification(@NonNull BluetoothGattCharacteristic characteristic,

                                                 @NonNull boolean enable, UUID uuid) {


        if (mBluetoothAdapter != null && mBluetoothGatt != null) {

            if (mConnectionState == BeaconContants.GATT_STATE_CONNECTED) {

                mBluetoothGatt.setCharacteristicNotification(characteristic, enable);

                final BluetoothGattDescriptor descriptor = characteristic.getDescriptor(uuid);

                if (descriptor != null) {


                    MLogger.d(TAG, "It's going to set notifition value : " + enable);


                    descriptor.setValue(enable ? BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE :

                            BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE);

                    final boolean state = mBluetoothGatt.writeDescriptor(descriptor);

                    MLogger.d(TAG, "setCharacteristicNotification..operation state : " + state);

                    return state;

                } else {

                    MLogger.e(TAG, "The descriptor is null or no descriptor with the given UUID was found! ");

                }

            } else {

                MLogger.e(TAG, "GATT client has disconnected from GATT server!");

            }


        } else {

            MLogger.d(TAG, "BluetoothAdapter not initialized");

        }

        return false;

    }複製代碼

執行這段代碼,會產生以下結果:

  • 當打開通知功能時,會觸發Android系統的藍牙配對功能,若是配對碼輸入正確且沒有超時時onBondStateChanged方法會有配對狀態的變化,onDescriptorWrite方法會有設置的狀態值的數組,和設置狀態;當配對碼輸入錯誤,取消配對或者配對超時,onBondStateChanged會返回配對狀態變化,onConnectionStateChange會返回鏈接狀態的變化;(注意:配對碼錯誤或者配對超時,會致使鏈接斷開!)
  • 當關閉Notify功能時,onDescriptorWrite會返回關閉Notify對應數組和狀態;


5.關閉Gatt Client:

當咱們使用完畢以後,須要關閉Gatt鏈接,同時釋放掉資源。

/**

     * After using a given BLE device, the app must call this method to ensure resources are

     * released properly.

     */

    public void close() {

        MLogger.d(TAG, TAG + " .. close()~");

        if (mBluetoothGatt == null) {

            return;

        }

        mBluetoothGatt.close();

        mBluetoothGatt = null;

    }複製代碼
相關文章
相關標籤/搜索