从 Android 9(API 级别 28)开始,平台会限制您的应用可以使用哪些非 SDK 接口。每当应用引用非 SDK 接口或尝试使用反射或 JNI 获取其句柄时,这些限制都适用。实施这些限制是为了帮助改善用户和开发者的体验,并降低用户崩溃和开发者紧急回滚的风险。有关此决定的更多信息,请参阅 通过减少非 SDK 接口的使用来提高稳定性。
区分 SDK 和非 SDK 接口
一般来说,公共 SDK 接口是在 Android 框架 包索引 中记录的那些接口。非 SDK 接口的处理是 API 抽象掉的一个实现细节,因此这些接口可能会在未经通知的情况下发生更改。
为避免崩溃和意外行为,应用应仅使用 SDK 中正式记录的类部分。这也意味着,当您使用反射等机制与类交互时,不应访问 SDK 中未列出的方法或字段。
非 SDK API 列表
随着每个 Android 版本的发布,都会限制更多非 SDK 接口。我们知道这些限制可能会影响您的发布工作流程,我们希望确保您拥有检测非 SDK 接口使用的工具、向我们提供反馈 的机会以及规划和调整新策略的时间。
为了最大限度地减少非 SDK 限制对开发工作流程的影响,非 SDK 接口被分成多个列表,这些列表根据目标 API 级别定义了其使用的严格程度。下表描述了每个列表。
列表 | 代码标签 | 描述 |
---|---|---|
黑名单 |
|
无论应用的 目标 API 级别 如何,您都不能使用的非 SDK 接口。如果您的应用尝试访问这些接口之一,系统将 抛出错误。 |
有条件地阻止 |
|
从 Android 9(API 级别 28)开始,每个 API 级别都有在应用以该 API 级别为目标时受限的非 SDK 接口。 这些列表按应用可以在无法再访问该列表中的非 SDK 接口之前可以定位到的最大 API 级别( 如果您的应用尝试访问对您的目标 API 级别受限的接口,系统将 将其行为视为该 API 是黑名单的一部分。 |
不受支持 |
|
不受限制且您的应用可以使用的非 SDK 接口。但是,请注意,这些接口不受支持,并且可能会在未经通知的情况下发生更改。预计这些接口将在将来的 Android 版本中在 max-target-x 列表中被有条件地阻止。 |
SDK |
|
可以自由使用并现在作为正式记录的 Android 框架 包索引 的一部分支持的接口。 |
测试 API |
|
用于内部系统测试的接口,例如通过兼容性测试套件 (CTS) 促进测试的 API。测试 API **不**属于 SDK 的一部分。从 Android 11(API 级别 30)开始,测试 API 已包含在黑名单中,因此无论其目标 API 级别如何,应用都不允许使用它们。所有测试 API 都不受支持,并且可能会在不另行通知的情况下更改,无论平台 API 级别如何。 |
虽然您可以使用一些非 SDK 接口(取决于您的应用的目标 API 级别),但使用任何非 SDK 方法或字段始终存在很高的应用程序崩溃风险。如果您的应用依赖于非 SDK 接口,则应开始计划迁移到 SDK 接口或其他替代方案。如果您找不到替代方案来使用应用中某个功能的非 SDK 接口,则应请求新的公共 API。
确定接口属于哪个列表
非 SDK 接口列表是作为平台的一部分构建的。有关每个 Android 版本的信息,请参阅以下部分。
Android 15
对于 Android 15(API 级别 35),您可以下载以下文件,该文件描述了所有非 SDK 接口及其对应的列表
SHA-256 校验和:40134e205e58922a708c453726b279a296e6a1f34a988abd90cec0f3432ea5a9
要了解有关 Android 15 中非 SDK API 列表更改的更多信息,请参阅Android 15 中非 SDK 接口限制的更新。
Android 14
对于 Android 14(API 级别 34),您可以下载以下文件,该文件描述了所有非 SDK 接口及其对应的列表
SHA-256 校验和:7e00db074cbe51c51ff4b411f7b48e98692951395c5c17d069c822cc1d0eae0f
要了解有关 Android 14 中非 SDK API 列表更改的更多信息,请参阅Android 14 中非 SDK 接口限制的更新。
Android 13
对于 Android 13(API 级别 33),您可以下载以下文件,该文件描述了所有非 SDK 接口及其对应的列表
SHA-256 校验和:233a277aa8ac475b6df61bffd95665d86aac6eb2ad187b90bf42a98f5f2a11a3
要了解有关 Android 13 中非 SDK API 列表更改的更多信息,包括 Android 13 中有条件阻止的 API 的建议公共 API 替代方案,请参阅Android 13 中非 SDK 接口限制的更新。
Android 12
对于 Android 12(API 级别 31),您可以下载以下文件,该文件描述了所有非 SDK 接口及其对应的列表
SHA-256 校验和:40674ff4291eb268f86561bf687e69dbd013df9ec9531a460404532a4ac9a761
要了解有关 Android 12 中非 SDK API 列表更改的更多信息,包括 Android 12 中有条件阻止的 API 的建议公共 API 替代方案,请参阅Android 12 的列表更改。
Android 11
对于 Android 11(API 级别 30),您可以下载以下文件,该文件描述了所有非 SDK 接口及其对应的列表
SHA-256 校验和:a19d839f4f61dc9c94960ae977b2e0f3eb30f880ba1ffe5108e790010b477a56
要了解有关 Android 11 中非 SDK API 列表更改的更多信息,包括 Android 11 中有条件阻止的 API 的建议公共 API 替代方案,请参阅Android 11 的列表更改。
Android 10
对于 Android 10(API 级别 29),您可以下载以下文件,该文件描述了所有非 SDK 接口及其对应的列表
SHA-256 校验和:f22a59c215e752777a114bd9b07b0b6b4aedfc8e49e6efca0f99681771c5bfeb
要了解有关 Android 10 中非 SDK API 列表更改的更多信息,包括 Android 10 中有条件阻止的 API 的建议公共 API 替代方案,请参阅Android 10 的列表更改。
Android 9
对于 Android 9(API 级别 28),以下文本文件包含不受限制(灰名单)的非 SDK API 列表:hiddenapi-light-greylist.txt
。
黑名单(blacklist
)和有条件阻止的 API 列表(深灰名单)是在构建时生成的。
从 AOSP 生成列表
在使用 AOSP 时,您可以生成一个hiddenapi-flags.csv
文件,其中包含所有非 SDK 接口及其对应的列表。为此,下载 AOSP 源代码,然后运行以下命令
m out/soong/hiddenapi/hiddenapi-flags.csv
然后,您可以在以下位置找到该文件
out/soong/hiddenapi/hiddenapi-flags.csv
访问受限的非 SDK 接口时的预期行为
下表描述了如果您的应用尝试访问属于黑名单的非 SDK 接口,您可以预期的行为。
访问方式 | 结果 |
---|---|
引用字段的 Dalvik 指令 | NoSuchFieldError 被抛出 |
引用方法的 Dalvik 指令 | NoSuchMethodError 被抛出 |
使用 Class.getDeclaredField() 或 Class.getField() 的反射 |
NoSuchFieldException 被抛出 |
使用 Class.getDeclaredMethod() 、Class.getMethod() 的反射 |
NoSuchMethodException 被抛出 |
使用 Class.getDeclaredFields() 、Class.getFields() 的反射 |
结果中没有非 SDK 成员 |
使用 Class.getDeclaredMethods() 、Class.getMethods() 的反射 |
结果中没有非 SDK 成员 |
使用 env->GetFieldID() 的 JNI |
返回 NULL ,抛出 NoSuchFieldError |
使用 env->GetMethodID() 的 JNI |
返回 NULL ,抛出 NoSuchMethodError |
测试您的应用中是否存在非 SDK 接口
您可以使用多种方法来测试应用中是否存在非 SDK 接口。
使用可调试应用进行测试
您可以通过在运行 Android 9(API 级别 28)或更高版本的设备或模拟器上构建和运行可调试应用来测试非 SDK 接口。确保您使用的设备或模拟器与应用的目标 API 级别匹配。
在运行应用测试时,如果您的应用访问某些非 SDK 接口,系统会打印日志消息。您可以检查应用的日志消息以查找以下详细信息:
- 声明类、名称和类型(使用 Android 运行时使用的格式)。
- 访问方式:链接、使用反射或使用 JNI。
- 非 SDK 接口所属的列表。
您可以使用 adb logcat
访问这些日志消息,这些消息显示在正在运行的应用的 PID 下。例如,日志中的条目可能如下所示:
Accessing hidden field Landroid/os/Message;->flags:I (light greylist, JNI)
使用 StrictMode API 进行测试
您还可以使用 StrictMode
API 测试非 SDK 接口。使用detectNonSdkApiUsage
方法启用此功能。启用 StrictMode
API 后,您可以通过使用penaltyListener
(您可以在其中实现自定义处理)为每次使用非 SDK 接口接收回调。Violation
对象在回调中提供,它派生自 Throwable
,并且封闭的堆栈跟踪提供了用法的上下文。
使用 veridex 工具进行测试
您还可以对 APK 运行 veridex 静态分析工具。veridex 工具会扫描 APK 的整个代码库,包括任何第三方库,并报告它找到的任何非 SDK 接口的使用情况。
veridex 工具的局限性包括:
- 它无法检测通过 JNI 的调用。
- 它只能检测通过反射进行的部分调用。
- 它对非活动代码路径的分析仅限于 API 级别检查。
- 它只能在支持 SSE4.2 和 POPCNT 指令的机器上运行。
Windows
不提供本机 Windows 二进制文件,但您可以使用 Windows 子系统 Linux (WSL) 执行 Linux 二进制文件来在 Windows 上运行 veridex 工具。在按照本节中的步骤操作之前,安装 WSL 并选择 Ubuntu 作为您的 Linux 发行版。
安装 Ubuntu 后,启动 Ubuntu 终端,然后按照以下步骤操作:
- 从 Android 运行时预构建库下载veridex 工具。
- 解压缩
appcompat.tar.gz
文件的内容。 - 在解压缩的文件夹中,找到
veridex-linux.zip
文件并解压缩它。 导航到解压缩的文件夹,然后运行以下命令,其中
your-app.apk
是您要测试的 APK./appcompat.sh --dex-file=your-app.apk
macOS
要在 macOS 上运行 veridex 工具,请按照以下步骤操作:
- 从 Android 运行时预构建库下载veridex 工具。
- 解压缩
appcompat.tar.gz
文件的内容。 - 在解压缩的文件夹中,找到
veridex-mac.zip
文件并解压缩它。 导航到解压缩的文件夹,然后运行以下命令,其中
/path-from-root/your-app.apk
是您要测试的 APK 的路径,从系统的根目录开始。./appcompat.sh --dex-file=/path-from-root/your-app.apk
Linux
要在 Linux 上运行 veridex 工具,请按照以下步骤操作:
- 从 Android 运行时预构建库下载veridex 工具。
- 解压缩
appcompat.tar.gz
文件的内容。 - 在解压缩的文件夹中,找到
veridex-linux.zip
文件并解压缩它。 导航到解压缩的文件夹,然后运行以下命令,其中
your-app.apk
是您要测试的 APK./appcompat.sh --dex-file=your-app.apk
使用 Android Studio lint 工具进行测试
每次在 Android Studio 中构建应用时,lint 工具都会检查您的代码是否存在潜在问题。如果您的应用使用非 SDK 接口,则可能会看到构建错误或警告,具体取决于这些接口所属的列表。
您也可以从命令行运行 lint 工具或手动运行检查特定项目、文件夹或文件。
使用 Play Console 进行测试
当您将应用上传到 Play Console 中的测试轨道时,您的应用会自动测试潜在问题,并生成预发布报告。如果您的应用使用非 SDK 接口,则预发布报告中会显示错误或警告,具体取决于这些接口属于哪个列表。
有关更多信息,请参阅使用预发布报告识别问题中的 Android 兼容性部分。
申请新的公共 API
如果您找不到替代方案来为应用中的功能使用非 SDK 接口,您可以通过创建功能请求在我们的问题跟踪器中申请新的公共 API。
创建功能请求时,请提供以下信息:
- 您正在使用的哪些不受支持的 API,包括在
Accessing hidden ...
logcat 消息中看到的完整描述符。 - 您为什么需要使用这些 API,包括有关 API 所必需的高级功能的详细信息,而不仅仅是低级细节。
- 为什么任何相关的公共 SDK API 不足以满足您的需求。
- 您尝试过的任何其他替代方案以及为什么这些方案不起作用。
当您在功能请求中提供这些详细信息时,您将增加获得新的公共 API 的可能性。
其他问题
本节包含一些开发者经常提出的问题的答案。
常见问题
Google 如何确保可以通过问题跟踪器捕获所有应用的需求?
我们通过对应用进行静态分析创建了 Android 9(API 级别 28)的初始列表,并使用以下方法进行了补充:
- 对顶级 Play 应用和非 Play 应用进行手动测试
- 内部报告
- 从内部用户自动收集数据
- 开发者预览报告
- 旨在保守地包含更多误报的附加静态分析
在评估每个新版本的列表时,我们会考虑 API 使用情况以及通过问题跟踪器提供的开发者反馈。
如何启用对非 SDK 接口的访问?
您可以通过使用 adb 命令更改 API 强制执行策略来在开发设备上启用对非 SDK 接口的访问。您使用的命令因 API 级别而异。这些命令不需要 root 设备。
- Android 10(API 级别 29)或更高版本
要启用访问,请使用以下 adb
命令
adb shell settings put global hidden_api_policy 1
要将 API 强制执行策略重置为默认设置,请使用以下命令
adb shell settings delete global hidden_api_policy
- Android 9(API 级别 28)
要启用访问,请使用以下 adb 命令
adb shell settings put global hidden_api_policy_pre_p_apps 1
adb shell settings put global hidden_api_policy_p_apps 1
要将 API 强制执行策略重置为默认设置,请使用以下命令
adb shell settings delete global hidden_api_policy_pre_p_apps
adb shell settings delete global hidden_api_policy_p_apps
您可以将 API 强制执行策略中的整数设置为以下值之一:
- 0:禁用所有非 SDK 接口的检测。使用此设置将禁用所有非 SDK 接口使用情况的日志消息,并阻止您使用
StrictMode
API 测试您的应用。此设置不推荐使用。 - 1:启用对所有非 SDK 接口的访问,但打印带有警告的日志消息以指示任何非 SDK 接口的使用情况。使用此设置还可以让您使用
StrictMode
API 测试您的应用。 - 2:不允许使用属于黑名单的非 SDK 接口,或者对您的目标 API 级别而言被有条件地阻止的非 SDK 接口。
关于非 SDK 接口列表的问题
在哪里可以找到系统映像中的非 SDK API 列表?
它们编码在平台 dex 文件中的字段和方法访问标志位中。系统映像中没有包含这些列表的单独文件。
在具有相同 Android 版本的不同 OEM 设备上,非 SDK API 列表是否相同?
OEM 可以将其自己的接口添加到黑名单中,但它们不能从 AOSP 非 SDK API 列表中删除接口。CDD 阻止此类更改,CTS 测试确保 Android 运行时正在强制执行该列表。
关于相关应用兼容性的问题
本机代码中对非 NDK 接口是否有任何限制?
Android SDK 包含 Java 接口。该平台从 Android 7(API 级别 26)开始限制对本机 C/C++ 代码的非 NDK 接口的访问。有关更多信息,请参阅Android N 中通过私有 C/C++ 符号限制提高稳定性。
是否有计划限制 dex2oat 或 DEX 文件操作?
我们目前没有计划限制对 dex2oat 二进制文件的访问,但我们并不打算使 DEX 文件格式保持稳定,也不打算将其作为公共接口(超出Dalvik 可执行文件格式中公开指定的那些部分)。我们保留随时修改或删除 dex2oat 和 DEX 格式的未指定部分的权利。另请注意,由 dex2oat 生成的派生文件(例如 ODEX(也称为 OAT)、VDEX 和 CDEX)都是未指定的格式。
如果关键的第三方 SDK(例如,混淆器)无法避免使用非 SDK 接口,但确实承诺保持与未来 Android 版本的兼容性,Android 是否可以放弃其兼容性要求?
我们目前没有计划按每个 SDK 的方式放弃兼容性要求。如果 SDK 开发者只能通过依赖于不受支持(以前为灰色)列表中的接口来保持兼容性,他们应该开始计划迁移到 SDK 接口或其他替代方案,并在找不到使用非 SDK 接口的替代方案时申请新的公共 API。
非 SDK 接口限制是否适用于所有应用,包括系统应用和第一方应用,而不仅仅是第三方应用?
是的,但是,我们豁免了使用平台密钥签名的应用和一些系统映像应用。请注意,这些豁免仅适用于作为系统映像一部分的应用(或更新的系统映像应用)。此列表仅适用于针对私有平台 API(而不是 SDK API,其中LOCAL_PRIVATE_PLATFORM_APIS := true
)构建的应用。