Android 低功耗藍芽的那點事

程式設計師呂小布發表於2017-03-26

近期專案使用到了藍芽技術,菜鳥一枚,網上各種找資料,發現不是不全就是過時,要麼就是抄襲轉載,真實醉了,現在將這一塊的東西整理出來,供大家參考。

基本概念

Android中的藍芽分兩種:經典藍芽、低功耗藍芽。

  1. 二者本質上沒有太多區別,可以理解為後者是前者的升級優化版本。對於API上的實現區別還是很大的。
  2. 工作流程:發現裝置->配對/繫結裝置->建立連線->資料通訊。
    至於底層如何工作,本人不瞭解,也不是本文關注的重點。
  3. 官方文件:https://developer.android.com/guide/topics/connectivity/bluetooth.html?hl=zh-cn

重要例項

  1. 經典藍芽聊天例項:https://github.com/googlesamples/android-BluetoothChat
  2. 低功耗藍芽:https://github.com/googlesamples/android-BluetoothLeGatt
  3. 作為外設(API>=21):https://github.com/googlesamples/android-BluetoothAdvertisements
  4. 基礎概念講解:http://blog.csdn.net/qinxiandiqi/article/details/40741269
  5. 深入理論:http://www.race604.com/android-ble-in-action/

注意事項

  1. 低功耗藍芽(BLE)Android 4.3(API 18)以上才支援
  2. 使用藍芽需要許可權
    <uses-permission android:name="android.permission.BLUETOOTH" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
  3. Android 5.0(API 21) 掃描藍芽需要定位許可權,否則掃描不到裝置,實際使用時候發現 5.0不需要也可以掃描,Android 6.0(API 23)以上必須(不知道什麼原因,測試機器:MI 2,知道原因的可告知)
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/> 或
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

    官方文件:https://developer.android.com/guide/topics/connectivity/bluetooth-le.html?hl=zh-cn

  4. 低功耗藍芽要宣告特徵,或者程式碼判斷
    // 如果為true表示只能在支援低功耗藍芽的裝置上使用,如果不支援的裝置也可以使用,採用程式碼判斷
    <uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>
    // 程式碼判斷
    if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
        Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_SHORT).show();
        finish();
    }
  5. 經典藍芽連線成功後獲取一個socket連線得到輸入輸出流進行通訊,低功耗通過特徵(具體實現不知道是什麼)
  6. Android 5.0(API 21)之前不能當成外設(藍芽耳機、音響等)來使用,只能作為中心即主機

低功耗藍芽

現在藍芽開發基本上都是低功耗藍芽,比如心率裝置、耳機裝置、手環,所以我們先從重要的開始,講講低功耗藍芽的使用,後面在完善經典藍芽。

宣告許可權

建立專案在AndroidManifest.xml中宣告許可權:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.lowett.android">
  // 定位許可權第二個包含第一個,所以這裡就宣告瞭一個,兩個都宣告也可以
  <!-- <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/> -->
  <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> 
  <uses-permission android:name="android.permission.BLUETOOTH"/>
  <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
  // 是否必須支援低功耗藍芽,此處不必須
   <uses-feature
      android:name="android.hardware.bluetooth_le"
      android:required="false"/>
   // 是有gps硬體,這個現在的智慧手機沒有不支援的吧
  <uses-feature android:name="android.hardware.location.gps"/>

  </manifest>

如果manifest中宣告的藍芽特性為false,那麼在程式碼中監測是否支援BLE特性,

// 使用此檢查確定BLE是否支援在裝置上,然後你可以有選擇性禁用BLE相關的功能
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
    Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_SHORT).show();
    finish();
}

BluetoothAdapter

獲取BluetoothManager得到藍芽介面卡BluetoothAdapter,注意這兩個類都是系統級別,只有一個,代表了你手機上的藍芽模組

final BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
BluetoothAdapter bluetoothAdapter = bluetoothManager.getAdapter();

如果獲取到的Adapter為空說明不支援藍芽,或者沒有藍芽模組

開啟藍芽

