android 蓝牙4.0 ble 低功耗蓝牙

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u010339039/article/details/49909419

一:概述

这段时间做了蓝牙4.0的项目,就是一个蓝牙设备控制手机进行拍照。并且有很多按键,不同的按键对应到手机上有不同的功能,并且组合起来也有不同的功能。

 低功耗蓝牙有中央设备后周边设备的概念手机就是一个中央设备,像我这次试用的一个控制器,
   我试过小米体重秤。来测试玩。

 a.GATT 这是蓝牙技术联盟定义的一个协议。

 b.Service 这个是许多或者一个特征值的集合。

 c.Characteristic 这是特征值。我们需要使用的数据就是这个。
   我们可以读取或者在其值在变化的时候会接收到其变化的回调。

 d.Descriptor 这个是特征值的描述。最开始我有点不太懂这个东西到底哪里能用到。结果在设置
   Characteristic 为可以通知的时候才看到,就是辅助Characteristic 的。这个谷歌官方的解释

这里写图片描述

可以看看我做的一个小demo的效果(一个控制器按键后):

请忽略背景音乐

二:源码解析

现在就通过源码讲解下我这个小demo:

因为这个是需要蓝牙权限,并且在android 系统4.3后才支持的接口。所以在使用蓝牙之前都需要申请权限和判断是否可以使用.

 <uses-permission android:name="android.permission.BLUETOOTH"/>
 <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
 //这里的true标示手机如果不支持低功耗蓝牙就直接不让安装,如果你不想这样的话,可以写false
 <uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>

这里是检查是否支持低功耗蓝牙。

 // Use this check to determine whether BLE is supported on the device. Then
 // you can selectively disable BLE-related features.
 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
        Toast.makeText(this,"不支持蓝牙4.0", Toast.LENGTH_SHORT).show();
        finish();
 }

获取mBluetoothAdapter 需要用他进行判断蓝牙的开关,和搜索蓝牙设备。

 // Initializes Bluetooth adapter.
 final BluetoothManager bluetoothManager =
                (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
 mBluetoothAdapter = bluetoothManager.getAdapter();

 if(mBluetoothAdapter == null){
            Toast.makeText(this,"获取失败!", Toast.LENGTH_SHORT).show();
            finish();
 }

猜这个是干嘛的,居然猜到是判断是否开蓝牙了。哟西。
如果没有开蓝牙当然就让用户开了。会有一个弹窗出来让用户选择是开还是关。

 if (!mBluetoothAdapter.isEnabled()) {
    Intent enableBtIntent = new  Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
    startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
 }

如果用户选择不开的话,就直接结束当前的activity。

   @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        // User chose not to enable Bluetooth.
        if (requestCode == REQUEST_ENABLE_BT && resultCode == Activity.RESULT_CANCELED) {
            finish();
            return;
        }
        super.onActivityResult(requestCode, resultCode, data);
    }

好了。蓝牙也开了。那么开始搜索蓝牙了。mLeScanCallback是一个回调,当搜索到一个蓝牙的时候就回调他的接口

 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);

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

喏,回调就长这个样子。还行吧。
BluetoothDevice 重写了equals的方法用设备的address作为作为判断的
所以 listDevice.contains(device) 是可以判断的。好吧。这是java的基础知识。还是唠叨两句。

  private BluetoothAdapter.LeScanCallback mLeScanCallback =
            new BluetoothAdapter.LeScanCallback() {
                @Override
                public void onLeScan(final BluetoothDevice device, int rssi,
                                     byte[] scanRecord) {
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            if(!listDevice.contains(device)){
                                //不重复添加
                                listDevice.add(device);
                                deviceAdapter.setListDevice(listDevice);
                            }
                        }
                    });
                }
            };

我们找到了蓝牙之后干嘛呢?你觉得还能干嘛呢?肯定就是开始玩dota啦(想的美)。

好了我们准备开始连接蓝牙。

/*mBluetoothLeService是一个android上的后台服务,不是低功耗蓝牙中的服务啊。这个service是从谷歌demo中拷贝的,改动了部分。*/
mBluetoothLeService.connect(device.getAddress());

这个connect中具体的实现,我看见了mBluetoothGatt,就是那个破协议。
然后还看见mGattCallback一个回调,想想应该是连接成功后的一些回调。


    public boolean connect(final String address) {
        if (mBluetoothAdapter == null || address == null) {
            Log.w(TAG, "BluetoothAdapter not initialized or unspecified address.");
            return false;
        }
        if (mBluetoothDeviceAddress != null && address.equals(mBluetoothDeviceAddress)
                && mBluetoothGatt != null) {
            if (mBluetoothGatt.connect()) {
                mConnectionState = STATE_CONNECTING;
                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;
        }
        // We want to directly connect to the device, so we are setting the autoConnect
        // parameter to false.
        mBluetoothGatt = device.connectGatt(this, false, mGattCallback);
        Log.d(TAG, "Trying to create a new connection.");
        mBluetoothDeviceAddress = address;
        mConnectionState = STATE_CONNECTING;
        return true;
    }

下面的就是连接成功后具体的回调。各部分干什么的在注释中可以看看。
broadcastUpdate(…)是发送广播。

private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
            String intentAction;
            if (newState == BluetoothProfile.STATE_CONNECTED) {
                //蓝牙连接成功后
                broadcastUpdate(intentAction);

            } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
                //蓝牙断开连接
                broadcastUpdate(intentAction);
            }
        }
        @Override
        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
            if (status == BluetoothGatt.GATT_SUCCESS) {
            //这个就是发现了蓝牙4.0的服务
                broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED);
            } else {
                Log.w(TAG, "onServicesDiscovered received: " + status);
            }
        }

        @Override
        public void onCharacteristicRead(BluetoothGatt gatt,
                                         BluetoothGattCharacteristic characteristic,
                                         int status) {
            if (status == BluetoothGatt.GATT_SUCCESS) {
            //这个是读取Characteristic,通俗点就是读取我们想要的数据
                broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
            }
        }

        @Override
        public void onCharacteristicChanged(BluetoothGatt gatt,
                                            BluetoothGattCharacteristic characteristic) {
            //这个是我们监听的某个Characteristic变化了。然后发送一个广播
            broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
        }

    };

