去年中旬我就分享过几篇关于 Android 适配 16K Page Size 的文章,当时就提及了 2025 Google 将会强制要求,而现在 Google 给出了明确时间:自 2025 年 11 月 1 日起,所有提交到 Google Play 且面向 Android 15+ 设备的新应用和现有应用的更新都必须支持 16 KB 的页面大小。
❕❕❕注意,如何适配和查看调试的文章放在了最后面。
对于兼容,最简单的判断就是你是否使用了动态链接库 so ,如果用了陈年老库,那么你大概率是必须去做适配支持,同步的还有 Flutter 和 React Native 版本,升级到对应支持 16K 的版本是必须的:
如果你的应用已经上架了 Google Play ,可以通过访问 Play 管理中心内的 app bundle 资源管理器页面查看:
那什么是 Page Size ?一般意义上,页面(Page)指的就是 Linux 虚拟内存管理中使用的最小数据单位,页面大小(Page Size)就是虚拟地址空间中的页面大小, Linux 中进程的虚拟地址空间是由固定大小的页面组成:
因为 Android 用的是 Linux 内核,所以在这部分逻辑一直以来都是遵循 Linux 的实现,只是 Android 由于「历史因素」限制,一直只支持 4 KB 内存页面大小,现在想要支持 16K 了而已:
而简单说,16k 是 4k 的整数倍,所以 16K 的 so 也可以跑在 4k 上,但是 4k 因为不是 16k 的整数倍,所以 4k 跑不了 16k 的系统。
另外,目前来说 Android 继承了 Linux 的特点,不支持混合 Page Size,也就是在 16k 设备上,你的 4k so 就是跑不了,当然,不是系统层面就一定做不了,比如 :
关于这一点,其实 Apple Silicon 的 MacOS 其实是有混合 16K/4K 支持,事实上 macOS 本身始终以 16K 页面运行,只有 Rosetta 模式下应用会以 4K 模式运行。
从目前官方的信息看,Android 16 添加了兼容模式 ,让一些针对 4 KB 内存页面构建的应用可以在配置为 16 KB 内存页面的设备上运行:
在 AndroidManifest.xml 中设置 android:pageSizeCompat 属性以启用向后兼容模式,将会阻止应用启动时显示对话框,如果需要使用 android:pageSizeCompat 属性,需使用 Android 16 SDK 编译应用
另外,关于查看 so 是否适配 16K,还可以通过 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 对齐的状态
如果是
10000
,也就是 65536 ,那就是64K 对齐,属于 16K 的 4倍,那「理论上」应该是对齐的
还有更简单的办法,就是直接下载一个 libchecker,就可以看到你的 app 里的 so 是否是 16k page size 分页:
但是, so 分页是 16k ,不代表了它就一定能在 16k 设备上正常,这是血和泪的教训。
所以,除了 app bundle 资源管理器和查看分页信息之后,最重要是确保在 16 KB 的环境中测试你的应用 ,在之前的文章我就分享过,很多 so 查看时虽然分页是 16K 或者 64K ,但是它还是有问题的,跑在 16K 上是会崩溃的,具体原因有 NDK 工具可能过老,以 IJK 为例:
IJK 的 NDK10e 等版本,编译出来的 so 都是两个 LOAD 段的 Align 是
10000(65536)
, 也就是 64K 对齐,属于 16K 的 4倍,那「理论上」应该是对齐的,但是跑在 16K 上会 crash ,不过 crash 提示也不是 so 不对齐,而是在某段代码执行时出现 crash,并且你定位到的地址代码会很奇葩。
具体解决办法也有,就是强制除了 max-page-size
之外,再配置上 common-page-size
也可以支持:
LOCAL_LDFLAGS += -Wl,-z,max-page-size=65536
LOCAL_LDFLAGS += -Wl,-z,common-page-size=65536
如果是需要 cmake 支持的,则可以:
target_link_libraries(a4ijkplayer "-Wl,-z,max-page-size=65536")
target_link_libraries(a4ijkplayer "-Wl,-z,common-page-size=65536")。
当然,还可以升级到 NDK r22 解决,例如 NDK r21 就只需要 max-page-size=16384
,从分页上更合理,至于为什么 NDK r22 可以,这就涉及 llvm 和 GUN 的版本和兼容问题 ,详细可见末尾的填坑思路。
目前 GSYVideoPlayer 的 IJK 内核已经在去年提供了 16K 的支持,具体的 git patch 和支持方式也提交在 Github ,需要的可以参考:https://github.com/CarGuo/GSYVideoPlayer/tree/master/16kpatch
最后,如果你还没适配或者了解 16K,可以参考一下文章:
参考链接
- https://android-developers.googleblog.com/2025/05/prepare-play-apps-for-devices-with-16kb-page-size.html