Android蓝牙那点事——深刻了解Android蓝牙Bluetooth《进阶篇》

深刻了解Android蓝牙Bluetooth——《基础篇》一篇中咱们对蓝牙的各个版本的有了一个认识,蓝牙版本的历程及其优劣式介绍。那么接下来我们就深刻一点继续开车进入BLE的进及篇章。

BLE蓝牙设备链接读取的顺序:

Markdown
Markdown

蓝牙BLE4.x

BLE分为三部分:

  • Service
  • Characteristic
  • Descriptorandroid

    这三部分都用UUID做为惟一标识符。UUID为这种格式:0000ffe1-0000-1000-8000-00805f9b34fb。好比有3个Service,那么就有三个不一样的UUID与Service对应。这些UUID都写在硬件里,咱们经过BLE提供的API能够读取到。git

  • 一个BLE终端能够包含多个Service, 一个Service能够包含多个Characteristic,一个Characteristic包含一个value和多个Descriptor,一个Descriptor包含一个Value。Characteristic是比较重要的,是手机与BLE终端交换数据的关键,读取设置数据等操做都是操做Characteristic的相关属性。

API相关介绍

  • 1.先介绍一下关于蓝牙4.0中的一些名词吧:
    (1)GATT(Gneric Attibute Profile)程序员

    经过ble链接,读写属性类小数据Profile通用的规范。如今全部的ble应用Profile 都是基于GATTgithub

    • (2)ATT(Attribute Protocal) GATT是基于ATT Potocal的ATT针对BLE设备专门作的具体就是传输过程当中使用尽可能少的数据,每一个属性都有个惟一的UUID,属性chartcteristics and Service的形式传输。api

      • (3)Service是Characteristic的集合。
      • (4).Characteristic 特征类型。

      好比。有个蓝牙ble的血压计。他可能包括多个Servvice,每一个Service有包括多个Characteristic数组

      注意:蓝牙ble只能支持Android 4.3以上的系统 SDK>=18bash

  • 2.如下是开发的步骤:微信

  • 2.1首先获取BluetoothManagerasync

  • 2.2获取BluetoothAdapter
  • 2.3建立BluetoothAdapter.LeScanCallback
  • 2.4.开始搜索设备。
  • 2.5.BluetoothDevice 描述了一个蓝牙设备 提供了getAddress()设备Mac地址,getName()设备的名称。
  • 2.6开始链接设备
  • 2.7链接到设备以后获取设备的服务(Service)和服务对应的Characteristic。
  • 2.8获取到特征以后,找到服务中能够向下位机写指令的特征,向该特征写入指令。
  • 2.9写入成功以后,开始读取设备返回来的数据。
  • 2.十、断开链接
  • 2.十一、数据的转换方法
    大概总体就是如上的步骤。可是也是要具体根据厂家的协议来实现通讯的过程。
    那么具体要怎么使用呢?咱们据需开车往下走。
    一.添加权限
      和经典蓝牙同样,应用使用蓝牙,须要声明BLUETOOTH权限,若是须要扫描设备或者操做蓝牙设置,则还须要BLUETOOTH_ADMIN权限:
    <uses-permission android:name="android.permission.BLUETOOTH"/>
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>复制代码
    除了蓝牙权限外,若是须要BLE feature则还须要声明uses-feature:
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>复制代码

按时required为true时,则应用只能在支持BLE的Android设备上安装运行;required为false时,Android设备都可正常安装运行,须要在代码运行时判断设备是否支持BLE feature:ide

// 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, R.string.ble_not_supported, Toast.LENGTH_SHORT).show();
    finish();
}复制代码

第一步:开启蓝牙:

  • 1.首先获取有BluetoothAdapter两种方式:
private BluetoothManager bluetoothManager;

bluetoothManager =   (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = bluetoothManager.getAdapter();复制代码

或者是:

mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();复制代码

两行方式都是能够的。

注:这里经过getSystemService获取BluetoothManager,再经过BluetoothManager获取BluetoothAdapter。BluetoothManager在Android4.3以上支持(API level 18)。

  • 2.判断手机设备是否有蓝牙模块
// 检查设备上是否支持蓝牙
        if (mBluetoothAdapter == null) {
            showToast("没有发现蓝牙模块");
            return;
        }复制代码
  • 3.开启蓝牙设备

    开启蓝牙设备有两种方式:

    • 第一种直接简单暴力不给用户进行提示:

      if (!mBluetoothAdapter.isEnabled()) {
                mBluetoothAdapter.enable();
      }复制代码
    • 第二种直优雅的践行开启而且有弹框进行提示,隐式启动Intent:

      if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {
        Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
        startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
      }复制代码
  • 4.扫描蓝牙设备

    我这里是把扫描到的BLE地址存放到List集合中去。这里咱们能够写一个方法进行封装一下。

/***********
     * 扫描设备
     ********/
    private void scanLeDevice(final boolean enable) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
            if (enable) {
                devices.clear();//清空集合
                // Stops scanning after a pre-defined scan period.
                mHandler.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
                            mBluetoothAdapter.stopLeScan(mLeScanCallback);
                        }
                    }
                }, INTERVAL_TIME);
                mBluetoothAdapter.startLeScan(mLeScanCallback);
            } else {
                try {
                    mBluetoothAdapter.stopLeScan(mLeScanCallback);
                } catch (Exception e) {
                }
            }
        }
    }复制代码

在这个扫描方法中,咱们在AndroidStudio或者是Eclipse中会看到startLeScan方法会有横线,代表这个方式显示过时的方法,那么

