☰
JNA Android 开发环境搭建与交叉编译构建指南
2026/9/25 2:13:09 网站建设 项目流程
  • 系统编程
  • 后端

【免费下载链接】jna

Java Native Access

项目地址:https://gitcode.com/gh_mirrors/jn/jna
点击查看免费下载

导读

本文基于 JNA 仓库中的 www/AndroidDevelopmentEnvironment.md 整理扩充,完整讲解如何在 Linux 主机上借助 Android NDK 为 ARM、x86、MIPS 等架构交叉编译 JNA 的核心原生库libjnidispatch.so,并说明如何将产物dist/jna.jar、dist/jna-platform.jar正确接入 Android 应用(包括 Android Studio/Gradle、android-maven-plugin 与 Eclipse 插件等不同工程形态)。读完本文,你将掌握 JNA Android 环境的全部前置条件、ant -Dos.prefix=android-*系列构建命令、NDK 工具链与NDK_PLATFORM的配置语义,以及构建脚本(native/Makefile、native/build.xml)背后的源码级工作机制,能够独立完成一次完整的 Android 目标构建与部署。

为什么 JNA 需要单独的 Android 构建环境

JNA 的核心是一个名为jnidispatch的 JNI 原生库,其 C 源码位于仓库 native/ 目录(主要文件包括dispatch.c、callback.c、dll-callback.c)。与普通桌面平台不同,Android 的 APK 运行在 Dalvik/ART 虚拟机上,原生库必须被打包进 APK 并通过 APK 的lib/<abi>/目录加载。因此:

  1. 主机平台(如 Linux x86-64)无法直接运行或加载 Android 的原生库,测试必须在目标平台(真机或模拟器)上执行,而不能在构建主机上执行;
  2. Android 原生库的 ABI(如 armeabi、armeabi-v7a、arm64-v8a、x86、x86_64、mips、mips64)与桌面平台的 ELF 结构不同,必须使用 Android NDK 提供的交叉编译工具链来构建;
  3. 从源码结构看,JNA 在运行时通过 Platform.java 检测 JVM 是否为 Android(java.vm.name包含dalvik),并自动设置jna.nounpack=true,因为 Android 上原生库必须与 APK 一起打包(见 Platform.java)。

一、环境准备:SDK/NDK、PATH 与 NDK_PLATFORM

按照 www/AndroidDevelopmentEnvironment.md 的说明,Android 构建需要满足以下前置条件:

1. 安装 Android SDK 与 NDK

  • 下载并安装 Android NDK(仓库文档与 native/Makefile 中记录的版本示例为 NDK r10e/r12b,平台目录为platforms/android-21;实际使用请选择与目标 Android 版本匹配的 NDK,并相应调整下面的路径);
  • NDK 提供交叉编译所需的toolchains/<target>-4.9/prebuilt/linux-x86_64/bin/工具链,以及platforms/android-<api>/arch-<abi>/下的系统头文件与库。

2. 将 NDK 工具链加入 PATH

native/Makefile中 Android 分支会直接以$(PREFIX)gcc、$(PREFIX)ranlib、$(PREFIX)strip等形式调用编译器与工具,因此这些工具必须出现在PATH中。以 NDK r12b 为例,完整的环境配置如下(原文示例):

export NDK_PLATFORM=/home/matthias/bin/android-ndk-r12b/platforms/android-21 export PATH=$NDK_PLATFORM/../../toolchains/aarch64-linux-android-4.9/prebuilt/linux-x86_64/bin/:$PATH export PATH=$NDK_PLATFORM/../../toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/:$PATH export PATH=$NDK_PLATFORM/../../toolchains/mips64el-linux-android-4.9/prebuilt/linux-x86_64/bin/:$PATH export PATH=$NDK_PLATFORM/../../toolchains/mipsel-linux-android-4.9/prebuilt/linux-x86_64/bin/:$PATH export PATH=$NDK_PLATFORM/../../toolchains/x86-4.9/prebuilt/linux-x86_64/bin/:$PATH export PATH=$NDK_PLATFORM/../../toolchains/x86_64-4.9/prebuilt/linux-x86_64/bin/:$PATH

注意上述工具链前缀与 JNA 的架构命名一一对应,见下表(依据 native/Makefile):

JNA 架构(os.prefix)工具链前缀(PREFIX)链接目标 HOST
android-armarm-linux-androideabi-arm-linux-eabi
android-armv7arm-linux-androideabi-arm-linux-eabi
android-aarch64aarch64-linux-android-aarch64-linux-android
android-x86i686-linux-android-i686-linux-android
android-x86-64x86_64-linux-android-x86_64-linux-android
android-mipsmipsel-linux-android-mipsel-linux-android
android-mips64mips64el-linux-android-mips64el-linux-android

