目录

1. 功能部分

1.1 BLE简介

1.2. 详细流程

1.3 几个注意点:

2. 界面部分

3. 遇到的坑:

4. 推荐学习资料



即将迈入新阶段,贼开心,总结下。如果有误,麻烦指出。

先看下最终效果(图中的“刷新”只是方便测试,表明从硬件接收到了新的数据包而已):

1. 功能部分

1.1 BLE简介

核心功能使用的是Android官方提供的BLE SDK。在BLE协议中,有两个角色——周边和中央。周边用来提供数据,中央用来使用数据。在我的应用中,周边即BLE模块,中央即安卓手机。

先介绍2个关键类:

(1)BluetoothGattServer作为周边来提供数据;BluetoothGattServerCallback返回周边的状态。

(2)BluetoothGatt作为中央来使用和处理数据;BluetoothGattCallback返回中央的状态和周边提供的数据。

我们要做的是什么呢?是拿到这个BluetoothGatt!拿到了它,调用API中提供的方法,就可以通过BluetoothGattCallback和周边BluetoothGattServer交互了。

协议、服务(Service)、特征(Characteristic)等基本概念参考

1.2. 详细流程

我把Android蓝牙开发分为3个大步骤:扫描、连接和交互。

(1).声明BLE相关权限。

                

(2).判断设备是否支持BLE,若支持,则获取到设备的蓝牙适配器。

        //检查设备是否支持BLE        if(!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)){            Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_SHORT).show();            finish();        }        //蓝牙管理器,主要用于获取蓝牙适配器和管理所有和蓝牙相关的东西。        final BluetoothManager bluetoothManager =                (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);        //用蓝牙管理器获得蓝牙适配器,每一台支持蓝牙功能的手机都有一个蓝牙适配器。        mBluetoothAdapter = bluetoothManager.getAdapter();        //检查设备是否支持BLE        if(mBluetoothAdapter == null){            Toast.makeText(this, R.string.bluetooth_not_supported, Toast.LENGTH_SHORT).show();            finish();            return;        }        //andoird 6.0需要开启定位请求        mayRequestLocation();

(3).判断当前手机是否已经开启了蓝牙功能,若未开启,提示用户开启蓝牙功能。(跟共享单车APP里的步骤一样一样的)

        //如果当前手机蓝牙未开启,弹出dialog提示用户开启。        if(!mBluetoothAdapter.isEnabled()){            if(!mBluetoothAdapter.isEnabled()){                Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);                startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);            }        }

(4).扫描周边BLE设备,获取扫描到的设备。

        mBluetoothAdapter.startLeScan(mLeScanCallback);//扫描很费电,要预设扫描周期,扫描一定周期就停止扫描
            //作为BLE扫描结果的接口,实现如下。        private BluetoothAdapter.LeScanCallback mLeScanCallback =            new BluetoothAdapter.LeScanCallback() {                @Override                public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {                   ...                }            };

(5).调用连接到GATT服务端的方法;

            mBluetoothGatt = device.connectGatt(this, false, mGattCallBack);//这个mBluetoothGatt很关键,后面要调用这个类的方法。

到此为止,已经有了一个成果——已经拿到了BluetoothGatt!!!

