如何查看项目是否支持最新 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; 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.0"String aarIdentifier = resolvedArtifactResult.id.componentIdentifier.displayNameaarSoFilesMap.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}")}
}

这个脚本我是在 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 :为什么它会是最坑的一个适配点

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/905234.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

HttpServletResponse的理解

HttpServletResponse 是 Java Servlet API 提供的一个接口 常用方法 方法用途setContentType(String type)设置响应内容类型&#xff08;如 "application/json"、"text/html"&#xff09;setStatus(int sc)设置响应状态码&#xff08;如 200、404&#x…

可灵 AI:开启 AI 视频创作新时代

在当今数字化浪潮中&#xff0c;人工智能&#xff08;AI&#xff09;技术正以前所未有的速度渗透到各个领域&#xff0c;尤其是在内容创作领域&#xff0c;AI 的应用正引发一场革命性的变革。可灵 AI 作为快手团队精心打造的一款前沿 AI 视频生成工具&#xff0c;宛如一颗璀璨的…

用 AltSnap 解锁 Windows 窗口管理的“魔法”

你有没有遇到过这样的场景&#xff1a;电脑屏幕上堆满了窗口&#xff0c;想快速调整它们的大小和位置&#xff0c;却只能拖来拖去&#xff0c;费时又费力&#xff1f;或者你是个多任务狂魔&#xff0c;喜欢一边写代码、一边看文档、一边刷视频&#xff0c;却发现 Windows 自带的…

深度策略梯度算法PPO

一、策略梯度核心思想和原理 从时序差分算法Q学习到深度Q网络&#xff0c;这些算法都侧重于学习和优化价值函数&#xff0c;属于基于价值的强化学习算法&#xff08;Value-based&#xff09;。 1. 基于策略方法的主要思想&#xff08;Policy-based&#xff09; 基于价值类方…

【LaTeX】Word插入LaTeX行间公式如何编号和对齐

在 Word 文档中插入公式&#xff0c;需要用到 LaTeX \LaTeX LATE​X 。但遗憾的是&#xff0c;Word 只支持部分 LaTeX \LaTeX LATE​X 语法&#xff0c;这就导致很多在 Markdown 能正常渲染的公式在 Word 中无法正常显示。 “内嵌”和“显示” 首先介绍一下 Word 的“内嵌”…

互联网大厂Java面试实战:Spring Boot到微服务的技术问答解析

&#x1f4aa;&#x1f3fb; 1. Python基础专栏&#xff0c;基础知识一网打尽&#xff0c;9.9元买不了吃亏&#xff0c;买不了上当。 Python从入门到精通 &#x1f601; 2. 毕业设计专栏&#xff0c;毕业季咱们不慌忙&#xff0c;几百款毕业设计等你选。 ❤️ 3. Python爬虫专栏…

spring boot3.0自定义校验注解:文章状态校验示例

文章目录 Spring Boot 自定义校验注解&#xff1a;状态校验示例一、创建 State 注解步骤&#xff1a;1. 创建自定义注解&#xff1a;2. 实现校验逻辑&#xff1a; 二、 实现自定义校验步骤:1. 在实体类中使用自定义校验注解 State&#xff1a;2. 添加 State 注解&#xff1a; 总…

无侵入式弹窗体验_探索 Chrome 的 Close Watcher API

1. 引言 在网页开发中,弹窗(Popup)是一种常见的交互方式,用于提示用户进行操作、确认信息或展示关键内容。然而,传统的 JavaScript 弹窗方法如 alert()、confirm() 和 prompt() 存在诸多问题,包括阻塞主线程、样式不可定制等。 为了解决这些问题,Chrome 浏览器引入了 …

调出事件查看器界面的4种方法

方法1. 方法2. 方法3. 方法4.

Ubuntu 安装远程桌面连接RDP方式

1. 安装 XFCE4 桌面环境 如果你的 Ubuntu 系统默认使用 GNOME 或其它桌面环境&#xff0c;可以安装轻量级的 XFCE4&#xff1a; sudo apt update sudo apt install xfce4 xfce4-goodies 说明&#xff1a;xfce4-goodies 包含额外的插件和工具&#xff08;如面板插件、终端等&a…

LWIP传输层协议笔记

