文章目錄
- 前言
- 思考
- 分析
- 定位
前言
在做藍牙設備通信時,遇到一個奇葩的問題,公司另一個部門開發的藍牙組件庫,把藍牙設備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了,搞得什么飛機,序列化它干嘛,直接用變量緩存不就完了么!