android 蓝牙4.0(BLE) 开发

简介

蓝牙发展至今经历了9个版本的更新。1.1、1.2、2.0、2.1、3.0、4.0、4.1、4.2、5.0。那么在1.x~3.0之间的我们称之为传统蓝牙,4.x开始的蓝牙我们称之为低功耗蓝牙也就是蓝牙ble,当然4.x版本的蓝牙也是向下兼容的。android手机必须系统版本4.3及以上才支持BLE API。蓝牙4.0较传统蓝牙也有很大大差别:

随着蓝牙技术由手机、游戏、耳机、便携电脑和汽车等传统应用领域向物联网、医疗等新领域的扩展,对低功耗的要求会越来越高。蓝牙4.0这种低功耗版本很快会占主导。

使用

今天我就单独写个demo来介绍下android里面的BLE怎么来进行数据传输的。(如果不了解传统蓝牙BT的开发 操作请看https://blog.csdn.net/weixin_35959554/article/details/85277269

1.开启蓝牙权限


<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
//6.0以上需要加上
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
//required设置为true 表示只能在支持BLE的安卓设备上安装运行该APP
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>

2.判断手机是否支持ble

如果上面设置权限的时候required="false",那么就可能有设备不支持ble,所以得在进入activity时判断手机是否支持,不支持则退出程序。

if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
        Toast.makeText(this, "当前设备不支持蓝牙BLE,即将退出程序", Toast.LENGTH_SHORT).show();
        finish();
}

3.获取蓝牙管理以及蓝牙适配器

如果不确定蓝牙是否打开,那么在初始化时请求打开蓝牙。

//context activity传递的上下文  
Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
        context.startActivity(intent);

蓝牙打开后,开始获取适配器


BluetoothManager bluetoothManager = (BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE);
  BluetoothAdapter  bluetoothAdapter = bluetoothManager.getAdapter();

4.搜索蓝牙设备

    BluetoothAdapter.LeScanCallback leScanCallback;
        //搜索到设备后 会回调LeScanCallback接口
        leScanCallback = new BluetoothAdapter.LeScanCallback() {
            @Override
            public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {
                //把获取到的设备保存到设备集合,用于后面显示和连接
               if (!devices.contains(device)){
                    devices.add(device);
                }
            }
        };
//开始搜索设备
        bluetoothAdapter.startLeScan(leScanCallback);
   //停止搜索设备
//bluetoothAdapter.stopLeScan(leScanCallback);
    }

5.连接蓝牙设备

//_device为已选择了的设备   创建BluetoothDevice实例
      BluetoothDevice          remoteDevice = bluetoothAdapter.getRemoteDevice(_device.getAddress());
//开始打开device  bluetoothGattCallback为连接后的回调  
       BluetoothGatt         bluetoothGatt = remoteDevice.connectGatt(context, false,  bluetoothGattCallback); 

接口回调里面包含了很多操作,数据接收,连接反馈,获取服务等。

6.获取服务

    /**
     * 服务的UUID
     */

    public static final String SERVICE_UUID ="6e400001-b5a3-f393-e0a9-e50e24dcca9e";
    /**
     * 订阅通知的UUID
     */

    public static final String NOTIFY_UUID = "6e400003-b5a3-f393-e0a9-e50e24dcca9e";    

@Override
                    public void onServicesDiscovered(BluetoothGatt gatt, int status) {
                        super.onServicesDiscovered(gatt, status);
                        gattService = gatt.getService(UUID.fromString(SERVICE_UUID));// 获取到服务的通道
                        bluetoothGatt = gatt;
                        //获取到Notify的Characteristic通道 这个根据协议来定  如果设备厂家给的协议不是Notify的话  就不用做以下操作了
                        BluetoothGattCharacteristic notifyCharacteristic = gattService.getCharacteristic(UUID.fromString(NOTIFY_UUID));
                        enableNotification(gatt, true, notifyCharacteristic);//注册Notify通知
                    }
    /**
     * 是否开启蓝牙的通知
     *
     * @param enable
     * @param characteristic
     * @return
     */
    public static boolean enableNotification(BluetoothGatt bluetoothGatt, boolean enable, BluetoothGattCharacteristic characteristic) {
        if (bluetoothGatt == null || characteristic == null) {
            return false;
        }
        if (!bluetoothGatt.setCharacteristicNotification(characteristic, enable)) {
            return false;
        }
        //获取到Notify当中的Descriptor通道  然后再进行注册
        BluetoothGattDescriptor clientConfig = characteristic.getDescriptor(UUID.fromString(NOTIFY_DESCRIPTOR));
        if (clientConfig == null) {
            return false;
        }
        if (enable) {
            clientConfig.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
        } else {
            clientConfig.setValue(BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE);
        }
        return bluetoothGatt.writeDescriptor(clientConfig);
    }

