在我们的项目中其实有一个问题,断断续续困扰着整个项目组,也一直有用户反馈,而当我们排查时也没有什么头绪,最后发现了一个很离奇的情况,在使用 MediaStore 查询本地文件时,设置排序条件的写法直接影响查询结果——不混淆打包时一切正常,但在混淆打包后出现两种问题:
在代码中这样设置排序条件:
// 动态拼接字符串 var sortOrder = MediaStore.Files.FileColumns.DATE_MODIFIED sortOrder += " DESC"
这种写法在开发过程中(不进行混淆打包时)没有问题,但在混淆打包后可能会出现以下问题:
而当我们改为使用字符串模板直接生成排序条件时:
// 使用字符串模板生成编译期常量
val sortOrder = "${MediaStore.Files.FileColumns.DATE_MODIFIED} DESC"
问题便完全消失
编译期内联的优势
使用字符串模板(如上例所示)可以在编译期间将排序字符串生成一个不可变的常量(例如
"date_modified DESC"
)。这样生成的常量不仅格式固定,而且会在编译时被内联到调用处,确保在混淆过程中不会被意外修改。
动态拼接的潜在风险
而使用变量先声明,再通过“+=”进行字符串拼接的方式,是在运行时动态生成最终字符串。这种做法会让排序字符串的生成过程多出一个中间状态,可能会受到隐式类型转换或空格等细微问题的影响。更重要的是,在混淆打包过程中,ProGuard 或 R8 可能不会对运行时生成的字符串做同样的常量折叠和内联优化,从而导致最终传给 MediaStore 查询的排序字符串格式不符合预期。
在混淆打包过程中,混淆工具(如 ProGuard 或 R8)会对代码进行重命名、优化以及内联操作。
问题发现
开发过程中发现使用动态拼接的排序字符串在非混淆环境下工作正常,但在混淆打包后出现报错或查询结果不全的问题。
问题排查
经过仔细调试和对比,确认问题与排序字符串的生成方式有关。
原因定位
进一步分析发现,混淆工具在处理运行时动态拼接的字符串时,可能未能将其优化成编译期常量,导致排序条件格式出现问题,而编译期常量则不会受到这种影响。
问题解决
最终,通过改为使用字符串模板生成排序字符串,问题得以解决。这也提醒我们,在关键字符串的生成上,尽量在编译期内完成生成,从而避免混淆打包过程中的不可预期行为。
在 Android 开发中,混淆打包是一种常见的代码保护措施,但同时也可能引发一些意外问题。
本次案例中,MediaStore 查询排序字符串由于生成方式的不同而在混淆后产生错误。
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/69917874/viewspace-3078077/ ,如需转载,请注明出处,否则将追究法律责任。