预编译库与 Prefab 集成:把别人的 .so 安全放进你的 App
真实项目很少所有 native 代码都自己写。你可能会接入 FFmpeg、OpenCV、音视频 SDK、AI 推理库、图像处理库。这些库经常以 .so 或 .a 的形式交付。
这篇讲清楚:预编译库是什么,为什么“能链接”不等于“能运行”,以及怎样把三方 native 依赖纳入工程治理。
预编译库是什么
预编译库就是别人已经编译好的 native 二进制。
常见两种:
.a:静态库,链接时合进你的 .so
.so:动态库,运行时由 Android linker 加载
它像别人做好的零件。零件本身可能很好,但尺寸、接口、材质必须和你的机器匹配。
最容易忽略的匹配条件
接入 native 预编译库时,至少检查:
ABI 是否齐全
minSdk 是否兼容
STL 是否一致
依赖的其他 .so 是否齐全
符号是否冲突
16KB 页大小是否兼容
许可证是否允许分发
只看 CMake 能不能通过,是远远不够的。
CMake 接入 .so
假设三方库路径:
src/main/jniLibs/arm64-v8a/libthird_video.so
src/main/jniLibs/x86_64/libthird_video.so
CMake 中可以声明 imported library。
add_library(third_video SHARED IMPORTED)
set_target_properties(
third_video
PROPERTIES
IMPORTED_LOCATION
${CMAKE_SOURCE_DIR}/../jniLibs/${ANDROID_ABI}/libthird_video.so
)
target_link_libraries(
player_core
third_video
)
IMPORTED 的意思是:这个库不是当前 CMake 构建出来的,而是外部已经存在。
头文件也要有版本
.so 是二进制实现,.h 是源码层接口。两者必须匹配。
头文件来自 v1.2
.so 来自 v1.1
这种组合可能编译通过,但运行时行为错乱。三方库升级时要把头文件和二进制作为一个整体升级。
Prefab 是什么
Prefab 是 Android 生态中分发 native 依赖的一种方式。它把 native 库、头文件、ABI 信息、CMake 包信息放进 AAR,让 AGP/CMake 更容易消费。
如果三方 SDK 提供 Prefab,接入会比手动写 imported library 更规范。
概念上:
AAR
prefab/
modules/
xxx/
include/
libs/android.arm64-v8a/
module.json
使用方不需要手动拼每个 ABI 的路径。
为什么能链接不代表能运行
链接成功只说明构建机上能把符号关系拼起来。运行还要过 Android linker。
运行失败常见原因:
APK 没打包依赖 .so
设备 ABI 没有对应库
库依赖了设备不存在的新 API
三方库内部再 dlopen 另一个库失败
符号被隐藏或版本不一致
所以三方 native 依赖必须有运行时 smoke test。
接入清单
列出库名、版本、来源
列出支持 ABI
确认每个 ABI 都有 .so/.a
确认 headers 与 binary 同版本
确认 minSdk 与项目一致
确认是否支持 16KB page size
确认 license
写一个最小 native 调用 smoke test
本章实验
接入一个假想 libimage_filter.so。
arm64-v8a 有库
x86_64 有库
头文件版本 v2.0
minSdk 要求 23
构建后检查:
APK 里包含 lib/<abi>/libimage_filter.so
System.loadLibrary 不崩溃
调用最小函数返回预期版本号
llvm-readelf 检查 16KB 对齐
llvm-nm -D 检查异常导出符号
小白常见误解
第一,.so 放进 jniLibs 不代表它一定会被正确加载。它可能还依赖另一个没有打包的 .so。
第二,头文件能编译,不代表二进制版本正确。头文件和 .so 必须来自同一个版本。
第三,三方库不是“黑盒免责区”。它崩溃时,用户看到的是你的 App 崩溃。
第四,AAR 里藏着 native 库也要检查。很多团队以为自己没有 NDK,其实三方 SDK 已经带了 .so。
工程风险与观测
预编译库要做依赖审计。
库名
版本
来源
ABI
minSdk
是否支持 16KB
依赖库
license
每次升级都要对比旧版本。
新增了哪些 .so
删除了哪些 .so
导出符号是否明显变化
依赖库是否变化
包体大小是否变化
运行时 smoke test 也不能省。
System.loadLibrary 成功
最小函数调用成功
错误返回码可观测
初始化失败有降级或清晰错误
如果三方库初始化失败,不能让播放器进入半初始化状态。要么降级到基础能力,要么进入明确 Error 状态。
本章结论
预编译 native 库不是“复制进项目就完事”。它有 ABI、API level、符号、页对齐、依赖链和许可证边界。Prefab 能让分发更规范,但不能替代你的兼容性审计。