Android——Bluetooth 开发(五)

蓝牙 4.0

蓝牙4.0集成了传统蓝牙和低功耗蓝牙两个标准,所以蓝牙4.0有双模和单模之分。双模即传统蓝牙部分+低功耗蓝牙部分,单模即是单纯的低功耗蓝牙部分(BLE)。

蓝牙操作流程

蓝牙开发之前需要在 AndroidManifest.xml 中申请蓝牙相关权限

    
    <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="true" />
    
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

蓝牙操作基本可以分为五步:

  • 打开蓝牙
  • 扫描设备
  • 连接设备
  • 控制设备
  • 接收数据

Android BLE 相关功能及 API

说明

  • 服务:每个蓝牙设备,都有多个服务,每个服务都有不同的作用,我们可以根据蓝牙协议提供的 service uuid 找出 相应的service。
  • 特征值:蓝牙数据传输的载体。每个服务里包含多个特征值,每个特征值都有自己的特性(读、写或通知等)。

1、判断蓝牙是否打开

  • bluetoothAdapter.isEnabled(),true 蓝牙已打开;false 蓝牙未打开,需要跳转到设置页面打开蓝牙。
  • 跳转蓝牙设置页面,打开蓝牙

    Intent intent = new Intent(Settings.ACTION_BLUETOOTH_SETTINGS);
    startActivity(intent);

2、获取 bluetoothAdapter 并扫描蓝牙

蓝牙打开后,我们首先需要获取蓝牙适配器 bluetoothAdapter,获取蓝牙适配器后,就可以进行蓝牙扫描操作。

  • 获取 bluetoothAdapter
    public void openBle() {
        if (null != mBtAdapter) {
            return;
        }
        BluetoothManager manager = (BluetoothManager) mContext.getSystemService(BLUETOOTH_SERVICE);
        if (null != manager) {
            mBtAdapter = manager.getAdapter();
        }
        if (null == mBtAdapter) {
            mBtAdapter = BluetoothAdapter.getDefaultAdapter();
        }
        //设备不支持蓝牙功能
        if (mBtAdapter == null) {
            Toast.makeText(mContext, "设备不支持蓝牙功能!", Toast.LENGTH_LONG).show();
        }
    }
  • 蓝牙扫描
    在 Android 6.0 以后扫描蓝牙需要有位置权限,否则扫描不到附近的设备。
            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
                mBtAdapter.startLeScan(mLeScanCallback);
            } else {
                //需要 sdk 版本在 21(Android 5.0) 以上才可以定义使用
                mBtAdapter.getBluetoothLeScanner().startScan(mScanCallback);

            }
           //bluetoothAdapter.startDiscovery(); //该方法不建议使用

3、扫描 BLE 返回

蓝牙扫描开始后,就会有设备进行返回,返回的数据会出现重复,需要我们根据自身需求进行去重操作,并显示在列表中。

扫描结果有三种:

  • 广播接收扫描结果(BroadcastReceiver)

⚠️ 需要调用bluetoothAdapter.startDiscovery() 方法进行扫描,并且注册 ACTION_FOUND 广播。

  • Android 5.0 以下API
    private BluetoothAdapter.LeScanCallback mLeScanCallback =
            new BluetoothAdapter.LeScanCallback() {

                @Override
                public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {
                        //蓝牙扫描回调
                        mCallBackResult.onScanResult(scanResult);
                    }
                }
            };

⚠️ 需要使用 mBtAdapter.startLeScan(mLeScanCallback);进行扫描

  • Android 5.0 及以上( sdk 为 21 以上)
                mScanCallback = new ScanCallback() {
                    @Override
                    public void onScanResult(int callbackType, android.bluetooth.le.ScanResult result) {
                        super.onScanResult(callbackType, result);
                        scanType = SCAN_RESULT_TYPE_TWO;
                        // sdk 版本在 21(Android 5.0) 以上扫描结果处理
                        mCallBackResult.onScanResult(scanResult);
                    }

                    @Override
                    public void onBatchScanResults(List results) {
                        super.onBatchScanResults(results);
                    }

                    @Override
                    public void onScanFailed(int errorCode) {
                        super.onScanFailed(errorCode);
                    }
                };

⚠️ 需要使用mBtAdapter.getBluetoothLeScanner().startScan(mScanCallback); 进行蓝牙扫描

4、设备连接

扫描到我们所需要的设备后,下一步就要进行设备连接。连接设备可以对扫描到的设备 BluetoothDevice 进行连接,也可以根据 macAddress 进行连接(其实也是根据macAddress 找到设备 BluetoothDevice 进行连接)

    public boolean connect(final String address) {
        if (mBluetoothAdapter == null || address == null) {
            Log.w(TAG, "BluetoothAdapter not initialized or unspecified address.");
            return false;
        }

        if (mBluetoothDeviceAddress != null && address.equals(mBluetoothDeviceAddress) && mBluetoothGatt != null) {
            Log.d(TAG, "Trying to use an existing mBluetoothGatt for connection.");
            mBluetoothGatt.disconnect();
            mBluetoothGatt.close();
        }

        final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
        if (device == null) {
            Log.w(TAG, "Device not found.  Unable to connect.");
            return false;
        }
        //设备连接
        mBluetoothGatt = device.connectGatt(this, false, mGattCallback);
        mBluetoothDeviceAddress = address;
        return true;
    }

以上的代码中提到了 BluetoothGatt 和 BluetoothGattCallback。

  • BluetoothGatt:它是操作 BLE 的主要类,蓝牙的连接、断开、发现蓝牙服务、注册通道、发送命令等都会用到它。
  • BluetoothGattCallback 是关于蓝牙操作的回调,里面包含了连接是否成功回调、服务发现回调、蓝牙返回数据回调、写命令成功回调等十二个回调方法。

