Android蓝牙开发详解

一、相关API详解(API>=18)


  1. BluetoothAdapter
    本地蓝牙的适配器,蓝牙交互入口,使用已知的MAC地址来实例化一个BluetoothDevice对象,支持Android4.3(API18)及以上版本
  2. BuletoothDevice
    代表一个远程的蓝牙设备,通过这个类可以查询远程设备的物理地址, 名称, 连接状态等信息;
    对象获取途径 :
    1. 调用BluetoothAdapter的getRemoteDevice(address)方法获取物理地址对应的该类对象;
    2. 调用BluetoothAdapter的getBoundedDevices()方法, 可以获取已经配对的蓝牙设备集合;


二、需要权限

  1. android.permission.BLUETOOTH : 允许程序连接到已配对的蓝牙设备, 请求连接/接收连接/传输数据 需要改权限,主要用于对配对后进行操作;
  2. android.permission.BLUETOOTH_ADMIN : 允许程序发现和配对蓝牙设备, 该权限用来管理蓝牙设备, 有了这个权限, 应用才能使用本机的蓝牙设备, 主要用于对配对前的操作;
  3. android.permission.ACCESS_FINE_LOCATION:获取精确位置 ,Android6.0以上需动态获取该权限。

三、蓝牙连接

  • 使用蓝牙API完成建立蓝牙连接的必要四步:
    1. 开启蓝牙;
    2. 查找附近已配对或可用的设备;
    3. 连接设备;
    4. 设备间数据交换。
1、 开启蓝牙

判断设备是否支持BLE

getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE);

开启蓝牙

BluetoothManager bluetoothManager =BluetoothManager)getSystemService(Context.BLUETOOTH_SERVICE);
bluetoothAdapter = bluetoothManager.getAdapter();
bluetoothAdapter.enable();
2、查找附近已配对或可用的设备

谷歌在4.3之后发布了低功耗蓝牙(BLE)的API,在安卓5.0之后又引入了新的API,原来的API已经被废弃。在新的系统里采用旧API开发的APP仍可使用,但采用新API开发的APP只能在LOLLIPOP即安卓5.0及其以后的版本使用。
扫描设备:

  • API<21

    if (bluetoothAdapter.isEnabled()) {
            bluetoothAdapter.startLeScan(leScanCallback);
        } else {
            Toast.makeText(this, "请开启蓝牙!", Toast.LENGTH_SHORT).show();
            bluetoothAdapter.enable();
       }

    leScanCallback是开启扫描后的回调函数,所有扫描出的设备信息(设备名称name、设备mac地址 address 、设备信号强度rssi)全部包含在里面:

        /**
         * 搜索蓝牙  API 21以下版本
         */
        private BluetoothAdapter.LeScanCallback leScanCallback = new BluetoothAdapter.LeScanCallback() {
            @Override
            public void onLeScan(final BluetoothDevice device, final int rssi, final byte[] scanRecord) {
                runOnMainThread(new Runnable() {
                    @Override
                    public void run() {
                        BluetoothBean bluetoothBean = new BluetoothBean();
                        bluetoothBean.setName(device.getName());
                        bluetoothBean.setAddress(device.getAddress());
                        bluetoothBean.setRssi(rssi + "");
                        EventBus.getDefault().post(new ScanEvent(bluetoothBean));
                    }
                });
    
    
            }
        };
    

    当扫描到对应设备后可使用bluetoothAdapter.stopLeScan()方法停止搜索。

  • API>=21

    if(bluetoothAdapter.isEnabled()) {
            bluetoothLeScanner=bluetoothAdapter.getBluetoothLeScanner();
            bluetoothLeScanner.startScan(scanCallback);
        }else{
            Toast.makeText(this,"请开启蓝牙!", Toast.LENGTH_SHORT).show();
            bluetoothAdapter.enable();
    }

    scanCallback同leScanCallback

       /**
         * 搜索蓝牙  API 21(包含)以上版本
         */
        private ScanCallback scanCallback = new ScanCallback() {
            @Override
            public void onScanResult(int callbackType, final ScanResult result) {
                super.onScanResult(callbackType, result);
                runOnMainThread(new Runnable() {
                    @Override
                    public void run() {
                        BluetoothBean bluetoothBean = new BluetoothBean();
                        bluetoothBean.setName(result.getDevice().getName());
                        bluetoothBean.setAddress(result.getDevice().getAddress());
                        bluetoothBean.setRssi(result.getRssi() + "");
                        EventBus.getDefault().post(new ScanEvent(bluetoothBean));
                    }
                });
    
            }
    
            @Override
            public void onBatchScanResults(List<ScanResult> results) {
                super.onBatchScanResults(results);
            }
    
            @Override
            public void onScanFailed(int errorCode) {
                super.onScanFailed(errorCode);
            }
        };

    其中onBatchScanResults为批量搜索结果,
    当扫描到对应设备后可使用bluetoothLeScanner.stopScan()方法停止搜索。

