Android BLE开发

本文详细介绍蓝牙低能耗(BLE)技术的基础知识与开发流程,涵盖Android与iOS平台的权限配置、蓝牙初始化、设备扫描与连接、数据收发及通知设置等内容。

蓝牙ble(Bluetooth Low Energy):蓝牙低能耗技术蓝牙4.0

适用范围:Android4.3之后才支持BLE;iOS7.0开始支持BLE,Swift语言需要iOS8.0以上,硬件要求iPhone4s以上(包含4s)

蓝牙通信关系:主从关系,每一对设备进行蓝牙通讯时,必须一个为中心设备central,另一个为从设备peripheral.通信时必须central进行查找,发起配对,连接成功后双方可收发数据.一个central可以最多同时连接7个从设进行通讯,一个peripheral只能连接一个central.

Android开发手机做central:

1 获取权限

//判断手机是否有硬件BLE模块

<uses-feature

        android:name="android.hardware.bluetooth_le"

        android:required="false" />

//获取权限

 <uses-permission android:name="android.permission.BLUETOOTH" />

//允许程序连接到已配对的蓝牙设备

  <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />

//允许程序发现和配对蓝牙设备

Android6.0蓝牙权限需要添加模糊定位权限

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

模糊定位是Dangerous Permission,涉及到用户隐私,使用时需要用户实时授权:

在代码中添加首次进入判断://动态运行时权限

 private void checkBluetoothPermission() {
         if (Build.VERSION.SDK_INT >= 23) {
             //校验是否已具有模糊定位权限
             if (ContextCompat.checkSelfPermission(MainActivity.this,
                     Manifest.permission.ACCESS_COARSE_LOCATION)
                     != PackageManager.PERMISSION_GRANTED) {
                 ActivityCompat.requestPermissions(MainActivity.this,
                        new String[]{Manifest.permission.ACCESS_COARSE_LOCATION},
                         MY_PERMISSIONS_REQUEST_ACCESS_COARSE_LOCATION);
             } else {
             //具有权限
             }
         } else {
             //系统不高于6.0直接执行
         }
     }
 @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        doNext(requestCode, grantResults);
    }
2 初始化蓝牙外设

   获取到系统BluetoothManager:getSystemService(Context.BLUETOOTH_SERVICE)

   获取到蓝牙适配器:manager.getAdapter

   打开蓝牙:adapter.enable

     /**
     * 初始化蓝牙外设
     */
    public boolean initialize() {
        //判断手机是否配置蓝牙4.0
        if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
            Log.e(TAG, "initialize:不能初始化蓝牙,因为您的手机不适配蓝牙4.0,没有蓝牙模块 " );
            return false;
        }
        //获取到蓝牙管理者
        if (mBluetoothManager == null) {
            mBluetoothManager = (BluetoothManager) mContext.getSystemService(Context.BLUETOOTH_SERVICE);
            if (mBluetoothManager == null) {
                Log.e(TAG, "initialize:初始化蓝牙管理器失败 ");
                return false;
            }
        }
        //获取到蓝牙适配器
        if(mBluetoothAdapter == null) {
            mBluetoothAdapter = mBluetoothManager.getAdapter();
            if (mBluetoothAdapter == null) {
                Log.e(TAG, "initialize:初始化蓝牙适配器失败 " );
                return false;
            }
        }
        //打开手机蓝牙
        if(mBluetoothAdapter == null) {
            Log.e(TAG, "initialize:初始化蓝牙适配器失败 " );
            return false;
        } else {
            //判断是否有蓝牙模块
            boolean hasBle = mContext.getPackageManager().hasSystemFeature("android.hardware.bluetooth_le");
            if (!hasBle) {
                Log.e(TAG, "initialize:不能初始化蓝牙,因为您的手机不适配蓝牙4.0,没有蓝牙模块 ");
                return false;
            }

            //打开手机蓝牙
            mBluetoothAdapter.enable();

            return true;
        }
    }
3  扫描外设,断开扫描

   蓝牙扫描外设Android:Android5.0(SDK21)之前 startLeScan ,stopLeScan,回调函数LeScanCallback

Android5.0之后版本:先获取到BluetoothLeScanner: mBluetoothAdapter.getBluetoothLeScanner();

扫描:mBluetoothLeScanner.startScan(mScanCallback), 停止扫描:mBluetoothLeScanner.stopScan(mScanCallback)

