ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Android 15 16KB 适配实战:腾讯 mars xlog 独立编译与避坑指南

Android 15 16KB 适配实战:腾讯 mars xlog 独立编译与避坑指南 简介腾讯Mars中的xlog 16KB对齐版本面向Android 15与arm64-v8架构基于xlog master分支最新提交db98964cbc992c1191e4d993619adad72452fcdc编译而成专供需要在新系统上使用64位日志组件的开发者使用。包内除xlog核心so库外还附带Java接口、XML配置、C源码及Gradle构建文件压缩包共11个文件约4.38MB结构精简便于直接集成。资源对应NDK版本为28.1.13356709若App编译失败可切换至该版本解决由于仅支持arm64-v8使用时需注意排除32位机型。同时16KB对齐针对Android 15的内存访问要求做了优化可提升运行效率并减少碎片。目前已有914人学习下载对有适配Android 15或探索Mars xlog源码的开发者具备直接参考价值解压后即可查看关键实现与联调配置。 前两周把项目跑在 Android 15 的设备上结果一装机直接翻车系统提示“此应用与设备不兼容”手里的 16KB 模拟器上崩溃日志反复出现dlopen failed定位到最后问题出在腾讯 mars 里的 xlog 上。xlog 这个日志库微信用了很多年性能确实没话说但旧版 so 是按 4KB 内存页对齐编译的Android 15 开始强制 16KB 对齐所以必须重新编译一份。我这里整理的就是这样一版腾讯 mars 中的 xlog 16KB 对齐版本支持 Android 15但注意只有 xlog而且只有 arm64-v8a 架构。如果你也踩在这个坑里可以直接按这个思路走一遍。1. 为什么 Android 15 让 xlog“不打补丁就不行”1.1 16KB 内存页到底是什么操作系统把内存分成固定大小的块来管理这个块叫“页”。以前 Android 内核默认用 4KB 一页App 的 native 库在编译时也按 4KB 页大小做各种对齐。现在 Android 15 开始支持 16KB 的页大小同样一块数据系统一次能搬运更多对大内存设备和整体性能有一定好处代价就是所有 native 代码的 ELF 文件格式必须按新规则对齐。这个变化对普通 Java/Kotlin 代码几乎没有感知但凡是带.so的 App都要重新过一遍。系统加载 so 的时候内核和 linker 会检查每个 LOAD segment 的地址和偏移是否满足页大小对齐条件。如果不满足dlopen会直接报错轻则日志功能不可用重则整个进程起不来。1.2 老版 xlog 的 so 为什么首当其冲腾讯 mars 是老项目了xlog 模块最初编译时用的基本都是默认 4KB 对齐。我们项目里的libmars.so、libxlog.so用readelf一看LOAD 段对齐值写的是0x1000也就是 4096。在 Android 14 及以下没问题但在 16KB 页大小的 Android 15 设备上linker 发现对齐不满足要求直接拒绝加载。xlog 在初始化时走 JNI会立即加载 native 库所以Xlog.Init那一行必然崩溃。这个问题不只影响 xlog所有第三方 native 库都可能有同样的坑比如游戏引擎、其他日志库、加密库。但 xlog 因为微信生态太普及中招的团队特别多。1.3 快速判断现有 xlog 是否需要适配在终端里跑一条命令就行llvm-readelf -l libxlog.so | grep LOAD输出里每个 LOAD 段如果长这样LOAD 0x000000 0x0000000000000000 0x0000000000000000 0x0a1a40 0x0a1a40 R E 0x1000注意最后的0x1000这是段内对齐值。如果是0x1000说明还是 4KB 对齐到 Android 15 16KB 设备上大概率会出事。如果已经是0x4000那就是 16KB 对齐可以放心用。2. 这版“16KB对齐版本”的内容与边界2.1 只有 xlog不包含 mars 全家桶mars 是个大仓库里面有 xlog 日志、STN 长连接、SDT 信令等一堆模块。如果你的项目只需要日志能力直接把整个 mars 依赖拖进来会很重也不会为了一个日志库去适配它所有的网络和传输模块。所以我这个版本里只保留了 xlog 相关的代码和 so。这个选择和“按需接入”的思路是一致的。xlog 的核心价值是写日志快、省电、不丢日志不依赖 mars 的其他模块也能独立跑。mars 的 Java 层提供了com.tencent.mars.xlog.Log和Xlog初始化类只要把对应 JNI 层编译好单独接日志完全够用。2.2 只有 arm64-v8a选择理由这个版本只出arm64-v8a一个 ABI。原因很简单Android 15 的 16KB 模式面向的是 64 位系统32 位 so 在新设备上本来就不受待见。Google 现在的生态方向已经明显偏向 64 位老项目即使还在用 armeabi-v7a也应该尽快切到 arm64-v8a。从技术上讲只保留 arm64-v8a 能省去很多交叉编译的工作量也不用为 32 位设备里那些 16KB 对齐的坑费心思。如果你的 App 还在支持 32 位系统那这个版本不适合你建议等全量 64 位改造完成后再考虑。如果已经是纯 arm64-v8a 的项目这个版本是开箱即用的。2.3 适合什么项目不适合什么项目适合的场景很明确你已经在使用或者想接入 xlog只是把它当日志库用App 本身就是 arm64-v8a only并且要跑在 Android 15 上。不适合的场景也很明确如果你还在用 mars 的 STN 长连接、或者业务依赖 mars 的 IP 直连和推拉能力那么只换 xlog 会把链路打断。这种情况建议等 mars 官方发布完整的 16KB 适配版本或者自己把整个 mars 都重新编译一遍。另外如果你的 App 还有 armeabi-v7a 的 so那这个版本没法满足你。3. 从源码编译 16KB 对齐 xlog 的实操记录3.1 环境准备与版本选择推荐用 NDK r27 或更新版本。原因很简单新版 NDK 的链接器和系统库对 16KB 页大小支持更完整省得自己去处理一些莫名其妙的兼容问题。我用的是 Windows 11 加 WSL 环境Android 开发机是 macOS 也一样命令基本通用。需要准备的工具Android NDK r27CMake 3.22NinjaPython 3一个能正常跑 NDK 的终端环境3.2 抽 xlog 源码并准备独立构建mars 的源码在 GitHub 上可以拉取仓库地址就是Tencent/mars。不要直接在原工程里全量编译因为那样会带出一堆不需要的模块。我自己是把仓库里跟 xlog 强相关的目录抽出来单独组成一个工程。简化后的目录大致是这样xlog-solo/ ├── CMakeLists.txt ├── mars/ │ ├── comm/ │ └── xlog/ │ ├── src/ │ └── jni/ ├── libraries/ │ └── log/ │ ├── cpp/ │ └── java/libraries/log/cpp是核心 native 代码mars/comm是公共基础库这两个基本是 xlog 的最小依赖集合。libraries/log/java是 Java 层接口不参与编译直接拷到 Android 工程里用就行。需要注意不同版本的 mars 目录结构有差异我基于的是当前 master 分支。如果你用的是旧版本源码路径可能不同但核心依赖关系不会差太多。3.3 链接器参数16KB 对齐的关键编译时最关键的一步是给链接器加上这两个参数-Wl,-z,max-page-size16384 -Wl,-z,common-page-size16384作用很简单让链接器把输出 ELF 的最大页大小和通用页大小都改成 16384 字节。这样生成的 so 里 LOAD 段对齐值就从0x1000变成0x4000。在 CMake 里可以这么加target_link_options(xlog PRIVATE -Wl,-z,max-page-size16384 -Wl,-z,common-page-size16384 )如果你不是用 CMake而是用 Android Studio 的 externalNativeBuild直接在build.gradle里写externalNativeBuild { cmake { arguments -DANDROID_ABIarm64-v8a cppFlags } }然后在CMakeLists.txt里同样加target_link_options。还有一个容易忽略的点zipalign是 Android 打包时对 APK 资源的对齐它不会改 so 内部的 ELF 对齐。很多人只做了zipalign -p 16以为就完事了其实 ELF 的 LOAD 段对齐必须靠编译链接阶段修正。3.4 编译与产物提取我用的是命令行方式编译先写好CMakeLists.txt然后用 NDK 自带的工具链跑$ANDROID_NDK_HOME/build/cmake/android.toolchain.cmake \ -DANDROID_ABIarm64-v8a \ -DANDROID_PLATFORMandroid-23 \ -DANDROID_STLc_static \ -DCMAKE_BUILD_TYPERelease \ -B build-arm64 \ -G Ninja然后ninja -C build-arm64编译完成后在build-arm64目录下找到libxlog.so。这个 so 已经包含 xlog 的所有 native 逻辑可以直接用。记得先不要急着 strip等验证完对齐再 strip 也不迟。3.5 用 readelf 验证对齐为了保险编译完立刻验证llvm-readelf -l build-arm64/libxlog.so | grep LOAD如果输出里每个 LOAD 段的末尾对齐值都是0x4000说明成功了。如果还是0x1000回头检查链接参数是不是真的传进去了尤其是 CMake 有没有把target_link_options作用在最终的目标上。另外可以看下动态段llvm-readelf -l build-arm64/libxlog.so | grep DYNAMIC确认DYNAMIC段的对齐也是0x4000。到这里native 库这关就过了。4. 接入 Android 项目并验证 Android 154.1 替换 so 与 Java 层初始化把编译好的libxlog.so放到项目的app/src/main/jniLibs/arm64-v8a/目录下。如果你之前用的是整个 mars 包需要把原来对mars/stn、mars/app等模块的初始化依赖去掉只保留 xlog 初始化。Java 层初始化代码和官方示例保持一致import com.tencent.mars.xlog.Xlog; import com.tencent.mars.xlog.Log; public class XlogHelper { public static void initXlog(String logDir, String cacheDir) { Xlog.appenderOpen(Xlog.LEVEL_DEBUG, 0, 1, logDir, logDir, xlog, 0, false); Log.setLogImp(new Xlog()); } }如果你之前用的是 mars 全量包Java 层包名没有变只是依赖范围变小了所以改动不会太大。4.2 Gradle 里的 ABI 与打包配置既然这个版本只有 arm64-v8a就必须在build.gradle里明确 abiFilters否则如果项目其他地方还有armeabi-v7a的 so打包时会出问题。defaultConfig { ndk { abiFilters arm64-v8a } } packagingOptions { jniLibs { useLegacyPackaging false } }如果你的应用还需要支持 32 位设备这个版本就没办法直接嵌进去了只能做多 ABI 动态下载或者等完整版本。4.3 Android 15 真机/模拟器验证在 Android 15 设备上先确认当前系统的内存页大小adb shell getconf PAGESIZE如果输出16384说明这就是一台 16KB 页大小的设备。如果输出4096那还只是普通 4KB 页即使 Android 15 也测不出对齐问题建议换一台 16KB 模拟器或真机。然后安装 App跑一遍日志写入和读取。重点看启动日志里有没有dlopen failed之类的错误同时观察日志文件是否正确生成、写入速度是否正常。如果一切正常说明 xlog 已经在 Android 15 16KB 环境下跑通了。4.4 兼容旧设备minSdkVersion 的问题只支持 arm64-v8a 意味着你的 App 最低支持的设备也变成 64 位通常建议minSdkVersion不低于 23因为早期 32 位设备太多。如果项目还有老用户要提前做流量评估。xlog 的核心机制不挑系统版本所以只要 ARM64 设备Android 10 到 Android 15 都能正常用实测下来性能差异不大。5. 常见问题与避坑清单5.1 常见问题速查表下面这个表是我在适配过程中遇到或者身边朋友问得比较多的问题列成速查格式方便直接对号入座。问题现象根本原因解决办法dlopen failed: library ... not loadableso 还是 4KB 对齐16KB 设备上加载失败按 3.3 节重新编译确认 LOAD 段对齐为0x4000INSTALL_FAILED_NO_MATCHING_ABIS工程里 so 的 ABI 和系统不匹配检查 abiFilters确认只保留 arm64-v8a编译后readelf看到0x1000链接参数没生效或没编到最终目标用target_link_options并确认目标名正确初始化 xlog 时崩溃无明确日志JNI 层没有加载到 so或 Java 层初始化顺序不对先单独调System.loadLibrary(xlog)再调 appenderOpen放在 4KB 页设备上正常16KB 设备上崩溃还有第三方 so 没做 16KB 对齐逐个检查所有 native 库不能只改 xlog接入后日志不落盘日志路径没权限或目录不存在确认appenderOpen的目录已经在初始化前创建5.2 实操中容易忽略的细节第一个坑是strip。很多人编译完马上执行llvm-strip给 so 瘦身如果你先 strip 再验证一旦出错都不知道是编译参数问题还是 strip 改变了 ELF 布局。正确顺序是先验证对齐再做 strip最后再验证一次虽然繁琐但保险。第二个坑是只改了自己的 so没检查依赖。很多 App 不止 xlog 一个 native 库比如加密、图片处理、音视频都有 so。如果其他库还是 4KB 对齐App 一样上不了 16KB 设备。建议把jniLibs里所有 so 都扫一遍统一处理。第三个坑是 mars 官方包和独立 xlog 包混用。如果原来是com.tencent.mars:mars-core这种完整依赖现在换成只有 xlog 的自编译包要确认自己 app 的代码里没有引用com.tencent.mars.stn等其他类否则会NoClassDefFoundError。我建议直接全局搜索mars.stn和mars.app把相关调用清掉再集成。第四个坑是 Java 层和 native 层的版本匹配。xlog 的 Java 层和 native 层是有对应关系的自编译版本最好直接使用源码里同版本的 Java 类不要拿旧版 Java 去配新版 so容易导致 JNI 方法签名对不上。5.3 16KB 适配扩展检查清单如果你的问题是 xlog 解决了但整个包还没过 16KB 这关可以按这个清单快速排查列出所有jniLibs下的 so逐个llvm-readelf -l查看 LOAD 对齐检查 APK 里 so 是否被压缩或没对齐最好用zipalign -c -P 16 -v 4检查如果用了 Unity 等跨平台引擎引擎自己打的 lib 也要检查很多老版本 Unity 同样有 16KB 对齐问题部分第三方 SDK 只更新了 64 位32 位包可能没有适配直接砍掉 32 位 ABI 是最快的办法这次适配给我最大的感受是Android 15 的 16KB 改动是牵一发动全身的事xlog 只是第一块多米诺骨牌。单独编译难度不大真正的工程量在于把项目里所有 native 库都检查一遍。最后再分享一个小技巧改了链接参数之后一定要先做对齐验证再拿去集成不要等测试装机才问为什么崩。如果你的项目还有其他自己编译的 so全部按同样的思路过一遍Android 15 这关才算真正落地。本文还有配套的精品资源点击获取
返回列表