Android蓝牙开发详解

来源:互联网 发布:淘宝刷访问量 编辑:程序博客网 时间:2024/05/16 17:57

一、相关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
如有不足之处,欢迎大佬们指点一二。

原创粉丝点击