zoukankan      html  css  js  c++  java
  • 蓝牙Ble开发(支持API18<Android 4.3 Jelly>及以上)

      Android4.3(api18)开始支持蓝牙Ble(Bluetooth Low Energy)开发,到Android5.0(api21)开始修改了部分方法库,详细请看“蓝牙Ble开发(支持API21<Android 5.0 Jelly>及以上)”。

      https://note.youdao.com/ynoteshare1/index.html?id=4e9e1de1c3604df0f5b017eea73a19e1&type=note#/

    一、注意

    1、所需权限

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

     2、蓝牙Ble协议默认最大发送20字节(不包含头尾,包含头尾总共22字节);

    二、遗留问题

    1、蓝牙发送数据可以正常回调方法onCharacteristicWrite,蓝牙读数据无法正常回调方法onCharacteristicRead,但是可以正常回调方法onCharacteristicChanged,可以通过该方法获取蓝牙已读数据。原因未知。

    三、代码详情

    1、先判断蓝牙是否开启

         // 获取蓝牙管理器
            BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
            // 获取蓝牙适配器
            BluetoothAdapter bAdapter = bluetoothManager.getAdapter();
            // 判断是否可用
            if(bAdapter.isEnabled())
            {
                // 蓝牙已打开
    } else { // 弹出蓝牙打开对话框(系统) Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE); startActivityForResult(intent, 1); }

     蓝牙选择结果:

    @Override
        protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
            // 蓝牙选择结果判断
            if (requestCode == 1)
            {
                switch (resultCode)
                {
                    case 120:
                        // 蓝牙已打开
                        break;
                    case 0:
                        // 蓝牙未打开
                        break;
                }
            }
        }

    2、开始扫描(回调扫描结果方法)-- bAdapter.startLeScan(leScanCallback)返回device

     
    /**
         * 开始扫描ble
         */
        boolean canReconntect = false;
    
        public void startLeScan() {
            // 开始扫描后允许重连,防止意外断开连接
            canReconntect = true;
            if (bAdapter != null) {
                boolean result = bAdapter.startLeScan(leScanCallback);
                if (result) {
                    // LE开始扫描
                }
                Timer timer = new Timer();
                timer.schedule(new TimerTask() {
                    @Override
                    public void run() {
                        // 扫描10s后停止扫描
                        stopLenScan();
                    }
                }, 10000);
            }
        }
    
        /**
         * 停止扫描
         */
        public void stopLenScan() {
            if (bAdapter != null) {
                bAdapter.stopLeScan(leScanCallback);
                LogUtils.instance().logI("LE扫描停止...");
            }
        }
    
        // 扫描回调方法
        private BluetoothAdapter.LeScanCallback leScanCallback = new BluetoothAdapter.LeScanCallback()
        {
            @Override
            public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord)
            {
                // 扫描结果 device.getName()
                if (device.getName() != null && device.getName().equals("指定蓝牙名称"))
                {
                    LogUtils.instance().logI("扫描到指定结果:" + device.getName());
                    // 停止扫描
                    stopLenScan();
    
                    // 根据扫描到的蓝牙地址进行连接
                    requestConnect(device);
                    LogUtils.instance().logI("开始获取gatt...");
                }
            }
        };

    3、建立链接 -- device.connectGatt(context, true, bluetoothGattCallback)返回gatt

    /**
         *发起连接(供外部调用)
         * @param device:目标设备
         */
        public void requestConnect(BluetoothDevice device)
        {
            BluetoothGatt gatt = device.connectGatt(context, true, mGattCallback);
    
            // 为了防止重复的gatt,连接成功先检查是否有重复的,有则断开
            BluetoothGatt last = listBluetoothGatts.remove(device.getAddress());
            if(last != null)
            {
                last.disconnect();
                last.close();
            }
            // 添加当前gatt
            listBluetoothGatts.put(device.getAddress(), gatt);
        }
    
    
        /**
         * 断开连接
         * @param address
         */
        public synchronized void disconnect(String address)
        {
            if (null != listBluetoothGatts && listBluetoothGatts.containsKey(address))
            {
                BluetoothGatt gatt = listBluetoothGatts.remove(address);
                if (gatt != null)
                {
                    gatt.disconnect();
                    gatt.close();
                    LogUtils.instance().logE("LE GATT释放成功," + gatt.getDevice().getName());
                }
            }
        }
    
        /**
         * 释放所有连接
         */
        public void disconnectAll()
        {
            // 手动停止后不需要重连
            canReconntect = false;
            if(null != listBluetoothGatts)
            {
                for(String address : listBluetoothGatts.keySet())
                {
                    disconnect(address);
                }
            }
            LogUtils.instance().logI("已释放所有连接");
        }

    4、发现服务 -- gatt.discoverServices()返回gatt

    5、获取服务,并查找出与通信有关的服务 -- gatt.getServices()返回服务列表listBluetoothGattService

    6、获取特征值(读、写) -- bluetoothGattService.getCharacteristics()返回bluetoothGattCharacteristic

    /**
         * gatt连接状态回调
         */
        private BluetoothGattCallback mGattCallback = new BluetoothGattCallback()
        {
    
            /**
             * 连接状态改变回调
             * @param gatt Gatt服务对象
             * @param status 是否成功执行连接操作
             * @param newState 连接状态(已经连接、已经断开)
             */
            @Override
            public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState)
            {
                super.onConnectionStateChange(gatt, status, newState);
    
                String address = gatt.getDevice().getAddress();
                // 成功执行连接操作
                if(status == BluetoothGatt.GATT_SUCCESS)
                {
                    // 设备已连接
                    if(newState == BluetoothProfile.STATE_CONNECTED)
                    {
                        // 重置重连次数
                        reConnectCount = 0;
                        LogUtils.instance().logI("LE设备已连接");
    
                        // 发现服务进行通信(成功会回调方法onServicesDiscovered())
                        if(gatt != null)
                        {
                            // 发现服务成功后回调onServicesDiscovered()方法
                            if(gatt.discoverServices())
                            {
                                LogUtils.instance().logI( "LE发现服务正常");
                            }
                            else
                            {
                                LogUtils.instance().logE( "LE发现服务失败");
                            }
                        }
                    }
                    // 设备已断开
                    else if(newState == BluetoothProfile.STATE_DISCONNECTED)
                    {
                        // 先断开原有连接
                        disconnect(address);
                        LogUtils.instance().logE( "LE设备已断开");
    
                        // 重新连接
                        reConnectCount ++;
                        if(reConnectCount <= Constant.RE_CONNECT_COUNT && canReconntect)
                        {
                            requestConnect(gatt.getDevice());
                            LogUtils.instance().logI( "LE设备开始重新连接");
                        }
                        else
                        {
                            LogUtils.instance().logE( "LE设备重新连接次数已到上线;" + reConnectCount + "或已手动停止");
                        }
                    }
                }
                else
                {
                    LogUtils.instance().logE( "LE连接Gatt失败" + status + "//" + newState);
                    // 如果断开则重新连接
                    if(newState != BluetoothProfile.STATE_CONNECTED && canReconntect)
                    {
                        // 先断开原有连接
                        disconnect(address);
    
                        reConnectCount ++;
                        if(reConnectCount <= Constant.RE_CONNECT_COUNT && canReconntect)
                        {
                            // 重新连接
                            requestConnect(gatt.getDevice());
                            LogUtils.instance().logI( "LE设备开始重新连接");
                        }
                        else
                        {
                            LogUtils.instance().logE( "LE设备重新连接次数已到上线;" + reConnectCount + "或已手动停止");
                        }
                    }
                }
            }
    
            /**
             * 发现服务回调
             * @param gatt
             * @param status
             */
            @Override
            public void onServicesDiscovered(BluetoothGatt gatt, int status)
            {
                super.onServicesDiscovered(gatt, status);
                LogUtils.instance().logI( "LE发现服务回调");
    
                // api21以上方法,以下不支持
    //            boolean r = gatt.requestMtu(100);
    //            LogUtils.instance().logE(r + "突破");
    
                if(status == BluetoothGatt.GATT_SUCCESS && gatt != null)
                {
                    // 获取服务列表(包含通信的服务)
                    listGattServices = gatt.getServices();
                    for(BluetoothGattService s : listGattServices)
                    {
                        if(s.getType() == BluetoothGattService.SERVICE_TYPE_PRIMARY && s.getUuid().toString().equals(Constant.UUID_SERVER.toLowerCase()))
                        {
                            // 找到指定通信服务(使用UUID匹配)
                            LogUtils.instance().logI( "LE发现蓝牙为主模式,UUID Service确认OK" + listGattServices.size());
    
                            // 给Servic、Gatt赋值,供主线程调用
                            bluetoothGattMain = gatt;
    
                            List<BluetoothGattCharacteristic> listCharacteristic = s.getCharacteristics();
                            for(BluetoothGattCharacteristic c : listCharacteristic)
                            {
                                // 写入数据(蓝牙工作模式)
                                if(c.getUuid().toString().equals(Constant.UUID_RECEIVE.toLowerCase()))
                                {
                                    // 给Servic、Gatt赋值,供主线程调用
                                    bluetoothGattCharacteristicWriteMain = c;
                                }
                                else if(c.getUuid().toString().equals(Constant.UUID_TRANS.toLowerCase()))
                                {
                                    // 给Servic、Gatt赋值,供主线程调用
                                    bluetoothGattCharacteristicReadMain = c;
                                }
    
                                // 扫描到蓝牙设备
                                reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit("listenerBluetoothState", "蓝牙已连接");
                            }
    
                        }
                        else if(s.getType() == BluetoothGattService.SERVICE_TYPE_SECONDARY)
                        {
                            LogUtils.instance().logI( "LE发现蓝牙为从模式");
                        }
                    }
                }
            }
    
            /**
             * 数据接收回调(未回调,待确认...)
             * @param gatt
             * @param characteristic
             * @param status
             */
            @Override
            public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
                super.onCharacteristicRead(gatt, characteristic, status);
                LogUtils.instance().logI( "onCharacteristicRead");
                if(status == BluetoothGatt.GATT_SUCCESS)
                {
                    LogUtils.instance().logI( "LE蓝牙数据读入成功回调:");
                }
                else
                {
                    LogUtils.instance().logE( "LE蓝牙数据读入失败回调:");
                }
            }
    
            /**
             * 数据发送回调(OK)
             * @param gatt
             * @param characteristic
             * @param status
             */
            @Override
            public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status)
            {
                super.onCharacteristicWrite(gatt, characteristic, status);
                LogUtils.instance().logI( "LE蓝牙模式发送成功回调:" +  characteristic.getValue().toString());
            }
    
    
            @Override
            public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic)
            {
                super.onCharacteristicChanged(gatt, characteristic);
    //            LogUtils.instance().logI( "LE  onCharacteristicChanged:" + characteristic.getValue().toString());
                getDataFromCharacteristic(characteristic);
            }
    
            /**
             *
             * @param gatt
             * @param descriptor
             * @param status
             */
            @Override
            public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
                super.onDescriptorRead(gatt, descriptor, status);
            }
    
            @Override
            public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
                super.onDescriptorWrite(gatt, descriptor, status);
            }
        };

    7、使用对应的进行读写操作

    蓝牙读写操作尽量放在主线程中,所以这里需要发送广播,在广播接收器中进行蓝牙读写操作,

        /**
         * 发送通知
         */
        public void startNotification()
        {
            Intent iWrite = new Intent();
            iWrite.setAction(Constant.BLE_WRITE);
            context.sendBroadcast(iWrite);
    
            // 发送广播开始从蓝牙读数据
            Intent iRead = new Intent();
            iRead.setAction(Constant.BLE_READ);
            context.sendBroadcast(iRead);
        }

    广播接收器接收消息,进行蓝牙读写操作:

    public class BluetoothBroadcast extends BroadcastReceiver
    {
        @Override
        public void onReceive(Context context, Intent intent)
        {
    
            if(intent.getAction().equals(Constant.BLE_WRITE))
            {
                LogUtils.instance().logE("广播接收器收到消息了");
    //            LogUtils.instance().logE("广播接收器:" + BluetoothBleUtils.instance().bluetoothGatt.getDevice().getName() + "///" + Thread.currentThread().getName());
    
                BluetoothGattCharacteristic characteristicWrite = BluetoothBleLowUtils.instance().bluetoothGattCharacteristicWriteMain;
                characteristicWrite.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE);
                characteristicWrite.setValue(Constant.BLUETOOTH_MODE);
    
                BluetoothGatt gatt = BluetoothBleLowUtils.instance().bluetoothGattMain;
                gatt.setCharacteristicNotification(characteristicWrite, true);
    
                boolean result = gatt.writeCharacteristic(characteristicWrite);
    
                LogUtils.instance().logE("广播接收器:开始写了" + result);
            }
    
            if(intent.getAction().equals(Constant.BLE_READ))
            {
                LogUtils.instance().logE("广播接收器:开始读了");
                BluetoothGattCharacteristic characteristicRead = BluetoothBleLowUtils.instance().bluetoothGattCharacteristicReadMain;
    
                BluetoothGatt gatt = BluetoothBleLowUtils.instance().bluetoothGattMain;
                gatt.setCharacteristicNotification(characteristicRead, true);
    
                for(BluetoothGattDescriptor descriptor : characteristicRead.getDescriptors())
                {
                    descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
                    boolean b = gatt.writeDescriptor(descriptor);
                }
    //            boolean result = gatt.readCharacteristic(characteristicRead);
                LogUtils.instance().logE("广播接收器:开始读了");
            }
        }
    }
  • 相关阅读:
    iOS NSNotificationCenter 最基本使用
    iOS SDK 从配置文件里读SDK。转化成class 可同时加载多个SDK
    NSString json 车NSDictionary
    在C#中使用.NET SDK创建控制
    推荐几款制作网页滚动动画的 JavaScript 库
    CSS选择器、优先级与匹配原理
    为ASP.NET控件加入快捷菜单
    如何使用ASP.NET开发基于推技术的聊天室?
    ASP.NET Web API queryString访问的一点总结
    ASP.net 控件实现数据级联
  • 原文地址:https://www.cnblogs.com/bluejump/p/10143076.html
Copyright © 2011-2022 走看看