这些UUID可能会不相同,一般硬件哥哥会提供,也可以自己使用工具软件查看UUID。

7.连接状态反馈

 @Override
                    public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
                        super.onConnectionStateChange(gatt, status, newState);
                        switch (newState) {//对蓝牙反馈的状态进行判断
                            case BluetoothProfile.STATE_CONNECTED://已链接
                                sentConMsg("蓝牙连接成功");
                                gatt.discoverServices();
                                break;
                            case BluetoothProfile.STATE_DISCONNECTED://已断开
                                sentConMsg("蓝牙已断开");
                                break;
                        }
                    }

连接状态通过sentConMsg回调接口反馈给UI显示。

8.接收数据

连接成功后,如果硬件有发送数据,不出意外的话,onCharacteristicChanged中将持续接收硬件发送的数据。

     @Override
                    public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
                        super.onCharacteristicChanged(gatt, characteristic);
                         //接收到的byte数组                        
                      byte[] value = characteristic.getValue();

                        //实时传递数据给数据接口,供程序使用
                    }

9.发送数据

   /**
     * 写出数据的UUID
     */
    public static final String WRITE_UUID = "00006a02-0000-1000-8000-00805f9b34fb";   
/**
     * 向蓝牙写入数据
     * @param data
     */
    public boolean writeBuletoothData(String data) {
        BluetoothGattCharacteristic writeCharact = gattService.getCharacteristic(UUID.fromString(WRITE_UUID));
        bluetoothGatt.setCharacteristicNotification(writeCharact, true); // 设置监听
        // 当数据传递到蓝牙之后
        // 会回调BluetoothGattCallback里面的write方法
        writeCharact.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT);
        // 将需要传递的数据 打碎成16进制
        writeCharact.setValue(getHexBytes(data));
        return bluetoothGatt.writeCharacteristic(writeCharact);
    }

10.关闭连接

  bluetoothGatt.close();

完整工具类:



public class Bluetooth4Helper {
    //连接的协议
    private String MY_UUID = "00001101-0000-1000-8000-00805F9B34FB";
    /**
     * 服务的UUID
     */
    public static final String SERVICE_UUID = "6e400001-b5a3-f393-e0a9-e50e24dcca9e";
    /**
     * 订阅通知的UUID
     */
    public static final String NOTIFY_UUID = "6e400003-b5a3-f393-e0a9-e50e24dcca9e";
    /**
     * 写出数据的UUID
     */
    public static final String WRITE_UUID = "00006a02-0000-1000-8000-00805f9b34fb";
    /**
     * NOTIFY里面的Descriptor UUID
     */
    public static final String NOTIFY_DESCRIPTOR = "00002902-0000-1000-8000-00805f9b34fb";
    private static Bluetooth4Helper bluetoothHelper;
    private static Context context;
    private static BluetoothDevice _device = null;
    private Timer _Timer = null;
    private int _Datatime = 0;
    private Set<BluetoothDevice> devices = new HashSet<>();
    private BluetoothAdapter bluetoothAdapter;
    private BluetoothManager bluetoothManager;//蓝牙管理者
    private BluetoothGattService gattService;//通道服务
    private BluetoothGatt bluetoothGatt;
    private BluetoothDevice remoteDevice;

    public static Bluetooth4Helper getInstance(Context con) {
        if (null == bluetoothHelper) {
            bluetoothHelper = new Bluetooth4Helper();
            context = con;
        }
        return bluetoothHelper;
    }

    public void init() {
        //系统会显示对话框,允许后直接打开
        Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
        context.startActivity(intent);
        //其次获取到蓝牙的管理类:
        bluetoothManager = (BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE);
        bluetoothAdapter = bluetoothManager.getAdapter();
    }

    BluetoothAdapter.LeScanCallback leScanCallback;

    public void startLeScan() {
        //stopLeScan();防止重复搜索
        devices.clear();
        //搜索到设备后 会回调LeScanCallback接口
        leScanCallback = new BluetoothAdapter.LeScanCallback() {
            @Override
            public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {
                if (!devices.contains(device)) {
                    devices.add(device);
                }
            }
        };
        //开始搜索
        bluetoothAdapter.startLeScan(leScanCallback);
        _Timer = new Timer();
        _Timer.schedule(new DataTimer(), 0, 1000);
    }

    public void stopLeScan() {
        //停止扫描
        bluetoothAdapter.stopLeScan(leScanCallback);
        if (_Timer != null) {
            _Timer.cancel();
            _Timer = null;
            _Datatime = 0;
        }
    }

