PyTorch Android 构建与集成指南:从 Maven 依赖到源码编译 libpytorch.so 的完整实践
PyTorch 的 Android 集成位于仓库的 android/ 目录,它是把 LibTorch 的 C++ 推理能力通过 JNI 桥接层(libpytorch.so / libpytorch_jni_lite.so)暴露给 Android 应用的完整工程。本文基于 android/README.md 展开,覆盖 Android 官方发布的 Release/Nightly 依赖引入方式、从源码编译 libtorch 并打包成 AAR 的完整流程,以及在自己的 Android 原生构建中直接链接预编译 libtorch 的 CMake 方案,并深入源码讲解 BUILD_LITE_INTERPRETER、ABI_FILTERS、fbjni 桥接层等关键配置背后的实际实现。
一、Android 集成的整体形态:lite 与 full jit 两种构建
在阅读具体操作前,先理解 android 目录的模块划分。settings.gradle 声明了四个模块:
include ':app', ':pytorch_android', ':pytorch_android_torchvision', ':pytorch_host', ':test_app'
project(':pytorch_android_torchvision').projectDir = file('pytorch_android_torchvision')
project(':pytorch_host').projectDir = file('pytorch_android/host')
pytorch_android:核心库,产出pytorch_android.aar,内含 Java API、JNI 桥接层和各 ABI 的 native 库;pytorch_android_torchvision:视觉模型支持库,产出pytorch_android_torchvision.aar;pytorch_host:宿主(Host)侧构建,用于在非 Android 平台上验证 JNI 层。
整个集成最关键的设计是 lite interpreter 与 full jit 二选一 的构建模式。在 pytorch_android/CMakeLists.txt 中可以看到:
option(BUILD_LITE_INTERPRETER "Master flag to build pytorch_jni_lite" ON)
if(BUILD_LITE_INTERPRETER)
project(pytorch_jni_lite CXX)
set(PYTORCH_JNI_TARGET pytorch_jni_lite)
else()
project(pytorch_jni CXX)
set(PYTORCH_JNI_TARGET pytorch_jni)
endif()
即开关打开时编译 pytorch_jni_lite(Lite Interpreter 后端,对应 Maven 坐标中的 pytorch_android_lite),关闭时编译 pytorch_jni(完整 JIT,对应 pytorch_android)。CMake 根据该开关选择编译的源码文件也不同:
if(BUILD_LITE_INTERPRETER)
file(GLOB pytorch_android_SOURCES
${pytorch_android_DIR}/pytorch_jni_lite.cpp
${pytorch_android_DIR}/pytorch_jni_common.cpp
${pytorch_android_DIR}/pytorch_jni_common.h
)
else()
file(GLOB pytorch_android_SOURCES
${pytorch_android_DIR}/pytorch_jni_jit.cpp
${pytorch_android_DIR}/pytorch_jni_common.cpp
${pytorch_android_DIR}/pytorch_jni_common.h
)
endif()
pytorch_android/build.gradle 则通过环境变量 BUILD_LITE_INTERPRETER 同步控制 Java 侧的排除逻辑:当设置为 0 时,构建 full jit 版本,排除 LiteModuleLoader.java 和 LiteNativePeer.java,并只保留 full jit 的 instrumented tests;默认(非 0)则构建 lite 版本。这就是为什么 README 中 Maven 依赖要分 *_lite 与常规两种坐标——它们由同一套源码、同一个开关产出。
此外,CMakeLists 中对 Android 平台启用了 Vulkan 支持(if(ANDROID_ABI) set(USE_VULKAN ON)),并通过 import_static_lib 把 libtorch、libtorch_cpu、libc10、libnnpack、libXNNPACK、libpytorch_qnnpack、libpthreadpool、libeigen_blas、libcpuinfo、libclog 等静态库以 --whole-archive 方式整体链入最终的 libpytorch_jni(_lite).so,这就是 Android 端"单一 .so 自包含"的产物形态。
对于 Android 演示应用,官方推荐参考 ExecuTorch 示例仓库中的 DeepLabV3Demo(meta-pytorch/executorch-examples 的 dl3/android/DeepLabV3Demo 目录);问题反馈渠道为官方 Discord。注意 ExecuTorch 是 PyTorch 移动端生态的演进方向,本目录的构建/发布流程面向 PyTorch Android 库本身。
二、引入官方发布产物:Release 与 Nightly
2.1 Release 版本
稳定版发布到 jcenter,Maven 坐标为 org.pytorch:pytorch_android*:<版本>。android/gradle.properties 确认了发布元数据:
VERSION_NAME=2.2.0-SNAPSHOT
GROUP=org.pytorch
MAVEN_GROUP=org.pytorch
SONATYPE_STAGING_PROFILE=orgpytorch
GROUP/MAVEN_GROUP=org.pytorch 与 README 中的依赖坐标一致。引入方式:
repositories {
jcenter()
}
# lite interpreter build
dependencies {
implementation 'org.pytorch:pytorch_android_lite:1.10.0'
implementation 'org.pytorch:pytorch_android_torchvision_lite:1.10.0'
}
# full jit build
dependencies {
implementation 'org.pytorch:pytorch_android:1.10.0'
implementation 'org.pytorch:pytorch_android_torchvision:1.10.0'
}
选型建议:纯手机端 TorchScript 推理、追求包体积与加载性能时选 lite;需要在设备上运行完整 TorchScript(依赖完整 JIT 能力)时选 full jit。
2.2 Nightly(快照)版本
master 分支的夜间构建会发布到 Sonatype Central 快照仓库。由于快照仓库不会出现在默认仓库列表中,必须显式声明:
repositories {
maven {
url "https://central.sonatype.com/repository/maven-snapshots/"
}
}
# lite interpreter build
dependencies {
implementation 'org.pytorch:pytorch_android_lite:1.12.0-SNAPSHOT'
implementation 'org.pytorch:pytorch_android_torchvision_lite:1.12.0-SNAPSHOT'
}
# full jit build
dependencies {
implementation 'org.pytorch:pytorch_android:1.12.0-SNAPSHOT'
implementation 'org.pytorch:pytorch_android_torchvision:1.12.0-SNAPSHOT'
}
README 特别强调:当前 nightly 版本号不是写死的,而是当前目录 android/gradle.properties 中 VERSION_NAME 的值(本文撰写时仓库中该值为 2.2.0-SNAPSHOT)。升级 pytorch 源码树后,快照的语义化版本会随之变化,接入 nightly 时应先读 gradle.properties 确认坐标,避免依赖解析失败。
发布动作本身由 gradle/release.gradle 完成,它引入 com.vanniktech.maven-publish 插件:
apply from: rootProject.file('gradle/android_tasks.gradle')
apply plugin: 'com.vanniktech.maven.publish'
而 pytorch_android/build.gradle 末尾还注册了 sourcesJar 任务并把源码 jar 挂到 archives 工件上,保证发布产物附带源码。
三、从源码构建 PyTorch Android
当你需要定制算子集合、打本地补丁或产出与官方二进制不同的 libtorch 时,可以从源码构建。
3.1 构建流程总览
入口脚本为 ./scripts/build_pytorch_android.sh:
git clone https://github.com/pytorch/pytorch.git
cd pytorch
git submodule update --init --recursive
bash ./scripts/build_pytorch_android.sh
README 描述了脚本的三步工作流,可以在仓库源码中一一对应:
- 为 4 个 Android ABI 构建 libtorch:
armeabi-v7a、arm64-v8a、x86、x86_64,与 android/gradle.properties 第 1 行ABI_FILTERS=armeabi-v7a,arm64-v8a,x86,x86_64保持一致(common.sh 中parse_abis_list函数也硬编码了同一列表并注释要求两边同步); - 建立符号链接:把各 ABI 构建产物链接到
android/pytorch_android/src/main/jniLibs/${abi}(库)和android/pytorch_android/src/main/cpp/libtorch_include/${abi}(头文件),这两个目录正是后续 CMake 构建libpytorch.so时消费的输入(见下文 CMakeLists.txt 中的libtorch_include_DIR与import_static_lib); - 在
android/pytorch_android下执行 gradleassembleRelease。
common.sh 的 build_android 函数完整实现了第 1、2 步,可以逐行印证:
for abi in $(echo "$ABIS_LIST" | tr ',' '\n')
do
ANDROID_BUILD_ROOT="$BUILD_ROOT/build_android_$abi"
ANDROID_ABI="$abi" \
BUILD_ROOT="$ANDROID_BUILD_ROOT" \
"$PYTORCH_DIR/scripts/build_android.sh" \
-DANDROID_CCACHE="$(which ccache)" \
-DUSE_LITE_INTERPRETER_PROFILER="OFF"
ln -s "$ANDROID_BUILD_ROOT/install/lib" "$LIB_DIR/$abi"
ln -s "$ANDROID_BUILD_ROOT/install/include" "$INCLUDE_DIR/$abi"
done
注意两个工程细节:每个 ABI 独立输出到 build_android_$abi,且明确注释"These directories only contain symbolic links"——jniLibs 与 libtorch_include 目录每次构建前会被清空重建(rm -rf 后 mkdir -p),所以它们不应被纳入版本管理。另外脚本通过 $(which ccache) 注入 ANDROID_CCACHE 加速重复编译,并关闭 USE_LITE_INTERPRETER_PROFILER 以减小体积。
3.2 环境要求
脚本需要三个环境变量,均被 common.sh 中的检查函数强制校验:
| 环境变量 | 含义 | 校验逻辑 |
|---|---|---|
ANDROID_HOME |
Android SDK 路径 | check_android_sdk:未设置或路径不存在时直接退出 |
ANDROID_NDK |
Android NDK 路径(README 建议使用 NDK 21.x) | 由底层 scripts/build_android.sh 消费 |
GRADLE_HOME |
gradle 安装路径 | 仓库实际提供了 android/gradlew 包装器(check_gradle 中 GRADLE_PATH=$PYTORCH_DIR/android/gradlew) |
3.3 验证构建产物
构建成功后应能在各模块的 build/outputs/aar/ 下看到 AAR:
$ find pytorch_android/build/ -type f -name *aar
pytorch_android/build/outputs/aar/pytorch_android.aar
pytorch_android_torchvision/build/outputs/aar/pytorch_android.aar
3.4 在 Android 项目中直接使用本地 AAR
AAR 可以通过 flatDir 仓库直接作为 gradle 依赖引入:
allprojects {
repositories {
flatDir {
dirs 'libs'
}
}
}
dependencies {
implementation(name:'pytorch_android', ext:'aar')
implementation(name:'pytorch_android_torchvision', ext:'aar')
...
implementation 'com.facebook.soloader:nativeloader:0.10.5'
implementation 'com.facebook.fbjni:fbjni-java-only:0.2.2'
}
手动补充传递依赖的原因:本地 AAR 方式不会像 Maven 那样自动解析 pom.xml 中的依赖,因此需要显式声明 pytorch_android 的两个传递依赖。这两个版本的真值来源是 android/build.gradle 的根项目 ext 定义:
fbjniJavaOnlyVersion = "0.2.2"
soLoaderNativeLoaderVersion = "0.10.5"
以及 pytorch_android/build.gradle 中的依赖声明:
dependencies {
implementation 'com.facebook.fbjni:fbjni-java-only:' + rootProject.fbjniJavaOnlyVersion
implementation 'com.facebook.soloader:nativeloader:' + rootProject.soLoaderNativeLoaderVersion
...
}
即 nativeloader:0.10.5 负责在运行时 dlopen 各 ABI 的 .so,fbjni-java-only:0.2.2 提供 Java/C++ 桥接所需的 FBJavaClass 等基础设施——CMake 侧的 JNI 目标也显式链接了 fbjni(set(fbjni_DIR .../../libs/fbjni/) 后 add_subdirectory)。
另一个值得注意的 AAR 定制点:pytorch_android/build.gradle 在 afterEvaluate 中注册 addHeadersToAar 任务,把 src/main/cpp/libtorch_include/<abi> 头文件打进 AAR 的 headers/ 目录(getLibtorchHeadersDir 取 ABI_FILTERS 的第一个 ABI)。这直接服务于下一节的"原生构建链接预编译 libtorch"场景。
四、从 Maven AAR 链接预编译 libtorch 到自研 Native 代码
即使不自行编译 libtorch,也可以在自己的 CMake 原生构建中使用 libtorch C++ API——直接解包官方 AAR 中的头文件与预编译库即可。
4.1 Gradle 侧配置
在项目的 build.gradle 中新增一个专用 configuration,并注册解包任务:
android {
...
configurations {
extractForNativeBuild
}
...
compileOptions {
externalNativeBuild {
cmake {
arguments "-DANDROID_STL=c++_shared"
}
}
}
...
externalNativeBuild {
cmake {
path "CMakeLists.txt"
}
}
}
dependencies {
extractForNativeBuild('org.pytorch:pytorch_android:1.10.0')
}
task extractAARForNativeBuild {
doLast {
configurations.extractForNativeBuild.files.each {
def file = it.absoluteFile
copy {
from zipTree(file)
into "$buildDir/$file.name"
include "headers/**"
include "jni/**"
}
}
}
}
tasks.whenTaskAdded { task ->
if (task.name.contains('externalNativeBuild')) {
task.dependsOn(extractAARForNativeBuild)
}
}
要点解释(与 README 的说明一一对应):
- AAR 内部布局:
pytorch_android.aar内headers/目录存放 libtorch 头文件,jni/$ANDROID_ABI/存放各 ABI 的 native 库(对应jniLibs.srcDirs = ['src/main/jniLibs']与上文addHeadersToAar注入的产物); - 必须使用
c++_shared:PyTorch 原生库本身以ANDROID_STL=c++_shared构建,若你的模块用其它 STL 变体,会出现 C++ 运行时不兼容/重复符号问题。README 明确要求"should useANDROID_STL=c++_sharedto have only one loaded binary of STL"; - 任务依赖注入:
tasks.whenTaskAdded让所有externalNativeBuild*任务自动依赖解包任务,保证 CMake 配置时 headers/jni 已就位。
4.2 CMake 侧配置
# Relative path of gradle build directory to CMakeLists.txt
set(build_DIR ${CMAKE_SOURCE_DIR}/build)
file(GLOB PYTORCH_INCLUDE_DIRS "${build_DIR}/pytorch_android*.aar/headers")
file(GLOB PYTORCH_LINK_DIRS "${build_DIR}/pytorch_android*.aar/jni/${ANDROID_ABI}")
set(BUILD_SUBDIR ${ANDROID_ABI})
target_include_directories(${PROJECT_NAME} PRIVATE
${PYTORCH_INCLUDE_DIRS}
)
find_library(PYTORCH_LIBRARY pytorch_jni
PATHS ${PYTORCH_LINK_DIRS}
NO_CMAKE_FIND_ROOT_PATH)
find_library(FBJNI_LIBRARY fbjni
PATHS ${PYTORCH_LINK_DIRS}
NO_CMAKE_FIND_ROOT_PATH)
target_link_libraries(${PROJECT_NAME}
${PYTORCH_LIBRARY}
${FBJNI_LIBRARY})
注意事项:
build_DIR的取值依赖CMakeLists.txt与build.gradle同目录这一默认布局;若你的 CMakeLists 位于子目录(例如src/main/cpp/),需按相对位置调整该路径——这是 README 明确提示的常见坑;find_library使用pytorch_jni这一名称,对应 full jit 构建的 JNI 库;NO_CMAKE_FIND_ROOT_PATH 防止 CMake 在 sysroot 中误匹配;fbjni同样从 AAR 的 jni 目录发现,避免系统路径污染。
4.3 调用 LibTorch C++ API 的最小示例
链接完成后即可在 native 代码中使用 libtorch,README 给出的完整示例:
#include <string>
#include <ATen/NativeFunctions.h>
#include <torch/script.h>
namespace pytorch_testapp_jni {
namespace {
struct JITCallGuard {
c10::InferenceMode guard;
torch::jit::GraphOptimizerEnabledGuard no_optimizer_guard{false};
};
}
void loadAndForwardModel(const std::string& modelPath) {
JITCallGuard guard;
torch::jit::Module module = torch::jit::load(modelPath);
module.eval();
torch::Tensor t = torch::randn({1, 3, 224, 224});
c10::IValue t_out = module.forward({t});
}
}
JITCallGuard 这个"特殊设置"不可省略:c10::InferenceMode 将本次推理置于推断模式以跳过 autograd 元数据开销;torch::jit::GraphOptimizerEnabledGuard no_optimizer_guard{false} 关闭图优化器,因为移动端加载的 TorchScript 模型通常不需要、也不应再触发图优化。README 同时提醒:该 setup 可能随版本演进,最新形态应以 android/pytorch_android/src/main/cpp/pytorch_jni_jit.cpp 中官方 JNI 层的调用惯例为准。从源码结构看,该文件正是 full jit 构建(BUILD_LITE_INTERPRETER=0)时编译进 libpytorch_jni.so 的桥接实现,与 lite 构建的 pytorch_jni_lite.cpp 形成对照。
五、在 x86 模拟器上运行 instrumented tests
官方 CI 通过 android/run_tests.sh 在 x86 模拟器上跑 instrumented tests。脚本逻辑值得借鉴为本地验证流程:
- 检查
ANDROID_HOME并定位adb($ANDROID_HOME/platform-tools/adb); - 用
adb devices统计设备数,若为 0(DEVICES_COUNT -eq 1,输出只含表头)则打印完整的手动起机步骤并退出:- 安装系统镜像:
sdkmanager "system-images;android-25;google_apis;x86"(需要代理时可加--proxy=http --proxy_host=fwdproxy --proxy_port=8080); - 创建 AVD:
avdmanager create avd --name "x86_android25" --package "system-images;android-25;google_apis;x86"; - 无头无音频启动:
emulator -avd x86_android25 -no-audio -no-window; - 再
adb devices应看到emulator-5554 device;
- 安装系统镜像:
adb wait-for-device shell 'while [[ -z $(getprop sys.boot_completed) ]]; do sleep 1; done;'等待系统 boot 完成;- 执行
gradlew -PABI_FILTERS=x86 -p android connectedAndroidTest,并用 common.sh 中的retry函数做最多 3 次退避重试(sleep 10/20/40 秒)——注释说明该测试一轮约 10 分钟;失败时会提示参考test/mobile/model_test修复移动侧测试。
-PABI_FILTERS=x86 参数对应 ndk { abiFilters ABI_FILTERS.split(",") } 的 gradle 属性覆写,让测试只针对 x86 ABI 构建,与模拟器架构匹配。
六、API 文档与延伸阅读
- PyTorch Android API 的 JavaDoc 见官方 Javadoc;
- 模块根构建参数(minSdk 21 / targetSdk 28 / AGP 4.1.2 等)见 android/build.gradle:
ext {
minSdkVersion = 21
targetSdkVersion = 28
compileSdkVersion = 28
buildToolsVersion = '28.0.3'
...
}
- android/gradle.properties 中的
nativeLibsDoNotStrip=false控制 release 是否保留符号(pytorch_android/build.gradle的packagingOptions依据它决定是否doNotStrip "**/*.so",置为true时会打印 WARNING 并保留 debug 符号,仅建议调试期使用); android.useAndroidX=true/android.enableJetifier=true表明工程运行在 AndroidX + Jetifier 体系下。
七、总结
| 场景 | 推荐路径 | 关键配置 |
|---|---|---|
| 常规接入 | Maven 依赖 | jcenter + org.pytorch:pytorch_android(_lite):<ver> |
| 尝鲜 master | Nightly 快照 | 显式 Sonatype snapshots 仓库 + gradle.properties 中 VERSION_NAME 对应的 -SNAPSHOT 坐标 |
| 定制算子/本地补丁 | 源码构建 | scripts/build_pytorch_android.sh + 三个环境变量,产物为本地 AAR |
| 自研 native 代码 + 官方 libtorch | AAR 解包 + CMake | extractForNativeBuild 任务 + ANDROID_STL=c++_shared + find_library(pytorch_jni) |
| 本地回归验证 | 模拟器 instrumented tests | run_tests.sh:x86 AVD + -PABI_FILTERS=x86 connectedAndroidTest |
PyTorch Android 集成的工程核心可以概括为一句话:同一套源码由 BUILD_LITE_INTERPRETER 开关分化为 lite/jit 两种 JNI 目标,libtorch 静态库以 --whole-archive 方式整体链入单一 libpytorch_jni(_lite).so,再经 AAR 打包(含 jniLibs、headers)交付给 Java 层(nativeloader 加载 + fbjni 桥接)。掌握这条主线后,无论是选 Maven 坐标、读 gradle.properties 确认快照版本,还是排查 CMake 链接失败(STL 变体、build_DIR 相对路径、find_library 名称),都能落到源码层面的确切依据上。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0622
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00