> 技术文档 > 如何查看项目是否支持最新 Android 16K Page Size 一文汇总

如何查看项目是否支持最新 Android 16K Page Size 一文汇总

前几天刚聊过 《Google 开始正式强制 Android 适配 16 K Page Size》 之后,被问到最多的问题是「怎么查看项目是否支持 16K Page Size」 ?其实有很多直接的方式,但是最难的是当你的项目有很多依赖时,怎么知道这个「不支持的动态库 so」 文件是哪个依赖?有不少人的项目里可能有几十个 so ,如果一个一个那场景可太\"有爱\"了。

后面的脚本提供查找思路。

首先第一种方法用官方的脚本,保存下方脚本为 shell.sh ,然后执行 ./shell.sh src/main/jinLibs ,就可以检测到目录下所有动态库是否支持 16K:

#!/bin/bash# usage: alignment.sh path to search for *.so filesdir=\"$1\"RED=\"\\e[31m\"GREEN=\"\\e[32m\"ENDCOLOR=\"\\e[0m\"matches=\"$(find $dir -name \"*.so\" -type f)\"IFS=$\'\\n\'for match in $matches; do res=\"$(objdump -p ${match} | grep LOAD | awk \'{ print $NF }\' | head -1)\" if [[ $res =~ \"2**14\" ]] || [[ $res =~ \"2**16\" ]]; then echo -e \"${match}: ${GREEN}ALIGNED${ENDCOLOR} ($res)\" else echo -e \"${match}: ${RED}UNALIGNED${ENDCOLOR} ($res)\" fidone

如何查看项目是否支持最新 Android 16K Page Size 一文汇总

整个 Apk 的话,可以直接解压 Apk ,然后对动态库的目录用脚本扫描。

第二种方法就是通过 Google Play 的 app bundle 资源管理器页面直接查看,如果有问题,会看到类似的情况:

如何查看项目是否支持最新 Android 16K Page Size 一文汇总

另外,如果 so 没问题,但是还是提示你不支持 16 KB,那么很可能是你需要升级 AGP ,建议至少升级到 AGP 8+ ,最优是升级到 8.5.1 之后:

如何查看项目是否支持最新 Android 16K Page Size 一文汇总

没有问题的情况下是这样:

如何查看项目是否支持最新 Android 16K Page Size 一文汇总

第三种方法就是下载最新的 libchecker ,如果动态库都有“16 KB” ,那就是正常:

如何查看项目是否支持最新 Android 16K Page Size 一文汇总

还有一种方法就是使用 readelf 工具,通过终端对比 so 的 elf 对齐情况,工具一般位于 /Users/guoshuyu/Library/Android/sdk/ndk/21.4.7075529/toolchains/aarch64-linux-android-4.9/prebuilt/darwin-x86_64/bin,通过以下命令可以输出对应参数:

./aarch64-linux-android-readelf -l /Users/guoshuyu/workspace/android/******/libs/arm64-v8a/libijkffmpeg.so

如下两种图所示:

  • 图 1 LOAD 段在 Align 栏目显示 1000 (16进制,即 4096) ,也就是还没增加 16K 对齐的状态
  • 图 2 LOAD 段在 Align 栏目显示 4000 (16进制,即 16384) ,是增加 16K 对齐的状态

如何查看项目是否支持最新 Android 16K Page Size 一文汇总如何查看项目是否支持最新 Android 16K Page Size 一文汇总

注意,是 1000 才是 4K,而 10000 是 65536 ,那就是64K 对齐,属于 16K 的 4倍,那「理论上」应该是对齐的

最后,你还可以在 Android Studio 里运行你的 App,然后 Android Studio 会提示存在哪些动态库没适配 16 KB

如何查看项目是否支持最新 Android 16K Page Size 一文汇总

注意,目前需要 Android Studio Narwhal ,最新版 Canary 10,并且有个 Bug ,需要首次运行之后,关闭 Android Studio ,然后再次打开,再运行,才会弹出提示

