蓝牙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("广播接收器:开始读了");
        }
    }
}

 

转载于:https://www.cnblogs.com/bluejump/p/10143076.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
可调整大小的数组的实现List接口。 实现所有可选列表操作,并允许所有元素,包括null 。 除了实现List 接口之外,该类还提供了一些方法来操纵内部使用的存储列表的数组的大小。 (这个类是大致相当于Vector,不同之处在于它是不同步的)。 该size,isEmpty,get,set,iterator和listIterator操作在固定时间内运行。 add操作以摊余常数运行 ,即添加n个元素需要O(n)个时间。 所有其他操作都以线性时间运行(粗略地说)。 与LinkedList实施相比,常数因子较低。 每个ArrayList实例都有一个容量 。 容量是用于存储列表中的元素的数组的大小。 它总是至少与列表大小一样大。 当元素添加到ArrayList时,其容量会自动增长。 没有规定增长政策的细节,除了添加元素具有不变的摊销时间成本。 应用程序可以添加大量使用ensureCapacity操作元件的前增大ArrayList实例的容量。 这可能会减少增量重新分配的数量。 请注意,此实现不同步。 如果多个线程同时访问884457282749实例,并且至少有一个线程在结构上修改列表,则必须在外部进行同步。 (结构修改是添加或删除一个或多个元素的任何操作,或明确调整后台数组的大小;仅设置元素的值不是结构修改。)这通常是通过在一些自然地封装了列表。 如果没有这样的对象存在,列表应该使用Collections.synchronizedList方法“包装”。 这最好在创建时完成,以防止意外的不同步访问列表: List list = Collections.synchronizedList(new ArrayList(...)); The iterators returned by this class's个 iterator和listIterator方法是快速失败的 :如果列表在任何时间从结构上修改创建迭代器之后,以任何方式除非通过迭代器自身remove种或add方法,迭代器都将抛出一个ConcurrentModificationException 。 因此,面对并发修改,迭代器将快速而干净地失败,而不是在未来未确定的时间冒着任意的非确定性行为。 请注意,迭代器的故障快速行为无法保证,因为一般来说,在不同步并发修改的情况下,无法做出任何硬性保证。 失败快速迭代器尽力投入ConcurrentModificationException 。 因此,编写依赖于此异常的程序的正确性将是错误的:迭代器的故障快速行为应仅用于检测错误。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值