    public void open(String address) {
        //close();
        if (bluetoothGatt != null && bluetoothGatt.connect()) {
            bluetoothGatt.close();
        }
        if (devices != null && devices.size() > 0) {
            for (BluetoothDevice dev : devices) {
                if (address != null && address.equals(dev.getAddress())) {
                    _device = dev;
                    break;
                } else if (address == null) {
                    _device = dev;
                    break;
                }

            }
        }
        if (_device != null) {
            //创建一个 BluetoothDevice 类实例
            remoteDevice = bluetoothAdapter.getRemoteDevice(_device.getAddress());
            //开始连接本设备
            bluetoothGatt = remoteDevice.connectGatt(context, false, new BluetoothGattCallback() {
                @Override
                public void onPhyUpdate(BluetoothGatt gatt, int txPhy, int rxPhy, int status) {
                    super.onPhyUpdate(gatt, txPhy, rxPhy, status);
                }

                @Override
                public void onPhyRead(BluetoothGatt gatt, int txPhy, int rxPhy, int status) {
                    super.onPhyRead(gatt, txPhy, rxPhy, status);
                }

                @Override
                public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
                    super.onConnectionStateChange(gatt, status, newState);
                    switch (newState) {//对蓝牙反馈的状态进行判断
                        case BluetoothProfile.STATE_CONNECTED://已连接
                            sentConMsg("蓝牙连接成功");
                            gatt.discoverServices();
                            break;
                        case BluetoothProfile.STATE_DISCONNECTED://已断开
                            sentConMsg("蓝牙已断开");
                            break;
                    }
                }

                @Override
                public void onServicesDiscovered(BluetoothGatt gatt, int status) {
                    super.onServicesDiscovered(gatt, status);
                    gattService = gatt.getService(UUID.fromString(SERVICE_UUID));// 获取到服务的通道
                    bluetoothGatt = gatt;
                    //获取到Notify的Characteristic通道 这个根据协议来定  如果设备厂家给的协议不是Notify的话  就不用做以下操作了
                    BluetoothGattCharacteristic notifyCharacteristic = gattService.getCharacteristic(UUID.fromString(NOTIFY_UUID));
                    enableNotification(gatt, true, notifyCharacteristic);//注册Notify通知
                }

                @Override
                public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
                    super.onCharacteristicRead(gatt, characteristic, status);
                    String data = bytesToHexString(characteristic.getValue()); // 将字节转化为String字符串
                }

                @Override
                public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
                    super.onCharacteristicChanged(gatt, characteristic);
                    byte[] value = characteristic.getValue();
                    byte[] temp = new byte[value.length];
                    System.arraycopy(value, 0, temp, 0, value.length);
                    sentData(temp);
                }

            });
        }
    }


    /**
     * 向蓝牙写入数据
     *
     * @param data
     */
    public boolean writeBuletoothData(String data) {
        BluetoothGattCharacteristic writeCharact = gattService.getCharacteristic(UUID.fromString(WRITE_UUID));
        bluetoothGatt.setCharacteristicNotification(writeCharact, true); // 设置监听
        // 当数据传递到蓝牙之后
        // 会回调BluetoothGattCallback里面的write方法
        writeCharact.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT);
        // 将需要传递的数据 打碎成16进制
        writeCharact.setValue(getHexBytes(data));
        return bluetoothGatt.writeCharacteristic(writeCharact);
    }


    //得到当前连接蓝牙名称
    public String getDeviceName() {
        if (_device != null) {
            return _device.getName();
        }
        return "";
    }

    //得到已配对蓝牙列表
    public Set<BluetoothDevice> getDevices() {
        if (devices != null) {
            return devices;
        }
        return new HashSet<>();
    }

    //关闭蓝牙连接
    public void close() {
        if (_Timer != null) {
            _Timer.cancel();
            _Timer = null;
            _Datatime = 0;
        }
        if (bluetoothGatt != null)
            bluetoothGatt.close();
        if (_device != null) {
            _device = null;
        }

    }

    //在3秒内持续没有数据 那么判定蓝牙断开
    private class DataTimer extends TimerTask {
        @Override
        public void run() {
            _Datatime++;
            if (_Datatime > 5) {
                stopLeScan();
            }
        }
    }

    /**
     * 数据传输事件
     */
    private List<GetTerminalDataListener> lsDatas;

    public void addGetData(GetTerminalDataListener dataListener) {
        if (lsDatas == null) {
            lsDatas = new ArrayList<>();
        }
        lsDatas.add(dataListener);
    }

    public void removeGetData(GetTerminalDataListener dataListener) {
        if (lsDatas == null) {
            lsDatas = new ArrayList<>();
        }
        lsDatas.remove(dataListener);
    }

    private Handler DataHandler = new Handler() {
        public void handleMessage(Message msg) {
            if (lsDatas == null)
                return;
            for (GetTerminalDataListener item : lsDatas) {
                item.getTerminalData((TerminalLocationInfo) msg.obj);
            }
        }
    };

    //发送数据
    private void sentData(byte[] data) {
        Message message = Message.obtain();
        message.obj = data;
        DataHandler.sendMessage(message);
    }

    //发送状态
    private void sentConMsg(String msg) {
        Message message = Message.obtain();
        message.obj = msg;
        ConnetHandler.sendMessage(message);
    }

    /**
     * 连接状态事件
     */
    private List<ConnectStateListener> lstConnect;

    public void addConnectState(ConnectStateListener connectStateListener) {
        if (lstConnect == null) {
            lstConnect = new ArrayList<>();
        }
        lstConnect.add(connectStateListener);
    }

    public void removeConnectState(ConnectStateListener connectStateListener) {
        if (lstConnect == null) {
            lstConnect = new ArrayList<>();
        }
        lstConnect.remove(connectStateListener);
    }


    private Handler ConnetHandler = new Handler() {
        public void handleMessage(Message msg) {
            if (lstConnect == null)
                return;
            for (ConnectStateListener item : lstConnect) {
                item.ConnectState((String) msg.obj);
            }
        }
    };

    /**
     * 是否开启蓝牙的通知
     *
     * @param enable
     * @param characteristic
     * @return
     */
    public static boolean enableNotification(BluetoothGatt bluetoothGatt, boolean enable, BluetoothGattCharacteristic characteristic) {
        if (bluetoothGatt == null || characteristic == null) {
            return false;
        }
        if (!bluetoothGatt.setCharacteristicNotification(characteristic, enable)) {
            return false;
        }
        //获取到Notify当中的Descriptor通道  然后再进行注册
        BluetoothGattDescriptor clientConfig = characteristic.getDescriptor(UUID.fromString(NOTIFY_DESCRIPTOR));
        if (clientConfig == null) {
            return false;
        }
        if (enable) {
            clientConfig.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
        } else {
            clientConfig.setValue(BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE);
        }
        return bluetoothGatt.writeDescriptor(clientConfig);
    }

    /**
     * 将字节 转换为字符串
     *
     * @param src 需要转换的字节数组
     * @return 返回转换完之后的数据
     */
    public static String bytesToHexString(byte[] src) {
        StringBuilder stringBuilder = new StringBuilder("");
        if (src == null || src.length <= 0) {
            return null;
        }
        for (int i = 0; i < src.length; i++) {
            int v = src[i] & 0xFF;
            String hv = Integer.toHexString(v);
            if (hv.length() < 2) {
                stringBuilder.append(0);
            }
            stringBuilder.append(hv);
        }
        return stringBuilder.toString();
    }

    /**
     * 将字符串转化为16进制的字节
     *
     * @param message 需要被转换的字符
     * @return
     */
    public static byte[] getHexBytes(String message) {
        int len = message.length() / 2;
        char[] chars = message.toCharArray();

        String[] hexStr = new String[len];

        byte[] bytes = new byte[len];

        for (int i = 0, j = 0; j < len; i += 2, j++) {
            hexStr[j] = "" + chars[i] + chars[i + 1];
            bytes[j] = (byte) Integer.parseInt(hexStr[j], 16);
        }
        return bytes;
    }


}

