普通网站做,建个网站费用,wordpress站安装百度商桥,惠州网站建设惠州前几天刚聊过 《Google 开始正式强制 Android 适配 16 K Page Size》 之后#xff0c;被问到最多的问题是「怎么查看项目是否支持 16K Page Size」 #xff1f;其实有很多直接的方式#xff0c;但是最难的是当你的项目有很多依赖时#xff0c;怎么知道这个「不支持的动态库…前几天刚聊过 《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$1RED\e[31m
GREEN\e[32m
ENDCOLOR\e[0mmatches$(find $dir -name *.so -type f)
IFS$\n
for match in $matches; dores$(objdump -p ${match} | grep LOAD | awk { print $NF } | head -1)if [[ $res ~ 2**14 ]] || [[ $res ~ 2**16 ]]; thenecho -e ${match}: ${GREEN}ALIGNED${ENDCOLOR} ($res)elseecho -e ${match}: ${RED}UNALIGNED${ENDCOLOR} ($res)fi
done 整个 Apk 的话可以直接解压 Apk 然后对动态库的目录用脚本扫描。 第二种方法就是通过 Google Play 的 app bundle 资源管理器页面直接查看如果有问题会看到类似的情况 另外如果 so 没问题但是还是提示你不支持 16 KB那么很可能是你需要升级 AGP 建议至少升级到 AGP 8 最优是升级到 8.5.1 之后 没有问题的情况下是这样 第三种方法就是下载最新的 libchecker 如果动态库都有“16 KB” 那就是正常 还有一种方法就是使用 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 对齐的状态 注意是 1000 才是 4K而 10000 是 65536 那就是64K 对齐属于 16K 的 4倍那「理论上」应该是对齐的 最后你还可以在 Android Studio 里运行你的 App然后 Android Studio 会提示存在哪些动态库没适配 16 KB 注意目前需要 Android Studio Narwhal 最新版 Canary 10并且有个 Bug 需要首次运行之后关闭 Android Studio 然后再次打开再运行才会弹出提示 最后如果你发现存在动态库不适配但是你又不知道这个动态库是哪个 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 nullif (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 // 获取 ResolvedArtifactSetproject.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.0String aarIdentifier resolvedArtifactResult.id.componentIdentifier.displayNameaarSoFilesMap.putIfAbsent(aarIdentifier, new HashSetString())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})}
} 这个脚本我是在 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 之后的版本才行 如果你的 Apk 没适配 16 KB 那么在 Android 16 的 16 KB 模拟器上会看到这样的提示 目前在 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 为什么它会是最坑的一个适配点