Class 分析插件助力 Android 13 适配落地

2022-11-30 14:59:33 浏览数 (1)

距离 Android 13 发布马上有 3 个月了,各厂都已经开始了适配工作,我司也不例外。但适配前有一个问题需要解决,如果衡量这次适配的工作量,如何统计哪些模块需要改造,并能精确的找到对应模块负责人。

传统方式也有,通过 as 的 Find Usages 功能,找到该方法有被哪些类引用。例如 Android13 适配中,应用动态注册的广播接收器必须以显著的方式指出是否允许其他应用访问,所以,我们必须找到是哪些模块在调用 registerReceiver 方法,我们需要针对这些方法进行适配,如下是通过 Find Usages 检查的结果:

然后再手动统计下是哪些模块在调用,并记录起来,然后继续下个适配的检测,最终评估出改造的工作量。显然,传统方式也不是不能做,只是有点费劲而已。

说来也怪,Google 致力推行每个版本的发布,却没有提供一个工具来辅助开发者去实现自动化检查,也是够糟糕的。

其实想想也不难,我们的诉求不就是想找到需要适配的方法是在哪个模块被哪个类引用了嘛,我们可以来思考下,如果要实现自动化的话,我们需要做哪些事情:

  1. 准备好一个配置文件,描述我们需要检测的适配方法
  2. 找到应用的所有依赖,并提取出依赖中的 class.jar
  3. 解析 class.jar,提取出 class 文件并对其进行分析
  4. 分析 class 文件中有无调用到配置文件中的适配方法
  5. 如果有,则记录这个依赖,并记录这个 class 文件
  6. 最终,将收集到的结果输出成一个文件

比如,我们想适配 Android 13 如下几个 case:

  • 后台的传感器权限适配:App 在后台运行时,如果需要获取心率、体温、血氧饱和度等传感器信息,对于申明了 BODY_SENSORS 权限还必须申明新的 BODY_SENSORS_BACKGROUND 权限。
  • 更安全的系统组件适配:应用动态调用 registerReceiver 注册广播接收器必须以显著的方式指出是否允许其他应用访问,即其他应用是否可以向其发送广播。否则,在动态注册时系统将抛出安全异常。
  • 受限的 non-SDK 接口:不允许调用 ActivityThread.mCurDefaultDisplayDpi 字段

那么,我们的配置文件可以这么去描述:

代码语言:javascript复制
{
  "stringRef": [
    "android.permission.BODY_SENSORS"
  ], 
  "methodRef": [
    {
      "className": "android/content/Context",
      "method": "registerReceiver"
    }
  ],
 "fieldRef": [
    {
      "className": "android/app/ActivityThread",
      "fieldName": "mCurDefaultDisplayDpi",
      "signature": "I"
    }
  ],
}
  • stringRef : 描述字符常量的调用的情况。例如,我们需要检查所有依赖中,有哪些模块在调用 android.permission.BODY_SENSORS 权限
  • fieldRef : 描述字段的调用情况。例如,我们需要检查所有依赖中,有哪些模块在调用 ActivityThread.mCurDefaultDisplayDpi 字段
  • methodRef : 描述方法的调用情况。例如,我们需要检查所有依赖中,有哪些模块在调用 Context.registerReceiver 方法

最终的输出结果文件如下:

代码语言:javascript复制
{
  "stringRef": [
    {
      "name": "android.permission.BODY_SENSORS",
      "ref": [
        {
          "dependencies": "u0027:android-lib2:debugApiElementsu0027",
          "className": "com/codelang/android_lib2/TestCase"
        }
      ]
    }]
    }
  ],
  "methodRef": [
    {
      "className": "android/content/Context",
      "method": "registerReceiver",
      "ref": [
        {
          "dependencies": "u0027:android-lib2:debugApiElementsu0027",
          "className": "com/codelang/android_lib2/TestCase"
        },
        {
          "dependencies": "androidx.appcompat:appcompat:1.4.2",
          "className": "androidx/appcompat/app/AppCompatDelegateImpl$AutoNightModeManager"
        }
      ]
    }
  ],
  "fieldRef": [
    {
      "className": "android/app/ActivityThread",
      "fieldName": "mCurDefaultDisplayDpi",
      "signature": "I",
      "ref": [
        {
          "dependencies": "u0027:android-lib2:debugApiElementsu0027",
          "className": "com/codelang/android_lib2/TestCase"
        }
      ]
    }
  ]
}
  • stringRef 中分析出 android-lib2 模块中的 TestCase 类调用了 BODY_SENSORS 权限
  • methodRef 中分析出 android-lib2 模块中的 TestCase 类调用了 Context.registerReceiver 方法
  • fieldRef 中分析出 android-lib2 模块中的 TestCase 类调用了 ActivityThread.mCurDefaultDisplayDpi 字段

最终的接入与使用情况,可以参考 Github 的 README.md[1],目前插件已开源,项目源码可查看 https://github.com/MRwangqi/pluginDemo/blob/master/plugin/classAnalysis[2]

参考资料

[1]

README.md: https://github.com/MRwangqi/pluginDemo/blob/master/plugin/classAnalysis/README.md

[2]

https://github.com/MRwangqi/pluginDemo/blob/master/plugin/classAnalysis: https://github.com/MRwangqi/pluginDemo/blob/master/plugin/classAnalysis

0 人点赞