Android 蓝牙4.0使用心得

 最近在公司做了个蓝牙项目,之前刚出道的时候做过个智能手环的项目,那时蓝牙模块是开发组长写的,我看的也是一知半解,很多不了解的东西现在看起来有些新心得,写下来记录下.

蓝牙4.0(BLE) 在安卓4.3(API 18)以上支持,相比传统的蓝牙,BLE更显著的特点是低功耗。是Android 蓝牙史上一大转折点


经过这次开发我把BLE使用只要分为4个步骤:

  1. 搜索蓝牙设备
  2. 连接设备/断开连接设备
  3. 设置服务的读/写/通知通道
  4. 操作写,读/接受通知

搜索蓝牙设备

  1. 首先要获取到手机蓝牙权限。

    在应用程序manifest文件中添加如下代码,声明蓝牙权限。

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

但在android6.0以上做开发的同学需要注意在6.0以上还需加上位置的权限,否则会搜索不到任何设备。还需动态去获取权限(动态获取不详说)

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

2.判断手机是否支持蓝牙和蓝牙是否已打开,否跳转打开蓝牙设备

 BluetoothAdapter=mBluetoothAdapte=BluetoothAdapter.getDefa            ultAdapter();
                if (mBluetoothAdapter == null) {
                      "此设备不支持蓝牙传输功能!"
                     }
                else {  
                       "此设备支持蓝牙传输功能!", 
                    if (!mBluetoothAdapter.isEnabled())//判               断是否打开,否就跳转打开 
                    {
                          Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);  
                          enableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);  
                            startActivityForResult(enableIntent, REQUEST_ENABLE_BT);

1).BluetoothAdapter 就代表本地蓝牙设备,用getDefaultAdapter()获取本地蓝牙设备,其返回值如果为空表示不存在蓝牙设备,否则就说明存在蓝牙设备
2).用isEnabled()方法来确定蓝牙设备是否打开,若没有打开返回值为false,需要重新调用startActivityForResult(enableIntent, REQUEST_ENABLE_BT);方法来打开蓝牙设备

3.扫描设备
主要通过获取本地的BluetoothAdapter 进行扫描和停止扫描,通过BluetoothAdapter 的回调返回扫描到的结果

回调

private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() {
        @Override
        public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {

            if (device != null){
                Log.e("device.getName()",""+device.getName());//设备名称
                Log.e("device.getName()",""+device.getAddress());//设备地址

    };

扫描/停止扫描

 /**
     * 搜索蓝牙或停止搜索
     * @param enable
     */
    @SuppressLint("NewApi")
    private void scanLeDevice(final boolean enable) {
        if (enable) {
            // Stops scanning after a pre-defined scan period.
            mHandler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    mScanning = false;
                                                             mBluetoothAdapter.stopLeScan(mLeScanCallback);  

                }
            }, SCAN_PERIOD);//SCAN_PERIOD=扫描时间

            mScanning = true;

            mBluetoothAdapter.startLeScan(mLeScanCallback);

        } else {
            mScanning = false;
            mBluetoothAdapter.stopLeScan(mLeScanCallback);
        }
    }

连接设备和断开连接

一般我会把连接设备/断开连接设备,设置服务的读/写/通知通道 操作读/写/接受通知, 操作读/写/接受通知这三个步骤放在Service,这样能更好的脱离界面做到随处可操作。

  1. 获取BluetoothAdapter 里的BluetoothDevice,通过BluetoothDevice和之前扫描得到的设备地址进行连接。通过BluetoothGatt回调返回结果,BluetoothGatt这个比较关键之后所有的操作都通过它返回结果

    获取BluetoothAdapter

 /**
     * 初始化蓝牙服务
     * @return
     */
    public boolean initialize() {
        // For API level 18 and above, get a reference to BluetoothAdapter
        // through
        // BluetoothManager.
        if (mBluetoothManager == null) {
            mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
            if (mBluetoothManager == null) {
                Log.e(TAG, "Unable to initialize BluetoothManager.");
                return false;
            }
        }

        mBluetoothAdapter = mBluetoothManager.getAdapter();
        if (mBluetoothAdapter == null) {
            Log.e(TAG, "Unable to obtain a BluetoothAdapter.");
            return false;
        }

        return true;
    }