如何查看项目是否支持最新 Android 16K Page Size 一文汇总

最后,如果你发现存在动态库不适配,但是你又不知道这个动态库是哪个 aar 远程依赖的,可以通过下方脚本执行 ./gradlew findSoFileOrigins 来输出:

// 在你的模块级别 build.gradle 文件中添加此任务// 例如: app/build.gradletask findSoFileOrigins { description = \"扫描项目依赖的 AAR 文件,找出 .so 文件的来源。\" group = \"reporting\" // 将任务归类到 \"reporting\" 组下 doLast { // 用于存储 AAR 标识符及其包含的 .so 文件路径 // 键 (Key): AAR 的字符串标识符 (例如:\"project :gsyVideoPlayer\", \"com.example.library:core:1.0.0\") // 值 (Value): 一个 Set 集合,包含该 AAR 内所有 .so 文件的路径 (字符串) def aarSoFilesMap = [:] def variants = null if (project.plugins.hasPlugin(\'com.android.application\')) { variants = project.android.applicationVariants } else if (project.plugins.hasPlugin(\'com.android.library\')) { variants = project.android.libraryVariants } else { project.logger.warn(\"警告: findSoFileOrigins 任务需要 Android 应用插件 (com.android.application) 或库插件 (com.android.library)。\") return } if (variants == null || variants.isEmpty()) { project.logger.warn(\"警告: 未找到任何变体 (variants) 来处理。\") return } variants.all { variant -> project.logger.lifecycle(\"正在扫描变体 \'${variant.name}\' 中的 AAR 依赖以查找 .so 文件...\") // 获取该变体的运行时配置 (runtime configuration) def configuration = variant.getRuntimeConfiguration() try { // 配置一个构件视图 (artifact view) 来精确请求 AAR 类型的构件 def resolvedArtifactsView = configuration.incoming.artifactView { view ->  view.attributes { attributes -> // 明确指定我们只对 artifactType 为 \'aar\' 的构件感兴趣 // AGP 也常用 \"android-aar\",如果 \"aar\" 效果不佳,可以尝试替换 attributes.attribute(Attribute.of(\"artifactType\", String.class), \"aar\")  }  // lenient(false) 是默认行为。如果设为 true,它会尝试跳过无法解析的构件而不是让整个视图失败。  // 但如果像之前那样,是组件级别的变体选择失败 (如 gsyVideoPlayer),lenient 可能也无法解决。  // view.lenient(false) }.artifacts // 获取 ResolvedArtifactSet project.logger.info(\"对于变体 \'${variant.name}\',从配置 \'${configuration.name}\' 解析到 ${resolvedArtifactsView.artifacts.size()} 个 AAR 类型的构件。\") resolvedArtifactsView.each { resolvedArtifactResult ->  // resolvedArtifactResult 是 ResolvedArtifactResult 类型的对象  File aarFile = resolvedArtifactResult.file  // 获取组件的标识符,这能告诉我们依赖的来源  // 例如:\"project :gsyVideoPlayer\" 或 \"com.google.android.material:material:1.7.0\"  String aarIdentifier = resolvedArtifactResult.id.componentIdentifier.displayName  aarSoFilesMap.putIfAbsent(aarIdentifier, new HashSet<String>())  if (aarFile.exists() && aarFile.name.endsWith(\'.aar\')) { // project.logger.info(\"正在检查 AAR: ${aarIdentifier} (文件: ${aarFile.name})\") try { project.zipTree(aarFile).matching { include \'**/*.so\' // 匹配 AAR 中的所有 .so 文件 }.each { File soFileInZip -> aarSoFilesMap[aarIdentifier].add(soFileInZip.path) } } catch (Exception e) { project.logger.error(\"错误: 无法检查 AAR 文件 \'${aarIdentifier}\' (路径: ${aarFile.absolutePath})。原因: ${e.message}\") }  } else { if (!aarFile.name.endsWith(\'.aar\')) { project.logger.debug(\"跳过非 AAR 文件 \'${aarFile.name}\' (来自: ${aarIdentifier}),其构件类型被解析为 AAR。\") } else { project.logger.warn(\"警告: 来自 \'${aarIdentifier}\' 的 AAR 文件不存在: ${aarFile.absolutePath}\") }  } } } catch (Exception e) { // 这个 catch 块会捕获解析构件视图时发生的错误 // 这可能仍然包括之前遇到的 \"Could not resolve all artifacts for configuration\" 错误, // 如果问题非常根本,即使是特定的构件视图也无法克服。 project.logger.error(\"错误: 无法为配置 \'${configuration.name}\' 解析 AAR 类型的构件。\" + \"可能项目设置中存在依赖变体匹配问题,\" + \"详细信息: ${e.message}\", e) // 打印异常堆栈以获取更多信息 project.logger.error(\"建议: 请检查项目依赖(尤其是本地子项目如 \':xxxxx\')的构建配置,\" + \"确保它们能正确地发布带有标准 Android 库属性(如组件类别、构建类型,以及适用的 Kotlin 平台类型等)的变体。\") // 如果希望任务在此处停止而不是尝试其他变体,可以取消下一行的注释 // throw e } } // 打印结果 if (aarSoFilesMap.isEmpty()) { project.logger.lifecycle(\"\\n在所有已处理变体的可解析 AAR 依赖中均未找到 .so 文件,或者依赖解析失败。\") } else { println \"\\n--- AAR 依赖中的 .so 文件来源 ---\" // 按 AAR 标识符排序以获得一致的输出 aarSoFilesMap.sort { it.key }.each { aarId, soFileList -> if (!soFileList.isEmpty()) {  println \"${aarId}:\" // 例如:project :gsyVideoPlayer: 或 com.some.library:core:1.0:  soFileList.sort().each { soPath -> // 对 .so 文件路径排序 println \" - ${soPath}\" // 例如: - jni/armeabi-v7a/libexample.so  } } } println \"----------------------------------\" } project.logger.lifecycle(\"任务执行完毕。要再次运行此任务,请执行: ./gradlew ${project.name}:${name}\") }}