传输协议简介 文件/图片/视频 都是一堆二进制数据 经过传输层来传输 这两种协议有什么区别呢&#xff1f; 传输层的TCP/UDP三个步骤 TCP使用传输流程 1、三次握手 作用&#xff1a;三次握手就是建立连接的过程 2、传输数据 作用&#xff1a;建立连接完成之后&#xff…

数据分析与逻辑思维:六步解决业务难题;参考书籍《数据分析原理:6步解决业务分析难题 (周文全, 黄怡媛, 马炯雄)》

文章目录 一、懂业务&#xff1a;业务背景与逻辑前提1.1 明确业务目标与问题定义1.2 培养批判性思维与高于业务视角 二、定指标&#xff1a;构建科学的指标体系2.1 指标拆解与维度分析2.2 典型指标体系案例&#xff1a;用户与业务视角 三、选方法&#xff1a;匹配业务需求的分析…

开启WSL的镜像网络模式

开启WSL的镜像网络模式 前提 Windows主机系统版本高于Windows 11 22H2。WLS版本>2.0。 可输入wsl --version查看当前系统wsl版本。 修改设置 图形界面修改 在开始菜单中搜索&#xff1a;wsl settings&#xff0c;结果如下图所示&#xff1a; 点击“打开”&#xff0…

Python爬虫第20节-使用 Selenium 爬取小米商城空调商品

目录 前言 一、 本文目标 二、环境准备 2.1 安装依赖 2.2 配置 ChromeDriver 三、小米商城页面结构分析 3.1 商品列表结构 3.2 分页结构 四、Selenium 自动化爬虫实现 4.1 脚本整体结构 4.2 代码实现 五、关键技术详解 5.1 Selenium 启动与配置 5.2 页面等待与异…

聚类分析的原理、常用算法及其应用

聚类分析的原理、常用算法及其应用 一、聚类分析的基本原理 &#xff08;一&#xff09;什么是聚类分析 聚类分析是一种无监督学习方法&#xff0c;其目标是将数据集中的样本划分为若干个簇&#xff0c;每个簇包含相似的样本。聚类分析的核心思想是通过某种相似性度量&#…

Aware和InitializingBean接口以及@Autowired注解失效分析

Aware 接口用于注入一些与容器相关信息&#xff0c;例如&#xff1a; ​ a. BeanNameAware 注入 Bean 的名字 ​ b. BeanFactoryAware 注入 BeanFactory 容器 ​ c. ApplicationContextAware 注入 ApplicationContext 容器 ​ d. EmbeddedValueResolverAware 注入 解析器&a…

JDK 安装与配置

JDK 全称是 Java SE Development Kit&#xff0c;翻译成中文就是&#xff1a;Java 标准版开发包&#xff0c;是 Sun 公司&#xff08;后被 Oracle 公司收购&#xff09;专门外 Java 开发人员提供的一套用于开发 Java 应用程序的工具包。 JDK 提供了用于编译和运行 Java 应用程序…

防火墙来回路径不一致导致的业务异常

案例拓扑&#xff1a; 拓扑描述&#xff1a; 服务器有2块网卡&#xff0c;内网网卡2.2.2.1/24 网关2.2.254 提供内网用户访问&#xff1b; 外网网卡1.1.1.1/24&#xff0c;外网网关1.1.1.254 80端口映射到公网 这个时候服务器有2条默认路由&#xff0c;分布是0.0.0.0 0.0.0.0 1…

Java面试高频问题(36-37)

三十六、服务网格核心能力与设计模式 服务网格架构分层模型 mermaid graph TB subgraph 数据平面 ASidecar代理 -->拦截流量 BEnvoy B -->协议转换 CHTTP/gRPC B -->策略执行 D熔断/限流 end subgraph 控制平面 E配置中心 -->下发策略 Fistiod F -->证书管理 …

redis数据结构-02(INCR、DECR、APPEND)

字符串操作&#xff1a;INCR、DECR、APPEND Redis 字符串不仅仅是简单的文本&#xff0c;它们还可以表示数字。此功能使我们能够直接对存储在 Redis 中的字符串值执行原子的递增和递减操作。此外&#xff0c;Redis 还提供了一种附加到现有字符串的方法&#xff0c;从而可以轻松…