获取BluetoothDevice并连接

 /**
     * 请求连接
     */
    public boolean connect(final String address) {
        if (mBluetoothAdapter == null || address == null) {
            Log.e(TAG,"BluetoothAdapter not initialized or unspecified address.");
            return false;
        }

        // Previously connected device. Try to reconnect. (先前连接的设备。 尝试重新连接)
        if (mBluetoothDeviceAddress != null&&    address.equals(mBluetoothDeviceAddress)
                && mBluetoothGatt != null) {

            if (mBluetoothGatt.connect()) {

                return true;
            } else {
                return false;
            }
        }

        final BluetoothDevice device = mBluetoothAdapter
                .getRemoteDevice(address);
        if (device == null) {
            Log.w(TAG, "Device not found.  Unable to connect.");
            return false;
        }
       mBluetoothGatt = device.connectGatt(this, false, mGattCallback);


        return true;
    }

BluetoothGatt类

BluetoothGatt 是我们用的最多,她就像Android手机与BLE终端设备建立通信的一个大的管道,只有有了这个管道,我们才有了通信的前提所有的数据读写,通知等通道都是在这个管道传输。

BluetoothGatt 类

 private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status,int newState) {
            if (newState == BluetoothProfile.STATE_CONNECTED) { //连接成功
                mBluetoothGatt.discoverServices(); //连接成功后就去找出该设备中的服务(这方法比较重要必须调)
                BleConnetInter.sendCallBack(1);
                Log.e(TAG,"Ble connect suess");
            } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {  //连接失败
                BleConnetInter.sendCallBack(3);
                Log.e(TAG,"Ble connect fail");
            }
        }

        @Override
        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
            if (status == BluetoothGatt.GATT_SUCCESS) {
                Log.e(TAG,"onServicesDiscovered sucess");
                displayGattServices();//发现服务回调,在这设置服务,读,写,通知通道

            } else {
            }
            Log.e(TAG, "onServicesDiscovered received: " + status);
        }

        @Override
        public void onCharacteristicRead(BluetoothGatt gatt,
                                         BluetoothGattCharacteristic characteristic, int status) {
            System.out.println("onCharacteristicRead");
            if (status == BluetoothGatt.GATT_SUCCESS) {
                  //读通道返回数据
            }
        }

        @Override
        public void onDescriptorWrite(BluetoothGatt gatt,
                                      BluetoothGattDescriptor descriptor, int status) {

          //写通道
        }

        @SuppressLint("LongLogTag")
        @Override
        public void onCharacteristicChanged(BluetoothGatt gatt,
                                            BluetoothGattCharacteristic characteristic) {
           //
            byte[] data = characteristic.getValue();//接受终端返回的通知数据
            if (data != null && data.length>0){
               ParaseBleData(data);//解析数据
            }

        }

        @Override
        public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
            System.out.println("rssi = " + rssi);
        }

        public void onCharacteristicWrite(BluetoothGatt gatt,
                                          BluetoothGattCharacteristic characteristic, int status) {
            System.out.println("--------write success----- status:" + status);

        };
    };

设置服务的读写通知通道

首先了解些这些关键词,一个BLE终端可以包含多个Service, 一个Service可以包含多个Characteristic,每个Characteristic可以说是一个通道。Characteristic是比较重要的,也就是我们所说的通信通道,是手机与BLE终端交换数据的关键,读取设置数据等操作都是操作Characteristic的相关属性。

每个Service有一个唯一的UUID,每个Characteristic也都有一个唯一的UUID,我们就是通过硬件工程师提供的BLE终端的UUID确定我们需要的Service和Characteristic

当连接上设备室 BluetoothGatt 类会回到连接成功的通知,然后掉发现服务的方法BluetoothGatt.discoverServices(); (上面BluetoothGatt 例子里有代码提到),发现服务后在BluetoothGatt 里也有回调,在回调里获取和解析Service和Characteristic,这时我们需要根据提供的UUID去确定我们需要的Service,Characteristic

 /**
     *分析服务列表
     */
    public void displayGattServices(){
        List<BluetoothGattService> listServices = mBluetoothGatt.getServices()
        for (BluetoothGattService service : listServices){
            if (service != null){
                Log.e(TAG,""+service.getUuid());//获取服务的UUID
                if (service.getUuid().toString().equals(HANDBAND_SERVER)){//UUID比对
                    ArrayList<BluetoothGattCharacteristic> mGattCharacteristics =
                            (ArrayList<BluetoothGattCharacteristic>) service.getCharacteristics();//获取Characteristics

                    for (BluetoothGattCharacteristic characteristics : mGattCharacteristics){
                        if (characteristics != null){
                            if (HANDBAND_WRITE.equals(characteristics.getUuid().toString())){//比对写通道(characteristics)
                               // Log.e(TAG,"if characteristicsUUID:"+mGattCharacteristics.size());
                                writeBluetoothGattCharacteristic = characteristics;
                            }else if (HANDBAND_NOTIFICATION.equals(characteristics.getUuid().toString())){//比对通知通道(characteristics)
                                notifyBluetoothGattCharacteristic = characteristics;
                                //setCharacteristicNotification(true);
                                setNotify(notifyBluetoothGattCharacteristic);//设置通知通道
                                setEnableNotify(notifyBluetoothGattCharacteristic, true);设置通知通道
                            }

                        }
                        Log.e(TAG,"for characteristicsUUID:"+characteristics.getUuid());//service.getUuid();
                    }
                }
            }
        }
    }

