随笔:公司App升级第三方SDK踩坑两周小记

3,236 阅读11分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第1天,点击查看活动详情

0x1、引言

如题,最近两周疲于折腾公司第三方SDK升级,掉坑里一直出不来,前天突发奇想,终于定位到问题原因。

问题很简单,却花费了这么长时间,终究还是我太菜了

BUG虽然解了,但觉得还是有必要 记录复盘下,以便下次遇到类似问题时,能找到更好的切入点,更快地把问题解决。

问题概述

现在哪个房产类APP不上VR全景图啊,所以我司也整了一个,经纪人录盘时,使用全景相机对盘源进行拍摄,在录盘填信息的同时,把全景图也上传到服务器。而他们录盘的过程是这样的:

  • 使用 全景相机官方APP,连接相机,对盘源进行拍摄,官方APP拼接生成全景图;
  • 拍完打开 公司APP,来到录盘页,填写录盘信息,然后打开手机相册,选择对应的全景图上传;

录一次盘得在两个APP间来回切换,可以是可以,但流程繁琐,效率也低,他们更希望:

直接就在公司APP上就可以完成相机连接、拍摄、拼接和上传。

任务传到客户端这边,所以我们需要:

集成官方SDK,参考官方Demo和文档,根据具体业务进行二次开发。

杰哥徒手封装的Library已经稳定跑了两年:

本以为可以一劳永逸,安享晚年,最近却出了幺蛾子:

有经纪人买了新款相机,然后用公司APP无法拍照。

em...硬件、固件升级,需要更新下SDK,可以理解,那就升吧,正当我以为这次升级,会像当初刚集成时那样 丝滑流畅,却是 磨人踩坑 的开始...


0x2、第一道坎:SDK自带预览组件黑屏

很快新的SDK到位,Library替换上新AAR,API有些变动,跟着Demo改改,算是跑起来了,但问题也来了:

拍摄预览页 → 关闭并跳转新页面 → 再次打开预览页 → 预览组件黑屏

看了下官方Demo有定义这个预览组件,但却 没用到,再看一下文档:

难不成是销毁时还要手动去释放什么资源么?上个版本没这个问题啊,尝试寻求技术支持:

隔天早上,我又试着改了下官方Demo,加上预览组件部分的代码,也会黑屏,再次发问:

在等待回应的同时,我还对可能有关的方法调用都加上了 日志打印,尝试找出问题原因:

对面说周一开早会,晚点看问题,结果等到了第二天中午:

按照他说的,在页面销毁时主动调用预览组件的 releasePreview(),预览的确正常了,问题好像解决了?但这样的操作,却引起了第二个问题...


0x3、第二道坎:小米机型闪退

页面销毁和创建多次会报异常奔溃,有时甚至发生在图片拼接时,再次发问,并提供机型和报错日志:

可能是我表达能力的问题,对面有点懵了:

重新组织了一下语言并附上更详细的奔溃日志,同时把demo中的改动文件发给他们测试:

得到的回复:

em...重复了30多次,看得出对面也很想帮我们解决问题,奈何 鞭长莫及

我甚至跟领导开玩笑说,要不把测试机寄过去那边给他们排查下吧,哈哈哈!另外,同事说他们这两款机型都是很老的机型了,会不会是 CPU架构 的问题,于是在build.gradle中指定了架构:

ndk {
    abiFilters 'armeabi-v7a', 'arm64-v8a'
}

运行后发现并没什么卵用,而网上搜了一圈这个异常:

Fatal signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x10 in tid 2261

发现一个唯一有用且这边能做的事情,删掉这个文件夹/data/local/tmp/perfd,删了可以,但治标不治本,删完下次又得删,麻烦且不说,公司APP也没有访问这个文件夹的权限啊 (root权限)。

唉,无解,关键代码都写在so库里,就一 黑盒子,奈何笔者太菜,不会 so库逆向调试,只能到这里了...

后面领导说先这样把,目前就一个经纪人用新相机,而且用的华为手机,打包给测试测下,没其它问题就先发包吧~


0x3、第三道坎:打Release包无法拍照

隔空艰难对接一周,最后妥协收场,正当我以为此事了解,可以 回归摸鱼日常,结果测试 一声惊雷平地起

!跟进了一下发现:Debug包可以,Release就不行,旧相机可以,新相机不行,看下日志:

果断再次向 技术支持 请求援助,这次吸取教训, 把问题整理清楚再提问:

唉,大概是因为 人类的悲欢并不相通 吧,并没有得到有效帮助,但也给了我们一个思路:可能是混淆的问题

但混淆代码 没Keep住,调用时不是报 ClassNotFound 之类的Exception吗?难不成他们用try-catch包住不往外抛?半信半疑,不过眼下好像没有更好排查方向了,死马当活马医吧,这也是 钻牛角尖 的开始...


① 混淆排查

说到混淆,这不得打开:《补齐Android技能树 - 从害怕到玩转Android代码混淆》,边看边排查,首先 混淆规则是叠加的

在他们的AAR里,有看到混淆文件:

理论上来说,这里写了的话,app模块是不要再写一遍的,2333,但是为了保证一定生效,还是再写一遍吧。但重新打包后一样不行,可能这个 keep规则没有完全覆盖?于是写了匹配范围更广的规则:

-keep class com.hozo.** { *;}
-keep class com.hznovi.** { *; }
-keep interface com.hozo.** { *; }
-keep interface com.hznovi.** { *; }

一样不行,不应该啊?难道其他模块的混淆规则影响了?又或者Dex分包?会不会是AAR嵌套的原因?等等。

