首页
/ PyTorch Android 构建与集成指南:从 Maven 依赖到源码编译 libpytorch.so 的完整实践

PyTorch Android 构建与集成指南:从 Maven 依赖到源码编译 libpytorch.so 的完整实践

2026-09-04 21:07:47作者:董灵辛Dennis

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_INTERPRETERABI_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.javaLiteNativePeer.java,并只保留 full jit 的 instrumented tests;默认(非 0)则构建 lite 版本。这就是为什么 README 中 Maven 依赖要分 *_lite 与常规两种坐标——它们由同一套源码、同一个开关产出。

此外,CMakeLists 中对 Android 平台启用了 Vulkan 支持(if(ANDROID_ABI) set(USE_VULKAN ON)),并通过 import_static_liblibtorchlibtorch_cpulibc10libnnpacklibXNNPACKlibpytorch_qnnpacklibpthreadpoollibeigen_blaslibcpuinfolibclog 等静态库以 --whole-archive 方式整体链入最终的 libpytorch_jni(_lite).so,这就是 Android 端"单一 .so 自包含"的产物形态。

对于 Android 演示应用,官方推荐参考 ExecuTorch 示例仓库中的 DeepLabV3Demo(meta-pytorch/executorch-examplesdl3/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.propertiesVERSION_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 描述了脚本的三步工作流,可以在仓库源码中一一对应:

  1. 为 4 个 Android ABI 构建 libtorcharmeabi-v7aarm64-v8ax86x86_64,与 android/gradle.properties 第 1 行 ABI_FILTERS=armeabi-v7a,arm64-v8a,x86,x86_64 保持一致(common.shparse_abis_list 函数也硬编码了同一列表并注释要求两边同步);
  2. 建立符号链接:把各 ABI 构建产物链接到 android/pytorch_android/src/main/jniLibs/${abi}(库)和 android/pytorch_android/src/main/cpp/libtorch_include/${abi}(头文件),这两个目录正是后续 CMake 构建 libpytorch.so 时消费的输入(见下文 CMakeLists.txt 中的 libtorch_include_DIRimport_static_lib);
  3. android/pytorch_android 下执行 gradle assembleRelease

common.shbuild_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"——jniLibslibtorch_include 目录每次构建前会被清空重建(rm -rfmkdir -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_gradleGRADLE_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 的 .sofbjni-java-only:0.2.2 提供 Java/C++ 桥接所需的 FBJavaClass 等基础设施——CMake 侧的 JNI 目标也显式链接了 fbjni(set(fbjni_DIR .../../libs/fbjni/)add_subdirectory)。

另一个值得注意的 AAR 定制点:pytorch_android/build.gradleafterEvaluate 中注册 addHeadersToAar 任务,把 src/main/cpp/libtorch_include/<abi> 头文件打进 AAR 的 headers/ 目录(getLibtorchHeadersDirABI_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.aarheaders/ 目录存放 libtorch 头文件,jni/$ANDROID_ABI/ 存放各 ABI 的 native 库(对应 jniLibs.srcDirs = ['src/main/jniLibs'] 与上文 addHeadersToAar 注入的产物);
  • 必须使用 c++_shared:PyTorch 原生库本身以 ANDROID_STL=c++_shared 构建,若你的模块用其它 STL 变体,会出现 C++ 运行时不兼容/重复符号问题。README 明确要求"should use ANDROID_STL=c++_shared to 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.txtbuild.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。脚本逻辑值得借鉴为本地验证流程:

  1. 检查 ANDROID_HOME 并定位 adb$ANDROID_HOME/platform-tools/adb);
  2. 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
  3. adb wait-for-device shell 'while [[ -z $(getprop sys.boot_completed) ]]; do sleep 1; done;' 等待系统 boot 完成;
  4. 执行 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.gradlepackagingOptions 依据它决定是否 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.propertiesVERSION_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 名称),都能落到源码层面的确切依据上。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
527
590
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
904
1.82 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
889
5.78 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.52 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.33 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
982
502
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384