若是你只须要搜索指定UUID的外设,你能够调用 startLeScan(UUID[], BluetoothAdapter.LeScanCallback)方法。
其中UUID数组指定你的应用程序所支持的GATT Services的UUID。

那么LeScanCallback的初始化代码以下:

private void initCallBack(){
     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
            mLeScanCallback = new BluetoothAdapter.LeScanCallback() {
                @Override
                public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            if (device != null) {
                                if (!TextUtils.isEmpty(device.getName())) {
                                   // devices.add(device);
                                    String name = device.getName();
                                    if (name.contains(BluetoothDeviceAttr.OYGEN_DEVICE_NAME)) {
                                        if (!devices.contains(device)) {
                                            devices.add(device);
                                        }
                                    }
                                }
                            }
                        }
                    });
                }
            };
        } else {
            getToast("设备蓝牙版本太低");
            return;
        }
}复制代码

那么若是在设备多的状况下咱们讲搜出不少的设备。咱们能够选择咱们所须要的地址进行连接。可是这类要注意的是:搜索时,你只能搜索传统蓝牙设备或者BLE设备,二者彻底独立,不可同时被搜索.

  • 5.进行连接设备
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);复制代码

这里调用的是device的connectGatt方法

/**
     * Connect to GATT Server hosted by this device. Caller acts as GATT client.
     * The callback is used to deliver results to Caller, such as connection status as well
     * as any further GATT client operations.
     * The method returns a BluetoothGatt instance. You can use BluetoothGatt to conduct
     * GATT client operations.
     * @param callback GATT callback handler that will receive asynchronous callbacks.
     * @param autoConnect Whether to directly connect to the remote device (false)
     *                    or to automatically connect as soon as the remote
     *                    device becomes available (true).
     * @throws IllegalArgumentException if callback is null
     */
    public BluetoothGatt connectGatt(Context context, boolean autoConnect,
                                     BluetoothGattCallback callback) {
        return (connectGatt(context, autoConnect,callback, TRANSPORT_AUTO));
    }复制代码

 api中阐述的是第一个参数是上下文对象Context,第二个参数是是否自动链接,第三个是蓝牙的GattCallback回调。

private BluetoothGattCallback GattCallback = new BluetoothGattCallback() {
    // 这里有9个要实现的方法,看状况要实现那些,用到那些就实现那些
    //当链接状态发生改变的时候
    @Override
    public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState){

    };
    //回调响应特征写操做的结果。
    @Override
    public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status){

    };
    //回调响应特征读操做的结果。
    @Override
    public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
    }
    //当服务被发现的时候回调的结果
    @Override
    public void onServicesDiscovered(BluetoothGatt gatt, int status) {
    }
    当链接能被被读的操做
    @Override
   public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {

            super.onDescriptorRead(gatt, descriptor, status);
      }  
    ......
};复制代码

 链接的过程咱们一个经过service来进行链接,也能够在activity中进行操做。
好,到此为止,一个BLE蓝牙链接设备的整个流程咱们已经清楚完毕。

Android4.x的蓝牙不太成熟性

 可是在实际操做过程当中不免会出现一些比较坑人的问题。好比我用一个地址进行蓝牙设备链接,偶尔会出现蓝牙链接不上或者是说链接上设备后不返回数据等问题。那么此时咱们可能会重启一下蓝牙或手机就立马有成功了。此时咱们千万不能蒙蔽,也不要怀疑本身的人生。这是由于Android4.x的蓝牙仍是不太成熟。目前能够来讲是个测试阶段。

  • 手机可能会搜索不到蓝牙设备
  • 有时候会在广播中接收不到数据
  • 出现异常须要从新启动手机或者是重启才能恢复正常

这个时候咱们怎么办呢?

此时不要抱怨什么,难到咱们做为Android程序员就注定如此的苦逼吗?

答案是否认的。

如何去优化呢?那么咱们就应该从UI界面,用户体验上进行操做来实现

  • 作一个定时器,若是在在肯定蓝牙设备一打开而且存在的状况系,能够在手机搜索5s内没有搜索到蓝牙设备时重启蓝牙,而且在广播中接收到蓝牙开启后再次搜索
  • 能够在UI上进行对用户进行相对应的提示

    • 当蓝牙为启动时,提示用户去开启器蓝牙
    • 当蓝牙开启后,在处在开启状态后,提示用户蓝牙正在开启...
    • 蓝牙已开启,设备并无链接上,提示用户去进行链接
    • 设备正在链接上手机,提示用户,正在链接,请等待...
    • 蓝牙设备链接上手机,正在读取,提示数据正在读取中...

      咱们不能子在Android系统上来操做什么,咱们在体验上作到了咱们能作的就能够的。

      手机蓝牙链接BLE设备要求

  • 手机Android 4.3以上的系统 SDK>=18
  • 蓝牙版本>=4.0

学习参考道demo下载地址:
github.com/androidstar…

学到这里,关于AndroidBLE蓝牙链接咱们已经基本上实现了蓝牙的搜索,链接,读取等。

你们项目中若是常常涉及到硬件好比手环,温度计,汗液仪,心电图,血压计等这些ble的蓝牙设备,就必定会用到蓝相关方面的知识。这里笔者先给你们提早踩一下坑,进行了总结,为后面的小伙伴在研究蓝牙方面尽可能的少踩一些坑。如多对蓝牙的历程还未有清楚的认识,请参考深刻了解Android蓝牙Bluetooth4.0——《基础篇》

若是你以为此文对您有所帮助,欢迎入群 QQ交流群 :232203809   
微信公众号:终端研发部

技术+职场
技术+职场

(欢迎关注学习和交流)复制代码
相关文章
相关标签/搜索