为了账号安全,请及时绑定邮箱和手机立即绑定

深入了解Android蓝牙Bluetooth——《进阶篇》

标签:
Android

蓝牙BLE4.x

BLE分为三部分:

  • Service
  • Characteristic
  • Descriptor
    这三部分都用UUID作为唯一标识符。UUID为这种格式:0000ffe1-0000-1000-8000-00805f9b34fb。比如有3个Service,那么就有三个不同的UUID与Service对应。这些UUID都写在硬件里,我们通过BLE提供的API可以读取到。
    一个BLE终端可以包含多个Service, 一个Service可以包含多个Characteristic,一个Characteristic包含一个value和多个Descriptor,一个Descriptor包含一个Value。Characteristic是比较重要的,是手机与BLE终端交换数据的关键,读取设置数据等操作都是操作Characteristic的相关属性。

API相关介绍

  • 先介绍一下关于蓝牙4.0中的一些名词吧:
    (1)GATT(Gneric Attibute Profile)
    通过ble连接,读写属性类小数据Profile通用的规范。现在所有的ble应用Profile 都是基于GATT
    (2)ATT(Attribute Protocal) GATT是基于ATT Potocal的ATT针对BLE设备专门做的具体就是传输过程中使用尽量少的数据,每个属性都有个唯一的UUID,属性chartcteristics and Service的形式传输。
    (3)Service是Characteristic的集合。
    (4)Characteristic 特征类型。
    比如。有个蓝牙ble的血压计。他可能包括多个Servvice,每个Service有包括多个Characteristic
    注意:蓝牙ble只能支持Android 4.3以上的系统 SDK>=18

  • 以下是开发的步骤:

    1、首先获取BluetoothManager
    2、获取BluetoothAdapter
    3、创建BluetoothAdapter.LeScanCallback
    4、开始搜索设备。
    5、BluetoothDevice 描述了一个蓝牙设备 提供了getAddress()设备Mac地址,getName()设备的名称。
    6、开始连接设备
    7、连接到设备之后获取设备的服务(Service)和服务对应的Characteristic。
    8、获取到特征之后,找到服务中可以向下位机写指令的特征,向该特征写入指令。
    9、写入成功之后,开始读取设备返回来的数据。
    10、断开连接
    11、数据的转换方法
    大概整体就是如上的步骤。但是也是要具体根据厂家的协议来实现通信的过程。

开发过程

  1. 添加权限
    和经典蓝牙一样,应用使用蓝牙,需要声明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:

    // 设备是否自持BLE
    private void isSupportBLE() {
        if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
            Toast.makeText(this, "该设备不自持BLE", Toast.LENGTH_SHORT).show();
            finish();
        }
    }
  1. 开启蓝牙
    2.1首先获取有BluetoothAdapter两种方式:
//获取BluetoothAdapter第一种方式
private BluetoothManager bluetoothManager;
bluetoothManager =   (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = bluetoothManager.getAdapter();
//获取BluetoothAdapter第二种方式
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();

注:这里通过getSystemService获取BluetoothManager,再通过BluetoothManager获取BluetoothAdapter。BluetoothManager在Android4.3以上支持(API level 18)。
2.2 判断手机设备是否有蓝牙模块

// 检查设备上是否支持蓝牙
if (mBluetoothAdapter == null) {
     showToast("没有发现蓝牙模块");
     return;
}

2.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);
}

2.4 扫描蓝牙设备
我这里是把扫描到的BLE地址存放到List集合中去。这里我们可以写一个方法进行封装一下。

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

                mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
                mBluetoothAdapter.startLeScan(mLeScanCallback);
            } else {
                try {
                    mBluetoothAdapter.stopLeScan(mLeScanCallback);
                } catch (Exception e) {

                }
            }
        }
    }

在这个扫描方法中,我们在AndroidStudio或者是Eclipse中会看到startLeScan方法会有横线,表明这个方式显示过期的方法,那么
如果你只需要搜索指定UUID的外设,你可以调用 startLeScan(UUID[], BluetoothAdapter.LeScanCallback)方法。
其中UUID数组指定你的应用程序所支持的GATT Services的UUID。
么LeScanCallback的初始化代码如下:

    //初始化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 {
            Toast.makeText(this, "设备蓝牙版本过低", Toast.LENGTH_SHORT).show();
        }
    }

那么如果在设备多的情况下我们将搜出很多的设备。我们可以选择我们所需要的地址进行链接。但是这类要注意的是:搜索时,你只能搜索传统蓝牙设备或者BLE设备,两者完全独立,不可同时被搜索.
2.5 连接蓝牙设备

    //连接设备
    private void connectDevice(String address) {
        BluetoothDevice bluetoothDevice = mBluetoothAdapter.getRemoteDevice(address);
        if (bluetoothDevice == null) {
            Toast.makeText(this, "Device not found.  Unable to connect", Toast.LENGTH_SHORT).show();
            return;
        }

        mBluetoothGatt = bluetoothDevice.connectGatt(this, false, bluetoothGattCallback);
    }

这里调用的是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 bluetoothGattCallback = 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界面,用户体验上进行操作来实现
1、做一个定时器,如果在在确定蓝牙设备一打开并且存在的情况系,可以在手机搜索5s内没有搜索到蓝牙设备时重启蓝牙,并且在广播中接收到蓝牙开启后再次搜索
2、可以在UI上进行对用户进行相对应的提示

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

手机蓝牙连接BLE设备要求

  • 手机Android 4.3以上的系统 SDK>=18
  • 蓝牙版本>=4.0
    学到这里,关于AndroidBLE蓝牙连接我们已经基本上实现了蓝牙的搜索,连接,读取等。
    Demo地址
点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消