再看看我们接收广播的时候做了什么 这个广播接收的部分代码。因为代码贴的太多感觉有点不爽了。

  else if (BluetoothLeService.
                    ACTION_GATT_SERVICES_DISCOVERED.equals(action)) {
        //services被发现的状态
        //在这里找到要要读的特征值或者需要通知的特征值
        LogServiceAndChara(mBluetoothLeService.getSupportedGattServices());
  }

我们在看看LogServiceAndChara()函数到底干了啥。(这个函数有点乱。T_T,就贴出部分代码吧)
首先我们遍历所有服务然后找我们想要的特殊服务(-_-我啥都不知道。)这个特殊服务怎么找?
用uuid,找到了我们想找的服务后,然互就开始找特征值,也是用uuid识别。

private void LogServiceAndChara(List<BluetoothGattService> gattServices){

 // Loops through available GATT Services.
 for (BluetoothGattService gattService : gattServices) {
     if(gattService.getUuid().toString().equals(SampleGattAttributes.DEVICE_SERVICE)){
         //找到了这个服务
          List<BluetoothGattCharacteristic> gattCharacteristics =
                        gattService.getCharacteristics();
              for(BluetoothGattCharacteristic gattCharacteristic : gattCharacteristics){                         if(gattCharacteristic.getUuid().toString().equals(SampleGattAttributes.DEVICE_CHARACTER)){
                        //找到了对应的服务

                        if((gattCharacteristic.getProperties() | BluetoothGattCharacteristic.PROPERTY_READ)>0){
                            if(mNotifyCharacteristic != null){
                                mBluetoothLeService.setCharacteristicNotification(gattCharacteristic,false);
                                mNotifyCharacteristic = null;
                            }
                             mBluetoothLeService.readCharacteristic(gattCharacteristic);
                         }
                        if((gattCharacteristic.getProperties() | BluetoothGattCharacteristic.PROPERTY_NOTIFY)>0){
                            mBluetoothLeService.setCharacteristicNotification(gattCharacteristic,true);
                            mNotifyCharacteristic = gattCharacteristic;
                        }
                    }
                }
            }


        }

    }

这两个分别是特征值和服务的uuid

public static String DEVICE_CHARACTER = "0000dfb1-0000-1000-8000-00805f9b34fb";
public static String DEVICE_SERVICE = "0000dfb0-0000-1000-8000-00805f9b34fb"; 

找到了我们想要的特征值后我们就要监听他了,或者读取他,我这里是用的监听。
这里有个点我纠结了挺久的地方。

 BluetoothGattDescriptor descriptor = characteristic.getDescriptor(
                UUID.fromString(SampleGattAttributes.CLIENT_CHARACTERISTIC_CONFIG));

我打印了所有特征值的descriptor的uuid都是一个样,我觉得应该不是硬件上自定义的。应该是蓝牙技术联盟定义的公用的。

然后去蓝牙技术联盟的官网查了下。发现了这个。蓝牙技术联盟CLIENT_CHARACTERISTIC_CONFIG解释

确定了就是他们定义公用的,然后用

descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);

设置这个特征值为可以为监听状态(就是可以变化的时我能收到通知)

    public void setCharacteristicNotification(BluetoothGattCharacteristic characteristic,
                                              boolean enabled) {
        if (mBluetoothAdapter == null || mBluetoothGatt == null) {
            Log.w(TAG, "BluetoothAdapter not initialized");
            return;
        }
        mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);
        //关于CLIENT_CHARACTERISTIC_CONFIG这个descriptor的解释
            BluetoothGattDescriptor descriptor = characteristic.getDescriptor(
                    UUID.fromString(SampleGattAttributes.CLIENT_CHARACTERISTIC_CONFIG));
            descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
            mBluetoothGatt.writeDescriptor(descriptor);
    }

在接收到变化或读取到后都能在这里接收到(这个是来自广播中的方法)

   else if (BluetoothLeService.ACTION_DATA_AVAILABLE.equals(action)) {
                //接收到数据的状态
                str += intent.getStringExtra(BluetoothLeService.EXTRA_DATA);
                str += "\n\r";
                tv.setText(str);
  }

源码下载

加个好友共同学习(不是公众号):

这里写图片描述

因为小弟水平有限,如果有写的有问题,希望指出。

猜你喜欢

转载自blog.csdn.net/u010339039/article/details/49909419