5、蓝牙操作

  • 向蓝牙写入命令
    向蓝牙写入命令,特征值必须有“写”的特性,才允许向蓝牙发送控制指令。

        public void writeRXCharacteristic(byte[] value) {
    
        Log.w("---Write","byte[] is "+ Arrays.toString(value)+ "\nHex is "+BleUtils.bytes2HexString(value));
    
        if(mBluetoothGatt == null)
        {
            System.out.println("=================================================================");
        }
        // 根据 UUID 获取蓝牙服务
        BluetoothGattService RxService = mBluetoothGatt.getService(RX_SERVICE_UUID);
        if (RxService == null) {
            showMessage("Rx service not found!");
            return;
        }
        // 根据 UUID 获取“写”的特征值
        BluetoothGattCharacteristic RxChar = RxService.getCharacteristic(RX_CHAR_UUID);
        if (RxChar == null) {
            showMessage("Rx charateristic not found!");
            return;
        }
        //给特征值 赋值(向蓝牙传递的命令,十六进制转 byte 数组)
        RxChar.setValue(value);
        //蓝牙写操作
        boolean status = mBluetoothGatt.writeCharacteristic(RxChar);
    }
  • 注册“读”特性特征值
    只能向有“读”特性的特征值注册读 操作

    public void readCharacteristic(BluetoothGattCharacteristic characteristic) {
        if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_READ) !=0) {
            if (mBluetoothAdapter == null || mBluetoothGatt == null) {
                Log.w(TAG, "BluetoothAdapter not initialized");
                return;
            }
            mBluetoothGatt.readCharacteristic(characteristic);
        }
    }

6、断开连接和关闭蓝牙

  • 断开蓝牙连接

    public void disconnect() {
        if (mBluetoothAdapter == null || mBluetoothGatt == null) {
            Log.w(TAG, "BluetoothAdapter not initialized");
            return;
        }
        mBluetoothGatt.disconnect();
    }
  • 关闭蓝牙操作

        public void close() {
        if (mBluetoothGatt == null) {
            return;
        }
        Log.w(TAG, "mBluetoothGatt closed");
        mBluetoothDeviceAddress = null;
        mBluetoothGatt.close();
        mBluetoothGatt = null;
    }

7、停止扫描

根据扫描方式的不同,停止扫描的方法也是不同:

```java
/**
 * 停止扫描
 */
public void stopScan() {
    switch (scanType) {
        case SCAN_RESULT_TYPE_ONE:
            mBtAdapter.stopLeScan(mLeScanCallback);//Android 5.0 以下(sdk 21以下)
            break;
        case SCAN_RESULT_TYPE_TWO:
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                //Android 5.0 及以上(sdk 21以上)
                mBtAdapter.getBluetoothLeScanner().stopScan(mScanCallback);
            }
            break;
        case SCAN_RESULT_TYPE_THREE:
            //使用 bluetoothAdapter.startDiscovery() 方法扫描设备
            mBtAdapter.cancelDiscovery();
            break;
        default:
            break;
    }
}
```

8、BluetoothGattCallback 回调方法

  • onConnectionStateChange(BluetoothGatt gatt, int status, int newState)

    设备连接状态回调。当 newState == BluetoothGatt.STATE_CONNECTED 时代表设备了解成功,可以进行服务扫描了 mBluetoothGatt.discoverServices()

    newState == BluetoothGatt.STATE_DISCONNECTED 时,代表设备断开连接。

  • onServicesDiscovered(BluetoothGatt gatt, int status)、

    发现蓝牙服务回调。可以进行特征值筛选,通道注册等操作。其中可以根据特征值的特性进行特征值注册和筛选。例如:BluetoothGattCharacteristic 的property值 等于 0x02 则只有“读”的特性;等于 0x04 为“写”的特性,但是不回调;等于 0x08 是“写”的特性,能够回调等。(详情参考BluetoothGattCharacteristic 类)

    以下是注册读取通道的方法:

     public void enableTXNotification()
    {
    
        if (mBluetoothGatt == null) {
            showMessage("mBluetoothGatt null" + mBluetoothGatt);
            return;
        }
        //根据 UUID 获取蓝牙服务
        BluetoothGattService RxService = mBluetoothGatt.getService(RX_SERVICE_UUID);
        if (RxService == null) {
            showMessage("Rx service not found!");
            return;
        }
        //根据 UUID 获取“读”特征值
        BluetoothGattCharacteristic TxChar = RxService.getCharacteristic(TX_CHAR_UUID);
        if (TxChar == null) {
            showMessage("Tx charateristic not found!");
            return;
        }
        //注册通知
        mBluetoothGatt.setCharacteristicNotification(TxChar,true);
         // 对特征值进行描述
        BluetoothGattDescriptor descriptor = TxChar.getDescriptor(CCCD);
        descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
        mBluetoothGatt.writeDescriptor(descriptor);
    }
  • onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic)
    蓝牙设备数据返回回调
  • onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status)
    读操作的回调。一般情况下不会使用该方法
  • onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status)
    写操作回调

总结

在蓝牙开发过程中,主要是弄清蓝牙协议,每个蓝牙设备的协议是不一样的,握手方式也是各不相同。对于向蓝牙发送命令要格外注意,稍有不慎命令编码就会出错。对于蓝牙返回的数据,也是多种多样,我们要分别处理。最后,由于蓝牙开发很多都是异步操作,为了节省Activity的开销,最好蓝牙相关操作放到service内进行。

你可能感兴趣的:(Android提升,bluetooth)