前面獲取到了BluetoothAdapter,可以通過呼叫isEnabled()函式去檢測是否開啟了藍芽,false表示沒有開啟,如果沒有開啟需要去啟用,

Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
activity.startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);

程式碼執行完後,會有彈框提示使用者是否啟用,我們需要在onActivityResult()中判斷返回

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == BluetoothLeManager.REQUEST_ENABLE_BT) {
        if (resultCode == Activity.RESULT_OK) {
           // something, 去掃描裝置
            startScan();
        } else {
            new AlertDialog.Builder(this)
                    .setMessage("請開啟藍芽,連線裝置")
                    .setPositiveButton(R.string.confirm, new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            dialog.dismiss();
                           // something
                        }
                    })
                    .create()
                    .show();
        }
    }
}

掃描裝置

低功耗藍芽和經典藍芽的掃描方式不同,如果熟悉經典藍芽,那就要可能掉進坑了起不來了,哈哈。藍芽掃描耗電比較嚴重,所以此處一定要記得在合適的實際停止掃描,比如定時停止、掃描到目標裝置就停止(奇怪的是API為何不提供掃描時長的介面呢?)掃描時呼叫Adapter的startLeScan()方法,然而這個被標記為過時,API>=21被另一個取代。然後通過回掉得到掃描結果(經典藍芽是廣播)

public void startScan() {
    // 初始化一個handler
        initHandler();
        if (!mScanning) {
            if (scanRunnable == null) {
                scanRunnable = new Runnable() {
                    @Override
                    public void run() {
                        stopScan();
                    }
                };
            }

          // SCAN_PERIOD = 3 * 10 * 1000, 30s後停止掃面
            mHandler.postDelayed(scanRunnable, SCAN_PERIOD);
// 新API
//            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
//                mBluetoothAdapter.getBluetoothLeScanner().startScan(this);
//            }else {
          // this 實現了BluetoothAdapter.LeScanCallback,即掃描結果回掉
                mBluetoothAdapter.startLeScan(this);
//            }
            mScanning = true;
            Log.i(TAG, "開始掃描,藍芽裝置");
        }
    }

回撥函式

@Override
public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {
 // 得到掃描結果
    Log.i(TAG, "掃描到的裝置, name=" + device.getName() + ",address=" + device.toString());
}

注意:device:代表外設即目標裝置 rssi:設一個強度值,但是時負值,利用這個值通過公式可以算出離你的距離

scanRecord:廣播資料,附加的資料,沒用到

停止掃描

掃描完成務必停止,因為掃描不僅耗電,還影響連線速度,所以當要連線的時候,先停止掃描時必須的

public void stopScan() {
    initHandler();
    Log.i(TAG, "停止掃描,藍芽裝置");
    if (mScanning) {
        mScanning = false;
        // 開始掃描的介面,要一樣的不然停止不了
        mBluetoothAdapter.stopLeScan(this);
    }

    if (scanRunnable != null) {
        mHandler.removeCallbacks(scanRunnable);
        scanRunnable = null;
    }
}

連線裝置

通常連線裝置速度還是很快的,連線理論上來說也是無狀態的,所以也需要一個定式任務來,保證超時停止。

public boolean connect(Context context, String address) {
    if (mConnectionState == STATE_CONNECTED) {
        return false;
    }
    if (mBluetoothAdapter == null || TextUtils.isEmpty(address)) {
        return false;
    }
    initHandler();
    if (connectRunnable == null) {
        connectRunnable = new Runnable() {
            @Override
            public void run() {
                mConnectionState = STATE_DISCONNECTING;
                disconnect();
            }
        };
    }
  // 30s沒反應停止連線
    mHandler.postDelayed(connectRunnable, 30 * 1000);
    stopScan();
// 獲取到遠端裝置,
    final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
    if (device == null) {
        return false;
    }
    // 開始連線,第二個參數列示是否需要自動連線,true裝置靠近自動連線,第三個表示連線回撥
    mBluetoothGatt = device.connectGatt(context, false, mGattCallback);
    mBluetoothDeviceAddress = address;
    mConnectionState = STATE_CONNECTING;
    return true;
}

監聽連線回撥