扫描结果回调函数:ScanCallback

 @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
    private static class VersionA {
        //扫描外设的回调函数
        private static BluetoothAdapter.LeScanCallback mLeScanCallback =
                new BluetoothAdapter.LeScanCallback() {
                    @Override
                    public void onLeScan(BluetoothDevice bluetoothDevice, int i, byte[] bytes) {
                        Log.e(TAG, "onLeScan: 扫描一次" );
                        mBleCallBack.deviceScanned(bluetoothDevice, i, bytes);
                    }
                };

        public static void startLeScan() {
            if (mBluetoothAdapter == null) {
                Log.e(TAG, "startLeScan:开始扫描,适配器失败 ");
                return;
            }
            mBluetoothAdapter.startLeScan(mLeScanCallback);
        }

        public static void stopLeScan() {
            if (mBluetoothAdapter == null) {
                Log.e(TAG, "stopLeScan:停止扫描,适配器失败 ");
                return;
            }
            mBluetoothAdapter.stopLeScan(mLeScanCallback);
        }
    }
    /**
     * 5.0之后版本的外设扫描
     */
    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    private static class VersionB {
        private static ScanCallback mScanCallback = new ScanCallback() {
            @Override
            public void onScanResult(int callbackType, ScanResult result) {
                //if (isDevice(result.getScanRecord().getBytes())) {
                    // TODO: 16/6/24 扫描回调函数
                Log.e(TAG, "onScanResult: 扫描一次" );
               // Log.d(TAG, "onScanResult: 扫描回调函数,回调一次"+result.getDevice().getAddress());
                    mBleCallBack.deviceScanned(result.getDevice(), callbackType, result.getScanRecord().getBytes());
               // }
            }
        };
        public static void startScan() {
            if (mBluetoothLeScanner == null) {
                mBluetoothLeScanner = mBluetoothAdapter.getBluetoothLeScanner();
            }

            mBluetoothLeScanner.startScan(mScanCallback);
        }

        public static void stopScan() {
            if (mBluetoothLeScanner != null) {
                mBluetoothLeScanner.stopScan(mScanCallback);
            }
        }
    }
3  连接设备:

手动连接设备,通过address连接

通过address获取到设备device:mBluetoothAdapter.getRemoteDevice(address);

创建连接,连接gatt通道:device.connectGatt(mContext, false, mGattCallback);

GATT:定义两个BLE设备通过Servicecharacteristic的东西进行通信,GATT连接是独占的,一个BLE外设同时只能被一个中心设备连接,一旦外设被连接就不再广播,这样它对其他设备不可见,设备断开时,它又开始广播.

GATT中

一个低功耗蓝牙(ble)可以包括多个Profile

