背景
- 首先贴上官方github地址https://github.com/NordicSemiconductor/Android-nRF-Mesh-Library
- 本次修改基于官方SDK 2.4.1版本.
- 阅读此文章之前,我们默认您对蓝牙mesh协议已经有了一定了解.
- 本次修复了两个问题,一个是节点对于数据库读写的并发问题,另一个问题是对于蓝牙包的单播消息序列对应的问题.
1.节点数据库读写的并发问题
问题分析
起初遇到这个问题,是因为app总是会crash,会出现CME(ConcurrentModificationException),出现这个原因是因为我们mesh中保存节点的数据对象使用的ArrayList,而ArrayList是线程不安全的.在并发的情况下,如果对ArrayList同时进行读写,就会出现CME,从而导致crash,而在我们的应用场景中,对设备同时进行读写的场景比较频繁,所以我们
为了避免出现CME的情况,我们需要应对这种处理场景.
修改内容
经过研究,我们找到了CopyOnWriteArrayList,来替代ArrayList,以保证并发场景下的读写问题.为什么可以保证并发读写,这里我们简单解释一下,CopyOnWriteArrayList在写数据的时候,会先将里面的数组copy出来,进行修改之后,再copy回来.这样就达到了读写分离的效果了.这也是未来语言代码设计的趋势.比如现在很流行的kotlin,就将读写分离的思想,完全融入到语言设计当中去了.
对于并发节点的修改,涉及到很多地方,这里就不一一列举出来了.大致修改了三个list的并发场景,就是NetworkKey,ApplicationKey,ProvisionedMeshNode三个数据的节点列表的并发场景.如下:
List<NetworkKey> netKeys = new CopyOnWriteArrayList<>();
@Ignore
@SerializedName("appKeys")
@Expose
List<ApplicationKey> appKeys = new CopyOnWriteArrayList<>();
@Ignore
@SerializedName("nodes")
@Expose
List<ProvisionedMeshNode> nodes = new CopyOnWriteArrayList<>();
既然在定义的时候,我们对节点进行了修改,那么我们在赋值,节点的导入导出,我们都需要考虑到对数据的处理.
下面还需要注意一点的是,CopyOnWriteArrayList是不支持排序的,因此,在SDK代码中对于数据的排序方法,我们也做了一些小小的修改.改动如下:
//处理CopyOnWriteArrayList的排序问题
List temp = Arrays.asList(nodes.toArray());
Collections.sort(temp,nodeComparator);
nodes.clear();
nodes.addAll(temp);
可以看出,我们是先将数组copy出来,放到ArrayList中进行排序完,再放回去.
2.并发消息收发序列的问题
问题分析
对于收发消息序列的问题,这里主要是涉及到应用层消息接收的顺序问题,因为我们如果同时发了多个单播消息,那么我们没办法将回来的消息一一对应起来,这样可能就会出现消息错乱的问题,这是我们不想得到的结果.
修改内容
所以我们可以在应用层,每次发数据的时候,腾出一个字节出来(保证一定是时间内可以处理256个并发),用来记录消息的序列号,(我们一般命名Tid),用这个序列号来记录我发出去的消息序列号,我们让设备端每次处理完消息,返回给我们的时候,再将Tid带回来,等到我们收到数据的时候,我们将Tid再解出来与之前发送的Tid进行匹配,那么就可以将消息一一对应上了.我们现在来画个时序图来表述序列对应的思想:
因此,问题得到迎刃而解.
如果任何疑问,请联系邮箱:569133338@qq.com