如何查看项目是否支持最新 Android 16K Page Size 一文汇总

如何查看项目是否支持最新 Android 16K Page Size 一文汇总

这个脚本我是在 APG 8+ 下测试,不同 AGP 可能存在细微 API 差异,思路上一样。

当然,最终还是要在 16 KB 环境运行没有崩溃才行, 在之前的文章我就分享过,很多 so 查看时虽然分页是 16K 或者 64K ,但是它还是有问题的,跑在 16K 上是会崩溃的,具体原因有 NDK 工具可能过老之类:

当时是 NDK10e 等版本,编译出来的 so 都是两个 LOAD 段的 Align 是 10000(65536) , 也就是 64K 对齐,属于 16K 的 4倍,那「理论上」应该是对齐的,但是跑在 16K 上会 crash ,不过 crash 提示也不是 so 不对齐,而是在某段代码执行时出现 crash,并且你定位到的地址代码会很奇葩。

测试环境可以使用模拟器,一般适配 16 KB 的就是 arm64 ,所以 x86_64 模拟器基本没用,而且需要 Android studio Koala Feature Drop 之后的版本才行:

如何查看项目是否支持最新 Android 16K Page Size 一文汇总

如果你的 Apk 没适配 16 KB ,那么在 Android 16 的 16 KB 模拟器上会看到这样的提示:

如何查看项目是否支持最新 Android 16K Page Size 一文汇总

目前在 React Native 和 Flutter 都已经支持了 16 KB:

  • Flutter 至少 3.22 版本
  • RN 至少 0.77 版本

比如你的 RN 版本太老,就是看到类似下面的场景:

最后,如果你还没适配或者了解 16 KB,可以参考一下文章

  • Android 15 上适配 16K Page Size 的填坑思路,以 IJKPlayer 为例子

  • Android 15 之如何快速适配 16K Page Size

  • Android 15 适配之16K Page Size :为什么它会是最坑的一个适配点