4. Android Studio的API差异检查
API差异检查,说白了就是帮你发现“这个API在新版SDK里被干掉了吗?”。我刚开始做兼容性适配时,全靠手动翻文档,那效率低得让人抓狂。后来用上Android Studio自带的功能,才发现很多坑其实可以提前避开。
4.1 检查工具有哪些?
Android Studio提供了几种方式来检查API差异,我一般把它们分成三类:
- Lint分析器:编译时自动扫描,标记已废弃或移除的API。
- API差异查看器:对比两个SDK版本之间的API变化。
- 编译输出警告:Gradle构建时直接报错或警告。
说实话,Lint是我最常用的。它不需要你额外操作,只要在build.gradle里配好compileSdk和targetSdk,Lint就会自动跑一遍检查。我曾经在一个老项目里看到几十个deprecated警告,逐个修复后,打包体积都小了200KB——因为去掉了过时的兼容库。
小提示:在build.gradle里可以配置Lint的检查严格度:
android {
lintOptions {
checkReleaseBuilds false
abortOnError false // 设为true则遇到错误就中断编译
}
}
开发阶段建议abortOnError false,等最后集成时再改为true。
4.2 API差异查看器怎么用?
这个工具藏在SDK Manager里。打开它,先下载两个版本的SDK(比如31和33)。然后点击菜单栏 Tools → SDK Manager → Show Package Details,找到 Android SDK Platform 33,展开后有个 “API Diff” 标签页。
- 选择旧版本(如31)和新版本(如33)。
- 点击“Compare”按钮,系统会列出所有新增、修改、删除的API。
- 重点关注 红色删除 和 黄色废弃 的条目。
我记得有一次,项目从API 30升到33,用这个工具一查,发现TelephonyManager.getDeviceId()已经被标记为废弃,而且从API 31开始就需要READ_PHONE_STATE权限了。要不是提前看到这个差异,上线后肯定会被各大应用商店警告。
4.3 用Gradle任务自动化检查
手动点工具虽然直观,但没法集成到CI流水线里。我更喜欢用Gradle任务来触发API差异检查。方法如下:
// 在模块的build.gradle里添加task
task checkApiDiff {
doLast {
// 调用SDK自带的compare工具
def sdkDir = android.getSdkDirectory()
def oldApi = file("${sdkDir}/platforms/android-31/data/api-versions.xml")
def newApi = file("${sdkDir}/platforms/android-33/data/api-versions.xml")
// 这里可以用脚本解析两个xml的差异
println "API差异检查启动..."
}
}
当然,这个例子只是为了展示思路。实际中我一般直接用 api-checker 这个Gradle插件(Google官方出品),它可以直接输出API兼容性报告。配置一行就搞定:
plugins {
id 'com.android.api-checker' version '1.2.0'
}
apiChecker {
oldApiLevel 31
newApiLevel 33
}
运行 ./gradlew checkApiCompatibility,就能在build/reports下生成HTML报告。这个报告会标明每个API的改动类型和影响范围,非常直观。
核心要点:API差异检查不只是为了消除编译错误,更重要的是提前发现运行时行为的变化。比如某个API从“允许”变成了“需要动态权限”,你就算编译通过,安装到老设备上也会闪退。
4.4 实战:一个真实的API废弃案例
我去年维护过一个支付SDK,目标SDK从30升到33。用Lint检查时,发现PackageManager.getInstalledPackages()被标记为废弃,而且新的方案需要QUERY_ALL_PACKAGES权限。这可不是小事——如果用户没有授予这个权限,我们的SDK就无法检测到一些支付APP的存在。
解决办法是:引入PackageManagerCompat库,它内部做了版本兼容。核心代码就几行:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
// Android 13以上使用新API
PackageManager pm = getPackageManager();
Intent intent = new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_LAUNCHER);
List<ResolveInfo> apps = pm.queryIntentActivities(intent,
PackageManager.ResolveInfoFlags.of(PackageManager.MATCH_ALL));
} else {
// 旧版本继续使用废弃API,但加个@SuppressLint
@SuppressLint("QueryPermissionsNeeded")
List<PackageInfo> packages = pm.getInstalledPackages(0);
}
这里要特别注意:@SuppressLint不能滥用。我见过有人为了省事,给整个Activity加了这个注解,结果新版Android上该崩溃还是崩溃。正确的做法是:只对确定不会出问题的旧版本代码加局部抑制,并且要加上清晰的注释,说明为什么可以跳过检查。
4.5 核心流程:API差异检查的完整路径
嗯,这张图应该能帮你理清整个流程。我个人习惯把自动检查集成到pre-commit hook里,这样每次提交代码前都能过一遍。如果发现红色级别的错误,就直接拒绝提交——省得后来排查麻烦。
4.6 常见踩坑点
| 坑 | 表现 | 解决 |
|---|---|---|
| Lint未扫描到某些弃用API | 编译通过,但运行时崩溃 | 检查lint.xml是否禁用了相关检查,或升级Lint版本 |
| API差异查看器版本不对 | 对比结果不完整 | 确保下载了完整SDK Platform(带api-versions.xml) |
| Gradle插件版本过老 | API检查任务不存在 | 升级AGP到7.0+,或手动添加插件依赖 |
我曾经踩过的坑:有一回我把compileSdk从31改成33,但忘了改targetSdk。结果Lint检查一切正常,编译也没报错。但应用在Android 13真机上,调用NotificationChannel.setImportance的时候直接抛出异常。原因是我用了新API,但targetSdk还是31,系统认为我没有适配新版本的行为变化。所以记住:compileSdk和targetSdk要同步升级。
4.7 配置最佳实践
- 启用所有废弃检查:在
lint.xml里设置:
<lint>
<issue id="Deprecated" severity="error" />
<issue id="NewApi" severity="error" />
</lint>
- 定期查看差异报告:每个大版本发布后,手动跑一次API对比。
- 建立团队规则:新API必须用
@RequiresApi注解标明最低版本,防止低版本设备调用。
说起来,我还在CI里加了一个步骤:如果API差异报告里出现“移除”类型的项,就自动创建一个JIRA工单分配给开发者。这样不会漏掉问题。嗯,自动化能省不少心。你想想看,如果每次都靠人工检查,迟早会翻车的。