private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
// 連線狀態變化
    @Override
    public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
        if (newState == BluetoothProfile.STATE_CONNECTED) { // 連線上
            mConnectionState = STATE_CONNECTED;
            boolean success = mBluetoothGatt.discoverServices(); // 去發現服務
            Log.i(TAG, "Attempting to start service discovery:" +
                    success);
        } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { // 連線斷開
            mConnectionState = STATE_DISCONNECTED;       
        }
    }

// 發現服務
    @Override
    public void onServicesDiscovered(BluetoothGatt gatt, int status) {
        if (status == BluetoothGatt.GATT_SUCCESS) {
            Log.i(TAG,"發現服務");
            // 解析服務
            discoverService();
        }
    }

// 特徵讀取變化
    @Override
    public void onCharacteristicRead(BluetoothGatt gatt,
                                     BluetoothGattCharacteristic characteristic,
                                     int status) {
        if (status == BluetoothGatt.GATT_SUCCESS) {

        }
    }

// 收到資料
    @Override
    public void onCharacteristicChanged(BluetoothGatt gatt,
                                        BluetoothGattCharacteristic characteristic) {
        mConnectionState = STATE_CONNECTED;

    }
};

斷開連線

public void disconnect() {
    if (mBluetoothAdapter == null || mBluetoothGatt == null) {
        mConnectionState = STATE_DISCONNECTED;
        return;
    }
    // 連線成功的GATT
    mBluetoothGatt.disconnect();
    mBluetoothGatt.close();
}

多裝置連線

藍芽介面卡沒有聽過連線多個裝置的介面,需要我們自己實現,即獲取到目標裝置的address後呼叫連線方法,自己維護多個BluetoothGatt即可(程式碼稍後放出)。

完整程式碼示例

正在做心率相關的藍芽裝置,此處程式碼發出來。

程式碼還在完善中,僅供參考,穩定程式碼將會發在Github中

package com.lowett.android.ble;

import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Handler;
import android.os.Looper;
import android.support.annotation.IntDef;
import android.text.TextUtils;

import com.fit.android.utils.Logger;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.List;
import java.util.Locale;
import java.util.UUID;

/**
 * Email: fvaryu@qq.com
 */
public class BluetoothLeManager implements BluetoothAdapter.LeScanCallback {
    public static final int REQUEST_ENABLE_BT = 1;
    private static final int SCAN_PERIOD = 3 * 10 * 1000;

    static final int STATE_DISCONNECTED = 1;
    public static final int STATE_CONNECTING = 2;
    public static final int STATE_DISCONNECTING = 3;
    public static final int STATE_CONNECTED = 4;
    public static final int STATE_DISCOVER_SERVICES = 5;

    @IntDef(value = {STATE_DISCONNECTED, STATE_CONNECTING, STATE_CONNECTED, STATE_DISCONNECTING, STATE_DISCOVER_SERVICES})
    @Retention(RetentionPolicy.SOURCE)
    public @interface State {
    }

    public final static UUID UUID_HEART_RATE_MEASUREMENT =
            UUID.fromString(SampleGattAttributes.HEART_RATE_MEASUREMENT);

    private static BluetoothLeManager ourInstance = new BluetoothLeManager();

    //    private Context mContext;
    private boolean is_inited = false;

    private android.bluetooth.BluetoothManager mBluetoothManager;
    private BluetoothAdapter mBluetoothAdapter;
    private String mBluetoothDeviceAddress;
    private int mConnectionState;
    private BluetoothGatt mBluetoothGatt;

    private boolean mScanning;
    private Runnable scanRunnable;
    private Handler mHandler;

    private Runnable connectRunnable;

    private OnDataReceivedListener mOnDataReceivedListener;
    // 記得清掉監聽 洩漏
    private OnLeScanListener mOnLeScanListener;
    private OnConnectionStateChangeListener mOnConnectionStateChangeListener;

    private int retryCount;

    public static BluetoothLeManager getInstance() {
        return ourInstance;
    }

    private BluetoothLeManager() {
    }

    public void setOnDataReceivedListener(OnDataReceivedListener onDataReceivedListener) {
        mOnDataReceivedListener = onDataReceivedListener;
    }