使用实例:

//初始化工具类
Bluetooth4Helper.getInstance(this).init();

//点击按钮开始搜索蓝牙设备  搜索结果通过RecyclerView等展示到界面上
 Bluetooth4Helper.getInstance(this).startLeScan();
 Set<BluetoothDevice> devices = Bluetooth4Helper.getInstance(this).getDevices();

//选中显示的蓝牙设备 并把设备地址address名传过去
 Bluetooth4Helper.getInstance(this).open(address);


//添加得到 蓝牙数据 和 连接状态信息 的事件
Bluetooth4Helper.getInstance(this).addGetData(this);
Bluetooth4Helper.getInstance(this).addConnectState(this);
 

//退出程序释放全部
 Bluetooth4Helper.getInstance(this).removeConnectState(this);
 Bluetooth4Helper.getInstance(this).removeGetData(this);
 Bluetooth4Helper.getInstance(this).close();

总结

蓝牙4.0到这就介绍完了,主要是介绍下怎么使用,如果有讲错的地方请哥哥些多多包涵,总的来说不管是BT蓝牙还是BLE蓝牙还是其他USB等,连接方式都是那几个套路。当你消化掉其中一种时,加把劲理解其他的东西应该都不是很困难的事。突然发现明天是1024,还是要自己给自己过过节(奖励bug一坨……)。

发布了40 篇原创文章 · 获赞 70 · 访问量 9万+

猜你喜欢

转载自blog.csdn.net/weixin_35959554/article/details/102705502