(6).连接到GATT服务端成功后,发现服务;发现服务并成功获取服务后,获取特征值,得到属性为Write和Notify的特征值,获取到特征值即代表手机与蓝牙设备连接成功;然后使能属性为Noitfy的特征值,只有使能特征值之后,特征值发生改变,才会马上回调到onCharacteristicChanged()中。

        private final BluetoothGattCallback mGattCallBack = new BluetoothGattCallback() {            @Override            public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { //连接状态改变时候回调到此处                if(newState == BluetoothProfile.STATE_CONNECTED){                    //连接上之后发现服务                    mBluetoothGatt.discoverServices();                    ...            }            //发现到服务之后回调到此处            @Override            public void onServicesDiscovered(BluetoothGatt gatt, int status) {                    //优先根据xxx蓝牙模块的服务UUID来查找服务                    BluetoothGattService service = gatt.getService(UUID.fromString(BleGattAttributes.BLE_Service));                    if(service != null){                        //根据xxx蓝牙模块的特征值UUID来查找特征值                        mWriteCharacteristic = service.getCharacteristic(UUID.fromString(BleGattAttributes.BLE_Write_Characteristic));                        mNotifyCharacteristic = service.getCharacteristic(UUID.fromString(BleGattAttributes.BLE_Notify_Characteristic));                    }                        if(mNotifyCharacteristic != null){                        //找到特征值才代表连接成功                        //使能通知,使能属性为Notify的特征值之后,以后特征值改变时候,就会回调到onChracteristicChanged()中。                        setCharacteristicNotification(mNotifyCharacteristic, true);                    }            }            //读取特征值后回调到此处            @Override            public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {                ...            }            //特征值发生改变时回调到此处            @Override            public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {                ...            }            //写入特征值后回调到此处            @Override            public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {                ...            }        };

(7).接下来手机就可与蓝牙模块交互了。即调用发送函数,向蓝牙模块发送数据包;有数据包返回时候,会回调到onCharactericChanged()中。

        //向蓝牙设备发送数据                  mWriteCharacteristic.setValue(data);        mBluetoothGatt.writeCharacteristic(mWriteCharacteristic);

(8).按照协议对收到的数据帧进行解析并显示。我的处理是把解析出来的数据存放到全局变量中,便于在Fragment中使用,然后适配器负责把这些数据与界面联接起来。

(9).数据需要定时更新的情况下,按照一定的周期重复步骤7、8。

1.3 几个注意点:

(1)BLE要求每包数据最多20字节,如果数据量大于20字节,难免要分包发送再拼接下,拼接后再处理。所以BLE更适合小数据量的应用,用来传递音视频的话,可能心会比较累……
(2)我是对拼接后的数据包处理了一下,判断了下拼接后的数据包的正误才处理的(不知道是否必要,不过加了更安全),有空贴出来我的写法。

2. 界面部分

整体采用界面效果跟微信相似,底部导航栏+ViewPager实现了滑动切换Fragment的效果,某些需要显示信息量大的页面采用ListView。

ListView的使用:其中第3、4个页面用的是列表,以第3个页面为例说明ListView的用法。第3个页面中的ListView中要装入的是单体电压数据,但是数组cellVoltage中的数据是无法直接传给ListView的,需要借助适配器来实现。我选择的适配器是ArrayAdapter(因为它可以通过泛型来指定要适配的数据类型),在构造适配器对象时候,将要用的列表样式和数据源作为参数传递进去。然后,将实例化出来的列表对象和构造出来的适配器关联在一起。Ok啦。

ViewPager的使用:ViewPager是一个页面切换的组件,可以往里面填充多个View,通过触摸屏左右滑动来切换不同的View。和ListView一样,需要一个适配器(适配器的作用:将复杂的数据填充在指定视图界上,数据源多种多样,而ListView等组件显示数据的格式是有要求的,适配器就是桥梁),将要显示的View和我们的ViewPager进行绑定,而ViewPager有自己特定的Adapter—PagerAdapter。ViewPager实现

3. 遇到的坑:

  1. 功能部分,上下位机数据交互时候(上位机指手机,下位机指带有BLE模块的硬件系统),读到的数据比较混乱,没有按照请求的包的顺序来收发。经过debug单步调试,找到了原因:在上下位机数据交互时,数据的载体是一个叫做“特征值”的东西(基本概念的介绍有空再加上),上下发送数据时候,都会改动这个特征值,两方同时操作一个特征值,不出现混乱才奇怪呢。解决方法是:当收到了下位机回复的第i包数据时候,上再向下要第i+1包数据,具体来说是这样的顺序:(假设一次数据更新分为了5包数据)上向下发送命令包1——>下向上回复数据包1——>上向下发送命令包2——>下向上回复数据包2——>…——>上向下发送命令包5——>下向上回复数据包5。严格控制这样的顺序,保证了每个时刻,上位机和下位机只有一个在操作特征值。
  2. 界面部分,左右滑动来切换页面的功能最初用了一个库,但是实验过程中发现,当数据刷新频率很快且左右滑动频率很快时候,边缘的页面(最靠右边的或者倒数第二个)就会崩溃。原因我认为在于库中对ViewPager的缓存机制用得不好。(ViewPager的缓存机制:ViewPager不仅会缓存当前页、还会缓存前一页和后一页,比如当前位于第2页,就会缓存第1、2、3页;比如当前位于第4页时候,缓存的是3和4)。后来我就索性自己实现了下这个功能。

4. 推荐学习资料

Android:

《第一行代码》郭霖,适合安卓新手学习。pdf下载

 

BLE:

https://blog.csdn.net/jimoduwu/article/details/21604215

https://www.jianshu.com/p/c7bb4e8f9fe6

https://developer.android.com/guide/topics/connectivity/bluetooth-le#terms //Andoid BLE开发官方文档

https://blog.csdn.net/shb2058/article/details/51279731 //Andoid BLE开发官方文档中文翻译

更多相关文章

  1. 蓝牙防丢器原理、实现与Android(安卓)BLE接口编程
  2. Android——开发环境简介
  3. Android中Notification的小发现
  4. android下的蓝牙A2DP
  5. Android之Intent深入
  6. Android音频编辑之音频合成功能
  7. 深入Android(安卓)【三】 —— 组件入门
  8. Android(安卓)内容提供器---创建内容提供器(概要)
  9. android原生的数据库实现[ContentProvider+SQLiteOpenHelpe...

随机推荐

  1. Android(安卓)之 AlarmManager(定时器)
  2. Android中Paint字体属性的一些设置
  3. 25、从头学Android之多媒体--使用SoundPo
  4. Android之获取屏幕的三种方式
  5. Android(安卓)C++ 线程使用
  6. Android(安卓)View 相关源码分析之二 继L
  7. [置顶] Android学习计划
  8. Android(安卓)SystemServer 启动流程
  9. 【Android】8.1 主题基本用法
  10. android自定义带下划线EditText