3、 连接设备

使用 bluetoothAdapter.getRemoteDevice()方法获取BluetoothDevice,其中的address参数为需要连接的设备的MAC地址,该值在扫描回调中可以获取到 ,然后调用BluetoothDevice 的connectGatt方法连接设备:

   private boolean connectDevice(String address) {
        if (bluetoothAdapter == null || address == null) {
            return false;
        }
        final BluetoothDevice device = bluetoothAdapter.getRemoteDevice(address);
        if (device == null) {
            return false;
        }
        if (bluetoothGatt != null) {
            bluetoothGatt.close();
        }
        bluetoothGatt = device.connectGatt(this, true, gattCallback);
        return true;
    }

其中gattCallback为设备连接回调,获取连接状态、读写操作均在此回调方法中进行。

在BluetoothGattCallback回调中,onConnectionStateChange为设备连接状态的回调方法,当设备开始连接、连接成功、断开连接中、断开连接后都会调用次方法:

        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
            if (newState == BluetoothProfile.STATE_CONNECTED) {
                Log.i("info", "bluetooth is connected");
                runOnMainThread(new Runnable() {
                    @Override
                    public void run() {
                        EventBus.getDefault().post(new ConnectSuccessEvent("ConnectSuccess"));
                    }
                });
                bluetoothGatt.discoverServices();
            } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
                Log.i("info", "bluetooth is disconnected");
                runOnMainThread(new Runnable() {
                    @Override
                    public void run() {
                        EventBus.getDefault().post(new DisConnectedEvent("DisConnected"));
                    }
                });
            }
        }
3、 设备间数据交换

当设备连接成功后,调用discoveryServices()发现服务,随后会进入onServicesDiscovered方法中,不调用则无法进入onServicesDiscovered方法,将无法获取到BluetoothGattService。
通过 bluetoothGatt.getServices()该方法可以获取到连接设备的所有服务(BluetoothGattService)的集合,通过遍历该集合可以获取到对应服务的特征BluetoothGattCharacteristic
通过bluetoothGattCharacteristic.getProperties()可以获取可以获取当前特征的属性,该返回值为int类型:

        @Override
        public void onServicesDiscovered(BluetoothGatt gatt, int status) {

            List<BluetoothGattService> supportedGattServices = bluetoothGatt.getServices();

            for (BluetoothGattService supportedGattService : supportedGattServices) {

                for (BluetoothGattCharacteristic bluetoothGattCharacteristic : supportedGattService.getCharacteristics()) {

                    int properties = bluetoothGattCharacteristic.getProperties();
                    if (BluetoothGattCharacteristic.PROPERTY_NOTIFY == properties) {
                       //具备通知属性
                        UUID characteristicUuid = bluetoothGattCharacteristic.getUuid();
                        UUID gattServiceUuid = supportedGattService.getUuid();
                    }

                }
            }
        }

当该值为BluetoothGattCharacteristic.PROPERTY_NOTIFY 则说明该特征具备通知属性。
当该值为BluetoothGattCharacteristic.PROPERTY_WRITE 则说明该值具备写入属性。
当该值为BluetoothGattCharacteristic.PROPERTY_READ 则说明该值具备读取属性。
此三种属性较为常用,当获取当对应的属性时,将serviceuuid和characteristicuuid保存起来即可。
例如当我们连接的蓝牙设备类似超市扫码枪,需要将扫描的条码传输到手机上时,此时则需要使用BluetoothGattCharacteristic特征的通知属性:

        @Override
        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
            BluetoothGattService service = bluetoothGatt.getService(UUID.fromString(UUID_SERVICE));
            BluetoothGattCharacteristic characteristic = service.getCharacteristic(UUID.fromString(UUID_NOTIFY));
            //收到蓝牙模块的数据后会触发onCharacteristicChanged方法
            bluetoothGatt.setCharacteristicNotification(characteristic, true);

        }

当收到蓝牙模块的数据后会触发onCharacteristicChanged方法,在onCharacteristicChanged方法中则可以获取到扫码枪返回的数据:

        @Override
        public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
            final byte[] data = characteristic.getValue();
            Log.e("info", "读取成功" + new String(data));
            runOnMainThread(new Runnable() {
                @Override
                public void run() {
                    EventBus.getDefault().post(new BluetoothScanResultEvent(new String(data)));
                }
            });
        }

该返回数据可能是原样数据,也可能返回的是16进制数据,具体根据硬件供应商的标准转换。

需要往设备里面写入数据时,将BluetoothGattCharacteristic换成具有写入属性BluetoothGattCharacteristic的,写入数据即可:

//设置数据内容
characteristic.setValue("send data->");
//往蓝牙模块写入数据
bluetoothGatt.writeCharacteristic(characteristic);

写入成功后会调用onCharacteristicWrite方法。

最后,附上该Demo的传送门:https://github.com/yfwang0810/Bluetooth
如有不足之处,欢迎大佬们指点一二。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值