3. 设置 NDK_PLATFORM 环境变量

NDK_PLATFORM指向 NDK 中某个platforms/android-<api>目录。native/Makefile通过它计算:

  • SYSROOT=$(NDK_PLATFORM)/arch-$(AARCH),其中AARCH由架构推导(arm→arm、armv7→arm、aarch64→arm64、x86→x86、x86-64→x86_64、mips→mips、mips64→mips64);
  • C 头文件搜索路径-I"$(NDK_PLATFORM)/arch-$(AARCH)/usr/include";
  • 链接库搜索路径-L"$(NDK_PLATFORM)/arch-$(AARCH)$(ALIBDIR)/"(32 位为/usr/lib,aarch64/x86-64/mips64 为/usr/lib64),并以-nostdlib方式显式链接-lgcc -lc -ldl -lm。

若未显式设置,native/Makefile会使用默认值:NDK?=/Developer/Applications/android-ndk-r10e、NDK_PLATFORM?=$(NDK)/platforms/android-21。因此推荐通过环境变量显式指定,避免默认路径与本地安装不符。

二、交叉编译构建:ant -Dos.prefix 全架构演练

环境变量配置好后,即可在仓库根目录执行构建。单架构构建命令为(原文核心命令):

ant -Dos.prefix=android-arm dist

-Dos.prefix是 JNA 构建系统识别目标平台的关键参数。native/build.xml中会将android-*前缀映射为make.OS=OS=android,并根据后缀把ARCH设置为arm、armv7、aarch64、x86-64、x86、mips、mips64(见 native/build.xml),随后调用native/Makefile完成libjnidispatch.so与测试库的编译,再把产物打包成${os.prefix}.jar(例如android-arm.jar),最终由dist目标统一复制到dist/目录(见 build.xml)。

原文示例为一次性构建全部 7 个 Android 架构:

ant -Dos.prefix=android-aarch64 ant -Dos.prefix=android-armv7 ant -Dos.prefix=android-arm ant -Dos.prefix=android-mips64 ant -Dos.prefix=android-mips ant -Dos.prefix=android-x86-64 ant -Dos.prefix=android-x86

逐个执行以上命令(或按需只构建应用所需架构),即可生成与仓库 lib/native/ 下预置产物同名的一系列android-*.jar。

各架构的编译参数差异(源码级)

native/Makefile的 Android 分支针对不同架构设置了不同的编译选项(native/Makefile):

  • android-arm:-mthumb-interwork -march=armv5te -mtune=xscale -msoft-float -fstack-protector,兼容老一代 ARM 设备;
  • android-armv7:-march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16 -Wl,--fix-cortex-a8,针对 Cortex-A8 的硬件缺陷做链接修正;
  • android-aarch64:64 位 ARM 目标,AARCH=arm64,库路径为/usr/lib64;
  • android-x86:-march=i686;android-x86-64:-m64。

所有 Android 架构统一启用:-fpic -ffunction-sections -funwind-tables -fno-short-enums,并定义-DFFI_STATIC_BUILD -DNO_JAWT -DNO_WEAK_GLOBALS -DFFI_MMAP_EXEC_WRIT=1 -DFFI_MMAP_EXEC_SELINUX=0 -Dmalloc_getpagesize='getpagesize()';链接参数包含-Wl,-shared,-Bsymbolic -Wl,--build-id=sha1 -Wl,-z,max-page-size=16384 -Wl,-z,common-page-size=16384。

libffi 的静态编译

JNA 的闭包(Closure)机制依赖 libffi。Android 分支中native/Makefile会以--host=<HOST>交叉配置仓库内置的 native/libffi 源码树,并使用--enable-static --disable-shared --with-pic=yes生成静态 libffi,再与dispatch.o、callback.o一起链接进libjnidispatch.so。这正是 Android 上 JNA 可以独立携带全部依赖、无需系统预装 libffi 的原因。

三、测试:必须在目标平台上运行

原文档明确指出:Tests must be run on the target platform, not the build platform.这是因为交叉编译出的libjnidispatch.so面向 Android ABI,无法在桌面 JVM 中加载。

仓库为此提供了android-test-setup目标(build.xml),用于将测试配置到 Android 模拟器/真机环境(通过shared目录作为设备存储卡挂载点)。结合源码可推断典型流程为:

  1. 在主机上执行ant android-test-setup完成测试类编译与准备;
  2. 将测试 jar、jnidispatch.so、testlib.so等推送/挂载到模拟器或真机可访问的存储目录;
  3. 在设备端运行 JUnit 测试,测试结果再回传主机分析。

