如何查看项目是否支持最新 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 :为什么它会是最坑的一个适配点
相关文章:

如何查看项目是否支持最新 Android 16K Page Size 一文汇总
前几天刚聊过 《Google 开始正式强制 Android 适配 16 K Page Size》 之后,被问到最多的问题是「怎么查看项目是否支持 16K Page Size」 ?其实有很多直接的方式,但是最难的是当你的项目有很多依赖时,怎么知道这个「不支持的动态库…...

ESP32C3连接wifi
文章目录 🔧 一、ESP32-C3 连接 Wi-Fi 的基本原理(STA 模式)✅ 二、完整代码 注释讲解(适配 ESP32-C3)📌 三、几个关键点解释🔚 四、小结 🔧 一、ESP32-C3 连接 Wi-Fi 的基本原理&a…...
HTTP方法和状态码(Status Code)
HTTP方法 HTTP方法(也称HTTP动词)主要用于定义对资源的操作类型。根据HTTP/1.1规范(RFC 7231)以及后续扩展,常用的HTTP方法有以下几种: GET:请求获取指定资源的表示形式。POST:向指…...

机器学习中分类模型的常用评价指标
评价指标是针对模型性能优劣的一个定量指标。 一种评价指标只能反映模型一部分性能,如果选择的评价指标不合理,那么可能会得出错误的结论,故而应该针对具体的数据、模型选取不同的的评价指标。 本文将详细介绍机器学习分类任务的常用评价指…...
# YOLOv3:基于 PyTorch 的目标检测模型实现
YOLOv3:基于 PyTorch 的目标检测模型实现 引言 YOLOv3(You Only Look Once)是一种流行的单阶段目标检测算法,它能够直接在输入图像上预测边界框和类别概率。YOLOv3 的优势在于其高效性和准确性,使其在实时目标检测任…...

MySQL的Docker版本,部署在ubantu系统
前言 MySQL的Docker版本,部署在ubantu系统,出现问题: 1.执行一个SQL,只有错误编码,没有错误提示信息,主要影响排查SQL运行问题; 2.这个问题,并不影响实际的MySQL运行,如…...