    public interface OnConnectionStateChangeListener {
        void onConnectionStateChange(BluetoothGatt gatt, int status, int newState);

        void onConnectTimeout();
    }

    public void setOnConnectionStateChangeListener(OnConnectionStateChangeListener onConnectionStateChangeListener) {
        mOnConnectionStateChangeListener = onConnectionStateChangeListener;
    }

    public interface OnLeScanListener {
        void onLeScan(BluetoothDevice device);
    }

    public interface OnDataReceivedListener {
        void onDataReceived(int heart);
    }

    private void init() {
        if (!is_inited) {
            is_inited = true;
        }
    }

    private boolean initialize(Context context) {
        init();
        if (!is_inited) {
            throw new RuntimeException("請先呼叫init");
        }

        if (mBluetoothAdapter != null) {
            return true;
        }
        // For API level 18 and above, get a reference to BluetoothAdapter through
        // BluetoothLeManager.
        if (mBluetoothManager == null) {
            mBluetoothManager = (BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE);
            if (mBluetoothManager == null) {
                return false;
            }
        }

        mBluetoothAdapter = mBluetoothManager.getAdapter();
        return mBluetoothAdapter != null;
    }

    public boolean isSupportBluetoothLe(Activity activity) {
        return activity.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE);
    }

    public boolean isSupportBluetooth(Context context) {
        return initialize(context);
    }

    public void enableBluetooth(Activity activity) {
        if (!initialize(activity)) {
            return;
        }
        if (!mBluetoothAdapter.isEnabled()) {
            Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            activity.startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
        }
    }

    public boolean isEnabled(Context context) {
        return initialize(context) && mBluetoothAdapter.isEnabled();
    }

    private void initHandler() {
        if (mHandler == null) {
            mHandler = new Handler(Looper.getMainLooper());
        }
    }

    public void startScan(OnLeScanListener onLeScanListener) {
        initHandler();
        if (!mScanning) {
            if (scanRunnable == null) {
                scanRunnable = new Runnable() {
                    @Override
                    public void run() {
                        stopScan();
                    }
                };
            }

            mHandler.postDelayed(scanRunnable, SCAN_PERIOD);

//            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
//                mBluetoothAdapter.getBluetoothLeScanner().startScan(this);
//            }else {
            mBluetoothAdapter.startLeScan(this);
//            }

            mScanning = true;

            this.mOnLeScanListener = onLeScanListener;

            Logger.i("開始掃描,藍芽裝置");
        }
    }

    @Override
    public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {
        final BluetoothDevice tmp = device;
        Logger.i("掃描到的裝置, name=" + device.getName() + ",address=" + device.toString());
        if (mOnLeScanListener != null) {
            mHandler.post(new Runnable() {
                @Override
                public void run() {
                    mOnLeScanListener.onLeScan(tmp);
                }
            });
        }
    }

    public void stopScan() {
        initHandler();
        mOnLeScanListener = null;
        Logger.i("停止掃描,藍芽裝置");
        if (mScanning) {
            mScanning = false;
            mBluetoothAdapter.stopLeScan(this);
        }

        if (scanRunnable != null) {
            mHandler.removeCallbacks(scanRunnable);
            scanRunnable = null;
        }
    }

    private void removeConnectRunnable() {
        if (connectRunnable != null) {
            mHandler.removeCallbacks(connectRunnable);
            connectRunnable = null;
        }
    }

    private void retry() {
        if (TextUtils.isEmpty(mBluetoothDeviceAddress)) {
            return;
        }
        mHandler.postDelayed(new Runnable() {
            @Override
            public void run() {
                if (++retryCount < 11 && mConnectionState < STATE_CONNECTED) {
                    reconnect(retryCount);
                    mHandler.postDelayed(this, retryCount * 5 * 1000);
                    Logger.i("藍芽重試次數=" + retryCount);
                }

            }
        }, 2000);
    }

    private void reconnect(int count) {
        if ((mConnectionState >= STATE_CONNECTING)) {
            return;
        }

        if (connectRunnable == null) {
            connectRunnable = new Runnable() {
                @Override
                public void run() {
                    mConnectionState = STATE_DISCONNECTING;
                    disconnect();
                }
            };
        }
        mHandler.postDelayed(connectRunnable, count * 3 * 1000);

        if (mBluetoothDeviceAddress != null
                && mBluetoothGatt != null) {
            mBluetoothGatt.connect();
            mConnectionState = STATE_CONNECTING;
        }
    }

    public boolean connect(Context context, String address) {
        if (mConnectionState == STATE_CONNECTED) {
            return false;
        }
        if (mBluetoothAdapter == null || TextUtils.isEmpty(address)) {
            return false;
        }
        initHandler();
        if (connectRunnable == null) {
            connectRunnable = new Runnable() {
                @Override
                public void run() {
                    mConnectionState = STATE_DISCONNECTING;
                    disconnect();

                    if (mOnConnectionStateChangeListener != null) {
                        mOnConnectionStateChangeListener.onConnectTimeout();
                    }
                }
            };
        }
        mHandler.postDelayed(connectRunnable, 30 * 1000);

        stopScan();

        // Previously connected device.  Try to reconnect.
        if (mBluetoothDeviceAddress != null && address.equals(mBluetoothDeviceAddress)
                && mBluetoothGatt != null) {
            if (mBluetoothGatt.connect()) {
                mConnectionState = STATE_CONNECTING;
                return true;
            } else {
                return false;
            }
        }

        final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
        if (device == null) {
            return false;
        }
        // We want to directly connect to the device, so we are setting the autoConnect
        // parameter to false.
        mBluetoothGatt = device.connectGatt(context, false, mGattCallback);
        mBluetoothDeviceAddress = address;
        mConnectionState = STATE_CONNECTING;
        return true;
    }

    public void disconnect() {
        if (mBluetoothAdapter == null || mBluetoothGatt == null) {
            Logger.i("BluetoothAdapter not initialized");
            mConnectionState = STATE_DISCONNECTED;
            return;
        }

        mBluetoothGatt.disconnect();

    }

    public void close() {
        disconnect();
        if (mBluetoothGatt == null) {
            return;
        }
        mBluetoothGatt.close();
        mBluetoothGatt = null;
    }

    public void disconnectNoRetry() {
        mBluetoothDeviceAddress = null;
        close();
    }

    private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {

        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
            if (newState == BluetoothProfile.STATE_CONNECTED) {
                mConnectionState = STATE_CONNECTED;
                boolean success = mBluetoothGatt.discoverServices();
                Logger.i("Attempting to start service discovery:" +
                        success);
                removeConnectRunnable();

                Logger.i("連結上");
            } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
                mConnectionState = STATE_DISCONNECTED;
                Logger.i("斷開連結");

                retry();
            }

            if (mOnConnectionStateChangeListener != null) {
                mOnConnectionStateChangeListener.onConnectionStateChange(gatt, status, newState);
            }
        }

        @Override
        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
            if (status == BluetoothGatt.GATT_SUCCESS) {
                Logger.i("發現服務");
                discoverService();
            }

        }

        @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);
        }
    };

    /**
     * Retrieves a list of supported GATT services on the connected device. This should be
     * invoked only after {@code BluetoothGatt#discoverServices()} completes successfully.
     *
     * @return A {@code List} of supported services.
     */
    private List<BluetoothGattService> getSupportedGattServices() {
        if (mBluetoothGatt == null) return null;
        return mBluetoothGatt.getServices();
    }

    private void discoverService() {
        if (mConnectionState == STATE_DISCOVER_SERVICES) {
            return;
        }
        mConnectionState = STATE_DISCOVER_SERVICES;
        List<BluetoothGattService> list = getSupportedGattServices();
        /**
         *  BluetoothGattService = 00001800-0000-1000-8000-00805f9b34fb
         BluetoothGattCharacteristic = 00002a00-0000-1000-8000-00805f9b34fb
         BluetoothGattCharacteristic = 00002a01-0000-1000-8000-00805f9b34fb
         BluetoothGattCharacteristic = 00002a04-0000-1000-8000-00805f9b34fb

         BluetoothGattService = 00001801-0000-1000-8000-00805f9b34fb
         BluetoothGattCharacteristic = 00002a05-0000-1000-8000-00805f9b34fb

         心跳服務
         BluetoothGattService = 0000180d-0000-1000-8000-00805f9b34fb
         心跳特徵
         BluetoothGattCharacteristic = 00002a37-0000-1000-8000-00805f9b34fb
         BluetoothGattCharacteristic = 00002a38-0000-1000-8000-00805f9b34fb

         BluetoothGattService = 0000180f-0000-1000-8000-00805f9b34fb
         BluetoothGattCharacteristic = 00002a19-0000-1000-8000-00805f9b34fb

         // 裝置名字
         BluetoothGattService = 0000180a-0000-1000-8000-00805f9b34fb
         BluetoothGattCharacteristic = 00002a28-0000-1000-8000-00805f9b34fb
         */
        for (BluetoothGattService s : list) {
            if (!SampleGattAttributes.HEART_RATE_SERVICES.equals(s.getUuid().toString())) {
                continue;
            }
            final List<BluetoothGattCharacteristic> l = s.getCharacteristics();
            for (final BluetoothGattCharacteristic bc : l) {
                if (!SampleGattAttributes.HEART_RATE_MEASUREMENT.equals(bc.getUuid().toString())) {
                    continue;
                }
                Logger.i("連線藍芽 服務成功");
                setCharacteristicNotification(bc, true);
                return;
            }
        }
    }

    private void broadcastUpdate(final BluetoothGattCharacteristic characteristic) {

        // 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;
            } else {
                format = BluetoothGattCharacteristic.FORMAT_UINT8;
            }
            int heartRate = characteristic.getIntValue(format, 1);
            Logger.i(String.format(Locale.getDefault(), "Received heart rate: %d", heartRate));
            if (mOnDataReceivedListener != null) {
                mOnDataReceivedListener.onDataReceived(heartRate);
            }
        }
