文章目录
- 前言
- 思考
- 分析
- 定位
前言
在做蓝牙设备通信时,遇到一个奇葩的问题,公司另一个部门开发的蓝牙组件库,把蓝牙设备BluetoothDevice进行了序列化,在连接时候又进行反序列化。但是当我去调试我的项目时,发现发序列化后的对象是个“{}”。
起初,我还纳闷,到底是为什么?为什么?!!!
其他项目组引用时候都没事,怎么到我这里就出问题了?!
难道是我引用组件的姿势不对?
后来对比了下,我发现:把项目的targetSdk降到27就好了!
问题是解决了,但是,为什么呢?
本着不抛弃、不放弃的原则,我去翻看了源码,对比了sdk27和sdk28的BluetoothDevice的源码,结果发现:
除了多了两个方法,其他完全没什么大的区别啊!
这就奇怪了!为什么呢?!!
头皮上的痘痘都挠破了,也想不出来啊!!
思考
冷静下来,认真思考:
问题现象是:BluetoothDevice反序列化后得到的是{},而不是null。说明这个对象还是存在的,只是对象里的属性没有了。
分析
序列化和反序列化都是使用的gson,会不会是gson的bug呢?
于是,我对gson序列化BluetoothDevice的代码,打上断点,一步一步的去排查,最终,在ReflectiveTypeAdapterFactory
中找到了问题的根源。
原来gson内部也是通过反射获取对象的属性,然后进行一系列的操作,不啦不啦不啦…的
如果序列化后得到的是{},那这里是不是就会有问题。
于是,我通过debug,对比了sdk=27和sdk=28时,raw.getDeclaredFields()
这里的区别:
当sdk=27时:
当sdk=28时:
震惊了!
sdk=28时,通过反射获取属性,竟然没有找到mAddress,且数组长度比sdk=27是少了好多!
定位
查找了安卓不同api的差异,我终于在安卓9.0中的“行为变更”中发现了端倪:
对于官方的这块解释,我的理解是:源码中的属性不再是你想通过反射就能自由获取的了!
其实,这个问题在好多功能使用时也遇到过,就是随便从百度搜索的结果中,就能发现之前的有些功能,动不动就是通过反射,调用源码内部的某些私有方法,以达到出奇制胜的效果。在早期的安卓版本中,确实能起到一定的作用,但是随着版本的不断提升,这些方法往往变得调用不通了。
最终,我也是定位到了问题的原因,既然是系统不允许再访问私有属性,那gson自然也拿不到maddress。自然得到的序列化对象没有对应的属性值。
所以,还是改自己的代码吧,别序列化BluetoothDevice了,搞得什么飞机,序列化它干嘛,直接用变量缓存不就完了么!