与桌面端ant test(fork JVM 在本机跑)不同,Android 场景必须依赖设备侧执行,这也是 JNA 工程对 Android 测试的基本约束。

四、将构建产物集成进 Android 应用

1. 基础用法:jna.jar 与 jna-platform.jar

构建完成后,将dist/jna.jar(核心库)和/或dist/jna-platform.jar(平台封装库,源码位于 contrib/platform)加入应用即可。dist目标会把 lib/native/ 下各架构的android-*.jar(内含对应 ABI 的libjnidispatch.so)一并复制到dist/。

JNA 运行时通过 Platform.java 计算原生库资源前缀:Android 上arch为 arm 系时统一归一为arm,最终资源前缀形如android-arm、android-x86等,从而从 jar 内com/sun/jna/<prefix>/libjnidispatch.so路径加载库。

2. Android Studio / Gradle:直接使用 AAR

仓库aar目标(build.xml)会把各架构的原生库按标准 ABI 目录打进 AAR 包:

JNA 产物AAR 内 jni 路径(ABI)
android-aarch64.jarjni/arm64-v8a
android-arm.jarjni/armeabi
android-armv7.jarjni/armeabi-v7a
android-mips.jarjni/mips
android-mips64.jarjni/mips64
android-x86-64.jarjni/x86_64
android-x86.jarjni/x86

AAR 还包含classes.jar、AndroidManifest.xml(package 为com.sun.jna,并带有由 ant 工具类 CalcAndroidVersion.java 计算的versionCode)以及构建所需的占位R.txt与字符串资源。将 AAR 作为依赖后,Gradle 会自动为各 ABI 抽取对应原生库,无需手动干预。

CalcAndroidVersion的版本号计算规则为(见 CalcAndroidVersion.java):

androidVersion = 1000000 * major + 10000 * minor + 100 * revision + (release 时为 99)

3. android-maven-plugin:jna.jar 可直接使用

原文档说明:如果使用 android-maven-plugin,jna.jar可以原样使用,原生库会被自动复制进项目。其依据是 JNA 将原生库以com/sun/jna/<os.prefix>/libjnidispatch.so形式内嵌在 jar 的 classpath 资源中,Maven 的 Android 打包流程会把 jar 内的.so资源抽取并归入 APK 的lib/<abi>/目录。

4. Google Eclipse 插件:需要手动处理

使用 Google 的 Eclipse Android 插件时,原生库不会自动归类到 APK 的 ABI 目录。原文档明确要求:

必须手动从jna.jar的lib/armeabi中移除libjnidispatch.so,并将其放入你项目工程的libs/armeabi目录。

即在 Eclipse 工程中,jna.jar内的.so需要被解压出来放到工程的libs/armeabi/(对应的 ABI 目录)下,构建时由插件将其打进 APK。这一约束的根本原因与前述相同:Android 只从 APK 的lib/<abi>/目录加载原生库。

五、Android 平台运行时行为小结

构建与部署之外,JNA 在 Android 上还有一些与桌面不同的运行时行为,可以从 Platform.java 中得到印证:

  • 平台识别:通过os.name以Linux开头且java.vm.name含dalvik判定为 Android(ANDROID=8);
  • 禁用解包:Android 上自动设置jna.nounpack=true,原生库随 APK 分发,不再在运行时解包到临时目录;
  • 无 AWT:HAS_AWT在 Android 上为false,相关桌面特性不可用;
  • C 库名:Android 与 Linux 一致使用c/m(Windows 才使用msvcrt/msvcrt与 CE 的coredll)。

结语

从环境变量配置、NDK 工具链 PATH 设置,到ant -Dos.prefix=android-*逐架构交叉编译,再到dist/jna.jar、dist/jna-platform.jar、AAR 以及 Maven/Eclipse 工程的不同集成方式,JNA 的 Android 开发链路清晰且完全由仓库的 native/Makefile、native/build.xml、build.xml 与 Platform.java 支撑。掌握本文所述方法后,你既能独立完成多 ABI 的 JNA Android 构建,也能理解各集成方式背后的打包与加载机制,从而在实际项目中避免“原生库未打进 APK”或“ABI 目录不匹配”等典型问题。更多相关指引可继续查阅仓库 README.md 中的 Android 开发环境说明。

  • 系统编程
  • 后端

【免费下载链接】jna

Java Native Access

项目地址:https://gitcode.com/gh_mirrors/jn/jna
点击查看免费下载

相关推荐

上一篇:Headscale-WebUI常见问题解答:解决部署与使用中的10个痛点
下一篇:StyleX 版本演进深度解析:从 0.11 到 0.19 的核心 API、编译优化与工具链变迁

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询