//        else {
        // For all other profiles, writes the data formatted in HEX.
//            final byte[] data = characteristic.getValue();
//            if (data != null && data.length > 0) {
//                final StringBuilder stringBuilder = new StringBuilder(data.length);
//                for (byte byteChar : data)
//                    stringBuilder.append(String.format("%02X ", byteChar));
//                intent.putExtra(EXTRA_DATA, new String(data) + "\n" + stringBuilder.toString());
//            }
//        }
        /**
         * 2、
         */
//        sendBroadcast(intent);
    }

    public boolean isConnected() {
        return mConnectionState == STATE_CONNECTED;
    }

    public String getConnectedAddress() {
        if (!isConnected()) {
            return null;
        }
        return mBluetoothDeviceAddress;
    }

    public void readCharacteristic(BluetoothGattCharacteristic characteristic) {
        if (mBluetoothAdapter == null || mBluetoothGatt == null) {
            Logger.i("BluetoothAdapter not initialized");
            return;
        }
        mBluetoothGatt.readCharacteristic(characteristic);

    }

    private void setCharacteristicNotification(BluetoothGattCharacteristic characteristic,
                                               boolean enabled) {
        if (mBluetoothAdapter == null || mBluetoothGatt == null) {
            Logger.i("BluetoothAdapter not initialized");
            return;
        }
        mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);

        // This is specific to Heart Rate Measurement.
        if (UUID_HEART_RATE_MEASUREMENT.equals(characteristic.getUuid())) {
            BluetoothGattDescriptor descriptor = characteristic.getDescriptor(
                    UUID.fromString(SampleGattAttributes.CLIENT_CHARACTERISTIC_CONFIG));
            descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
            mBluetoothGatt.writeDescriptor(descriptor);
        }
    }

    public void clear() {
        mOnLeScanListener = null;
        mOnConnectionStateChangeListener = null;
    }

    public void release() {
//        connectRunnable = null;
//        mHandler = null;
//         ourInstance = null;
    }
}

總結

專案還在進行中,遇到的問題將會持續丟擲,完善此文件。

相關文章