一个Profile中有多个Service(通过uuid就可以找到对应的Service

一个Service中有多个Characteristic(通过uuid就可以找到对应的Characteristic

一个Characteristic中包括一个value和多个Descriptor(通过uuid就可以找到对应的Descriptor

 /**
     * 手动连接设备,通过address连接
     */
    public boolean connect(final String address) {
        currentDevice = null;
        if (mBluetoothAdapter == null || address == null) {
            Log.e(TAG, "connect: 蓝牙适配器初始化失败或者蓝牙外设地址为空");
            return false;
        }
        final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
        if (device == null) {
            Log.e(TAG, "外设没有找到,连接失败");
            return false;
        }
        currentDevice = device;

        // 曾经连接上过,重新连接
//        if (mBluetoothDeviceAddress != null && address.equals(mBluetoothDeviceAddress)
//                && mBluetoothGatt != null) {
//            Log.e(TAG, "尝试连接一个曾经连接上的外设");
//            mBluetoothGatt.close();
//            if (mBluetoothGatt.connect()) {
//                //已经连接上了,但是基于蓝牙协议,并没有连接上,手动扫描service从而实现连接
//                mBluetoothGatt.discoverServices();
//                return true;
//            } else {
//                return false;
//            }
//        }
        if (mBluetoothGatt != null){
            mBluetoothGatt.close();
            mBluetoothGatt = null;
        }
        //连接设备

        mBluetoothGatt = device.connectGatt(mContext, false, mGattCallback);
        Log.e(TAG, "尝试创建一个新连接,连接外设Gatt新通道");
        mBluetoothDeviceAddress = address;
        if (mBluetoothGatt != null){
            Log.e(TAG, "Gatt通道建立成功");
        }else {
            Log.e(TAG, "Gatt通道建立失败");
        }
        return true;
    }

4 断开连接:gatt.disConnect

5发送数据到外设:

 this.writeCharacteristic.setValue(data);

  this.mBluetoothGatt.writeCharacteristic(this.writeCharacteristic);

6 一旦扫描到外设,就可以获取到service了,这样就可以对属性进行读写了

 private BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
     @Override
     public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
         if (newState == BluetoothProfile.STATE_CONNECTED) {
             // TODO: 16/6/24 设备连接
             mBleCallBack.deviceConnected(currentDevice.getAddress());
             //搜索连接到的蓝牙外设的服务
             try {
                 Thread.sleep(500);
             } catch (InterruptedException e) {
                 e.printStackTrace();
             }
             mBluetoothGatt.discoverServices();
             //监测到设备连接成功后,扫描service
         } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
             // TODO: 16/6/24 设备断开
             if (mBluetoothGatt != null){
                 mBluetoothGatt.close(); // 释放资源
                 mBluetoothGatt = null;
             }
             mBleCallBack.deviceDisconnected(currentDevice.getAddress());
         }
     }

     @Override
     public void onServicesDiscovered(BluetoothGatt gatt, int status) {
         Log.d(TAG, "onServicesDiscovered: BluetoothGatt Status:"+status);
         if (status == BluetoothGatt.GATT_SUCCESS){
             // TODO: 16/6/27 搜索服务中
             if(bleServiceList != null && bleServiceList.size() > 0) {
                 bleServiceList.clear();
             }
             if(mBluetoothGatt != null) {
                 bleServiceList = mBluetoothGatt.getServices();
             }
             Log.d(TAG, "onServicesDiscovered: gatt success");
             setCharacteristicNull();
             BluetoothGattService bluetoothGattService = mBluetoothGatt.getService(SERVICE_UUID);
             if (bluetoothGattService != null){
                 List<BluetoothGattCharacteristic> characteristicList = bluetoothGattService.getCharacteristics();
                 for (BluetoothGattCharacteristic characteristic : characteristicList) {
                     Log.d(TAG, "onServicesDiscovered: Characteristic:"+characteristic.getUuid()+"propertity:"+characteristic.getProperties());
                     if (characteristic.getUuid().equals(UUID.fromString(""))) {
                         if ((characteristic.getProperties() & 18) != 0) {
                             Log.d(TAG, "onServicesDiscovered: readCharacteristic:"+characteristic.getProperties());
                             readCharacteristic = characteristic;//获取到读写characteristic
                             setCharacterNotification(readCharacteristic,true);
                         } else if ((characteristic.getProperties() & 12) != 0) {
                             Log.d(TAG, "onServicesDiscovered: writeCharacteristic:"+characteristic.getProperties());
                             characteristic.setWriteType(1);
                             writeCharacteristic = characteristic;
                         }
                     }
                     }
                 }
             }
             BluetoothGattService bluetoothGattServicen = mBluetoothGatt.getService(SERVICE_UUIDN);
             if (bluetoothGattServicen != null) {
                 List<BluetoothGattCharacteristic> characteristicListn = bluetoothGattServicen.getCharacteristics();
                 for (BluetoothGattCharacteristic characteristic : characteristicListn) {

                     if (characteristic.getUuid().equals(UUID.fromString("d44bc439-abfd-45a2-b575-925416129601"))) {
                         readCharacteristic = characteristic;
                         setCharacterNotification(readCharacteristic,true);
                         Log.e(TAG, "读:" + characteristic.getProperties());
                     }
                     if (characteristic.getUuid().equals(UUID.fromString("d44bc439-abfd-45a2-b575-925416129600"))) {
                         writeCharacteristic = characteristic;
                         //characteristic.setWriteType(1);
                         Log.e(TAG, "写:" + characteristic.getProperties());
                     }
                 }
             }

         }
     }

     @Override
     public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
         if(status == 0) {
             // TODO: 16/6/27 读characteristic的值
         }

     }

     @Override
     public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
         byte[] var3 = characteristic.getValue();
         Log.d(TAG,"write  character response:"+CharUtils.bytesToHexString(var3));
     }
     @Override
     public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {

         byte[] var3 = characteristic.getValue();
         Log.d(TAG, "onCharacteristicChanged: data:"+CharUtils.bytesToHexString(var3));
         // TODO: 16/6/27 接收数据
         mBleCallBack.deviceReceiveData(currentDevice.getAddress(),var3);
     }


     @Override
     public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
         if(status == 0) {
             // TODO: 16/6/27 写入descriptor 之后回调
         }
     }
     @Override
     public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
         // TODO: 16/6/27 读取Rssi的值
         mBleCallBack.deviceRssiReport(currentDevice.getAddress(),rssi);
     }
 };
7 开启notify接收数据

如果设备主动给手机发信息,则可以通过notification的方式,如果notificaiton方式对于某个Characteristicenable的,那么当设备上的这个Characteristic改变时,手机上onCharacteristic回调就会被促发。

 private void setCharacterNotification(BluetoothGattCharacteristic characteristic, boolean enabled){
     Log.e(TAG,"set ChatacterNotification");
     boolean isOk = mBluetoothGatt.setCharacteristicNotification(characteristic,enabled);
     Log.e(TAG,"set ChatacterNotification:"+isOk);
     if(!isOk) {
         Log.e(TAG, "设置通知失败");
     }
//   BluetoothGattDescriptor descriptor = characteristic.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"));
//     if(descriptor != null) {
//         byte[] bytes = enabled ? BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE : BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE;
//         descriptor.setValue(bytes);
//         boolean isWrite =  mBluetoothGatt.writeDescriptor(descriptor);
//         if (isWrite){
//             Log.d(TAG, "writeDescriptor:ok ");
//         }else{
//             Log.d(TAG, "writeDescriptor:on ");
//         }
//   }
     List<BluetoothGattDescriptor> descriptorList = characteristic.getDescriptors();
     if(descriptorList != null && descriptorList.size() > 0) {
         Log.e(TAG, "descriptor != null");
         for(BluetoothGattDescriptor descriptor : descriptorList) {
             descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
             boolean isWrite = mBluetoothGatt.writeDescriptor(descriptor);
             if (isWrite){
             Log.d(TAG, "writeDescriptor:ok ");
             }else {
             Log.d(TAG, "writeDescriptor:on ");
             }
         }
     }
 }

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值