Android 蓝牙4.0使用心得

来源:互联网 发布:maven实战源码 许晓斌 编辑:程序博客网 时间:2024/05/29 18:04
 最近在公司做了个蓝牙项目,之前刚出道的时候做过个智能手环的项目,那时蓝牙模块是开发组长写的,我看的也是一知半解,很多不了解的东西现在看起来有些新心得,写下来记录下.

蓝牙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设置完成没问题,这是怀疑是不是协议出问题,但不能确定,购买的成品沟通起来也麻烦,最后把发的指令和返回的指令一一列出来,做出个文档让他们比对下最后发现问题,,协议更改了没跟新。其中不想求别人最后是用抓包工具,其中朋友推荐了几篇蓝牙抓包文章,还没研究好,研究好再写出来分享。

原创粉丝点击