先看下是不是 我们项目问题吧,于是尝试写一个Demo来引用这个库,Copy混淆规则,并打Release包,结果发现Demo 能正常使用!!!焯,那就是我们项目有问题了,难道因为不知名原因,导致类丢失?于是我用AS打开两个Release的APK (公司APP和Demo) 进行比对:

方法数啥的完全一样,难道 真不是混淆的问题?那还可能是什么原因呢?一时有些找不着方向,同事放弃了,领导甚至在群里提出改方案的建议了 (公司App只负责上传图片):


② 配置文件缺失/错误排查

而我还在 坚持,回归现象本身:

  • 连接相机报错 HZJsonResponse: Parse json payload failed
  • 等待很久 后回调onNeedInitCamera(),然后跳转相机初始化页面。
  • 点击后打印警告日志:Try to setup processor with invalid version,无法正常初始化。

对方的回复

  • 简单点说就是: 没连上相机,就调用初始化接口
  • 但明显和我们的代码逻辑相违背:只有连上了,才会进入初始化页面

这时,我突发奇想,手机连接相机耗时,会不会是接收相机发过来的某些文件出错导致的?比对下是否有文件缺失不就好了?于是我打开Demo和公司APP的 data/data/包名 目录进行比对,发现 shared_prefs 目录下没有这个文件:

copy一下,然后又看了下files目录,看到有个easy_panorama目录,里面明显是相机相关的东西,对比了下公司APP,有部分文件缺失:

同样copy覆盖一下,然后再次运行公司APP连相机,发现报错:没有文件操作权限,直接修改所有者和用户组为我司APP:

再次连相机,可以,不用初始化直接进入了拍摄页面,点击拍照,相机开始拍照,卧槽?难道真的是文件损坏或缺失引起的BUG吗?如果是的话可以往这个方面继续排查了,但事实打脸,拼接图片时卡住,然后提示照片下载失败,依旧是这个异常:

HZJsonResponse: Parse json payload failed。

又是空欢喜一场,唉,累了,毁灭了,就这样吧,改方案吧,什么垃圾SDK...


③ Xposed Hook调试AAR

度过了慵懒的周末,又到周一

一到公司,看到桌面的相机就烦,!破玩意,浪费我两个星期时间...

表面上 恨不得立马丢了,实际上 又看起了源码,嘴上说着不要,身体却很诚实。

尝试定位到报错位置 (jd-gui反编译class.jar搜字符串常量):

报JSONException异常,才会走这里的代码,而这个异常的引发条件:

大概率是Json的问题,比如格式不正确,而这个方法是将byte数组转换成Json字符串:

如果可以把入参byte数组搞出来看看就好了,尝试在此下 断点,然后调试附加到 APP进程 (手机Root了,并通过Magisk将APP都弄成Debug模式,所以能Debug Release APP)。

但是,并没有走进来,可能不能这样玩?得想下其他法子把参数打印出来。想到好久没玩的Xposed,立马打开专栏:Xposed从入门到入土「插件开发记录 x 框架源码解析」 温故知新。到Github找到前几年写的玩具: CPWechatXposed,clone后Build一下。新建一个Hook类:

XposedInit.kt 加上它:

运行模块后重启设备,然后连相机,日志断断续续打印出来了:

连接相机时,会报这个码,错误码么,文档上却没找着,再次发文:

噢,并不是错误码,后续Json数据也陆续打印出来了,复制到Json格式化工具,是正常的Json,这...

不过 细心的我 发现了,我在Xposed里 try-catch 这个JsonObject实例化的过程,并不会触发JsonException

但是 Parse json playload failed. 还是打印出来了,说明是触发 JsonException???

好家伙,难不成你们家的 JsonObjectJsonException 是金做的还是银做的?

随手点进 JsonObject 的一定位,直接 真相大白

JsonObjectJsonException 指向不同的库!!!

卧槽,这谁埋的雷啊,虽然没定位到具体哪里抛异常,但绝壁是这个库的原因,问了下虾哥能否强制指定,被告知不行:

后面看了一下引入这个库,只是为了调用下xml转json的方法:

implementation files('jars/java-json.jar') 干掉,引入另一个xml转json的库:implementation 'com.github.smart-fun:XmlToJson:1.4.5',修改调用处相关代码,编译打Release包,安装运行,连接相机,拍照拼接,一气呵成,完美解决。就这样的小BUG磨了我两周,好一个前人挖坑,后人填坑啊...


0x4、小结

事情的大概经过就这样,BUG说解了但又没有完全解,比如没有具体定位到哪里抛JsonException,也有一些让步,比如小米机型还能偶现奔溃问题,但这在我的能力边界外。可惜新相机寄回去了,不然我肯定趁机扩展这方面的姿势~ 能用就行,在这个过程中也意识到自己的一些不足:

  • 在与人对接描述问题时,并不能 简要描述清楚自己的诉求,导致沟通效率并不高;
  • 绝大部分时间都花在 打包验证 上了,每次一打就十来分钟,一直硬钢,没有想办法去提高编译效率;
  • 容易 钻牛角尖,明明自己认知里知道这样做是没问题的,还要去硬怼,而不是尝试 换个方向
  • Native Crash 的排查能力较弱,so库逆向调试 也不会,有时间有机会可以了解下~
  • 没有一个可以 随时快速验证问题的项目,比如上面写Demo验证,我还得去新建项目,折腾kt版本等;
  • 与之形成鲜明对比的就是我的 Xposed插件玩具,改改就能用,当然如果能加上 注解 简化调用就更棒了;

综上,还是自己太 Vegetable 了,得多多磨练和思考啊~