设置通知通道

  // 设置可通知
    public boolean setNotify(BluetoothGattCharacteristic data_char) {
        if (!mBluetoothAdapter.isEnabled()) { // 没有打开蓝牙
            return false;
        }

        if (data_char == null) {
            return false;
        }
        if (0 != (data_char.getProperties() & BluetoothGattCharacteristic.PROPERTY_NOTIFY)) { // 查看是否带有可通知属性
            mBluetoothGatt.setCharacteristicNotification(data_char, true);
            BluetoothGattDescriptor descriptor = data_char.getDescriptor(UUID
                    .fromString(CLIENT_CHARACTERISTIC_CONFIG));
           //
            descriptor
                    .setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
            mBluetoothGatt.writeDescriptor(descriptor);
        }else if (0 != (data_char.getProperties() & BluetoothGattCharacteristic.PROPERTY_INDICATE)){
            mBluetoothGatt.setCharacteristicNotification(data_char, true);
            BluetoothGattDescriptor descriptor = data_char.getDescriptor(UUID
                    .fromString(CLIENT_CHARACTERISTIC_CONFIG));
            //
            descriptor
                    .setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
            mBluetoothGatt.writeDescriptor(descriptor);
        }
        return true;
    }
 // 设置允许通知
    public boolean setEnableNotify(BluetoothGattCharacteristic data_char,
                                   boolean enable) {
        if (!mBluetoothAdapter.isEnabled()) { // 没有打开蓝牙
            return false;
        }

        if (data_char == null) {
            return false;
        }


        mBluetoothGatt.setCharacteristicNotification(data_char, enable);

        return true;
    }
/**
     * 设置可读
     * @param characteristic
     */
    public void readCharacteristic(BluetoothGattCharacteristic characteristic) {
        if (mBluetoothAdapter == null || mBluetoothGatt == null) {
            Log.w(TAG, "BluetoothAdapter not initialized");
            return;
        }
        mBluetoothGatt.readCharacteristic(characteristic);
    }

4. 操作读/写/接受通知

1.写操作
在发现和解析服务里我们获取到写的Characteristic,通过Characteristic进行对BLE终端设备写的操作

 /**
     * 写操作
     * @param
     */
    public synchronized void wirteCharacteristic(byte[] data) {

        if (mBluetoothAdapter == null || mBluetoothGatt == null
                || writeBluetoothGattCharacteristic == null) {
            Log.w(TAG, "BluetoothAdapter not initialized");
            return;
        }
        writeBluetoothGattCharacteristic.setValue(data);
        Log.e("wirteCharacteristic",""+writeBluetoothGattCharacteristic.getValue());
        String dataStr = "";
        for(int i =0;i<data.length;i++){
            dataStr =dataStr+" , "+data[i];

        }
        Log.e(TAG,"data:"+dataStr);
        mBluetoothGatt.writeCharacteristic(writeBluetoothGattCharacteristic);

    }

2.读/接受通知的消息都是在BluetoothGatt类里返回。

到此Ble 的基本 操作已完成,蓝牙说白了也就是一个信息传输通道,只要掌握了这四个步骤也就可以对她进行操作,无非就是扫描,连接,向终端设备写指令,接受终端指令。

在这我也跟家分享下在开发过程中遇到小坑,我遇到一个坑就是硬件工程师提供的指令协议不正确,因为硬件方面我们是购买别人的成品,他们给我的协议没更新,我按协议给终端发送个设置信息的指令,终端返回成功,通过查询指令查出来结果也是跟我设置的结果一样,但设置就是无效,将协议跟指令来回比对也没发现问题,因为这个设置涉及到几个步骤又来回测试步骤,最后都没能成功,因为设备是购买的成品不能连调,,但他们有成品的APK,通过他们APk设置完成没问题,这是怀疑是不是协议出问题,但不能确定,购买的成品沟通起来也麻烦,最后把发的指令和返回的指令一一列出来,做出个文档让他们比对下最后发现问题,,协议更改了没跟新。其中不想求别人最后是用抓包工具,其中朋友推荐了几篇蓝牙抓包文章,还没研究好,研究好再写出来分享。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值