Mac QT水平布局和垂直布局
首先上代码 #include "mainwindow.h" #include "ui_mainwindow.h" #include <QPushButton> #include<QVBoxLayout>//垂直布局 #include<QHBoxLayout>//水平布局头文件 MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), …...
服务器制造业中,L2、L6、L10等表示什么意思
在服务器制造业中,L2、L6、L10等是用于描述服务器生产流程集成度的分级体系,从基础零件到完整机架系统共分为L1-L12共12个等级。不同等级对应不同的生产环节和交付形态,以下是核心级别的具体含义: L2(Level 2…...

回答 | 图形数据库neo4j社区版可以应用小型企业嘛?
刚在知乎上看到了一个提问,挺有意思,于是乎,贴到这里再简聊一二。 转自知乎提问 当然可以,不过成本问题不容小觑。另外还有性能上的考量。 就在最近,米国国家航空航天局——NASA因为人力成本问题,摒弃了使…...

Linux操作系统从入门到实战(二)手把手教你安装VMware17pro与CentOS 9 stream,实现Vim配置,并配置C++环境
Linux操作系统从入门到实战(二)手把手教你安装VMware17pro与CentOS 9.0 stream,实现Vim配置,并编译C文件 前言一、安装VMware17pro二、安装CentOS9.02.1 为什么选择CentOS9,与CentOS7对比2.1 官网下载CentOS9.02.2 国内…...

软考架构师考试-UML图总结
考点 选择题 2-4分 案例分析0~1题和面向对象结合考察,前几年固定一题。近3次考试没有出现。但还是有可能考。 UML图概述 1.用例图:描述系统功能需求和用户(参与者)与系统之间的交互关系,聚焦于“做什么”。 2.类图&…...
后端系统做国际化改造,生成多语言包
要将你当前系统中的 中文文案提取并翻译为英文语言包,建议采用 自动扫描 翻译辅助 语言包生成 的方式,流程如下: ✅ 一、目标总结 提取:扫描后端 C# 和 Java 代码中的 中文字符串 翻译:将中文自动翻译为英文&#x…...

论文学习_Trex: Learning Execution Semantics from Micro-Traces for Binary Similarity
摘要:检测语义相似的函数在漏洞发现、恶意软件分析及取证等安全领域至关重要,但该任务面临实现差异大、跨架构、多编译优化及混淆等挑战。现有方法多依赖语法特征,难以捕捉函数的执行语义。对此,TREX 提出了一种基于迁移学习的框架…...

在VirtualBox中安装虚拟机后不能全屏显示的问题及解决办法
在VirtualBox中安装Windows或Linux虚拟机后,将遇到启动虚拟机后,只能在屏幕中的一块区域里显示虚拟机桌面,却不能全屏显示的问题。要解决此问题,需要在虚拟机中安装与VirtualBox版本相对应的VBox GuestAdditons软件。 这里…...
《大规模电动汽车充换电设施可调能力聚合评估与预测》MATLAB实现计划
模型概述 根据论文,我将复刻实现结合长短期记忆网络(LSTM)和条件变分自编码器(CVAE)的预测方法,用于电动汽车充换电设施可调能力的聚合评估与预测。 实现步骤 1. 数据预处理 导入充电数据 (Charging_Data.csv)导入天气数据 (Weather_Data.csv)导入电…...
CSS flex:1
在 CSS 中,flex: 1 是一个用于弹性布局(Flexbox)的简写属性,主要用于控制 flex 项目(子元素)如何分配父容器的剩余空间。以下是其核心作用和用法: 核心作用 等分剩余空间:让 flex …...
Python 字典键 “三变一” 之谜
开头:读者的“玄学”字典谜题 上周,朋友发来了一段让他抓耳挠腮的代码: >>> {True: foo, 1: bar, 1.0: baz} {True: baz} “我明明定义了布尔True、整数1、浮点数1.0三个键,结果字典里只剩True一个键,值…...
Spring Boot中HTTP连接池的配置与优化实践
精心整理了最新的面试资料和简历模板,有需要的可以自行获取 点击前往百度网盘获取 点击前往夸克网盘获取 一、HTTP连接池的核心价值 在微服务架构和分布式系统场景中,HTTP客户端频繁创建/断开连接会产生显著的性能损耗。通过连接池技术可以实现&#x…...
初识XML
初识XML <?xml version"1.0" encoding"utf-8" ?> <!--根标签只能有一个--> <!--第一行永远都是 <?xml version"1.0" encoding"utf-8" ?> 前面不允许出现任何其他东西,空格换行等均不行 --> &…...

element-ui分页的使用及修改样式
1.安装 npm install element-ui -S 2.在main.js中引入,这里是全部引入,也可以按需引入 import ElementUI from element-ui import element-ui/lib/theme-chalk/index.css Vue.use(ElementUI) 3.使用 layout"prev, pager, next, jumper" :jumpe…...
2025年第十六届蓝桥杯软件赛省赛C/C++大学A组个人解题
文章目录 题目A题目C:抽奖题目D:红黑树题目E:黑客题目F:好串的数目 https://www.dotcpp.com/oj/train/1166/ 题目A 找到第2025个素数 #include <iostream> #include <vector> using namespace std; vector<i…...
物理:人的记忆是由基本粒子构成的吗?
问题: 基因属于人体的一部分,记忆也是人体的一部分,那么为什么基因可以代际遗传,但是记忆却被清空重置。如果基因是由粒子构成,那么记忆是不是也应该由粒子构成?如果记忆是粒子构成的,那么能否说明记忆永恒,即使死亡了身体被分解了,那么只要保证其身体有关的所有粒子被…...
Memcached 的特性和使用场景介绍,以及集群搭建
以下是 Memcached 的特性和使用场景介绍,以及集群搭建的详细示例: 特性 高性能 内存存储:数据存储在内存中,读写速度极快。简单协议:使用基于文本的简单协议,通信高效。分布式架构 一致性哈希:采用一致性哈希算法,将数据均匀分布到多个节点,支持动态增减节点,减少数…...
uni-app,小程序中的addPhoneContact,保存联系人到手机通讯录
文章目录 方法详解简介 基本语法参数说明基础用法使用示例平台差异说明注意事项最佳实践 方法详解 简介 addPhoneContact是uni-app框架提供的一个实用API,用于向系统通讯录添加联系人信息。这个方法在需要将应用内的联系人信息快速保存到用户设备通讯录的场景下非…...

从数据中台到数据飞轮:数字化转型的演进之路
从数据中台到数据飞轮:数字化转型的演进之路 数据中台 数据中台是企业为整合内部和外部数据资源而构建的中介层,实现数据的统一管理、共享和高效利用,目标是打破信息孤岛,提高数据使用效率,支持业务决策和创新 实施成本…...
Spring Boot 注解详细解析:解锁高效开发的密钥
一、引言 Spring Boot 以其快速开发、自动配置等特性,成为构建 Java 应用程序的热门框架。而注解在 Spring Boot 中扮演着至关重要的角色,它们如同魔法指令,简化了配置流程,增强了代码的可读性与可维护性。本文将深入剖析 Spring…...

2025年5月-信息系统项目管理师高级-软考高项一般计算题
决策树和期望货币值 加权算法 自制和外购分析 沟通渠道 三点估算PERT 当其他条件一样时,npv越大越好...

zst-2001 上午题-历年真题 算法(5个内容)
回溯 算法 - 第1题 找合适的位置,如果没有位置就按B回家 d 分治 算法 - 第2题 b 算法 - 第3题 a 算法 - 第4题 划分一般就是分治 a 算法 - 第5题 分治 a 0-1背包 算法 - 第6题 c 算法 - 第7题 最小的为c 3100 c 算法 - 第8题 …...
【愚公系列】《Manus极简入门》036-物联网系统架构师:“万物互联师”
🌟【技术大咖愚公搬代码:全栈专家的成长之路,你关注的宝藏博主在这里!】🌟 📣开发者圈持续输出高质量干货的"愚公精神"践行者——全网百万开发者都在追更的顶级技术博主! …...
3d关键点 可视化
目录 pygame pygame保存mp4 mayavi pygame import pygame from pygame.locals import * import numpy as np import sys# 初始化Pygame pygame.init() width, height 800, 600 screen pygame.display.set_mode((width, height)) clock pygame.time.Clock()# 生成示例数据…...