尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

【Bug已解决】Android: Linux-targeted libonnxruntime.so (from runtimes/linux-arm64) gets bundled into APK…

【Bug已解决】Android: Linux-targeted libonnxruntime.so (from runtimes/linux-arm64) gets bundled into APK… 【Bug已解决】Android: Linux-targeted libonnxruntime.so (from runtimes/linux-arm64) gets bundled into APK instead of the Android AARs library 解决方案一、现象长什么样在 Android 项目里集成 ONNX Runtime通过官方 AAR内含libonnxruntime.so的arm64-v8a/armeabi-v7a版本构建 APK 后安装运行报UnsatisfiedLinkError或加载了错误的.so排查发现 APK 里打包的竟是Linux 主机用的libonnxruntime.so来自runtimes/linux-arm64是 x86_64 Linux 的库而不是 Android AAR 里的 Android 版.so。现象# 现象 A运行时加载失败 # java.lang.UnsatisfiedLinkError: dlopen failed: ... # /vendor/lib64/libonnxruntime.so has unexpected e_machine / wrong ELF class # 其实是 Linux x86_64 的 ELFAndroid arm64 加载器拒绝 # 现象 BAPK 里混入了 linux-arm64 的 .so # unzip -l app.apk | grep onnxruntime # 看到 libonnxruntime.so 来自 runtimes/linux-arm64x86_64不是 AAR 的 # 现象 C只在特定 gradle 配置/依赖顺序触发 # 当项目同时依赖了某个拉入 runtimes/linux-arm64 的包且它的 .so # 被 gradle 的 jniLibs 合并规则优先选中就覆盖掉 AAR 的 Android .so最坑的是现象 C正常只依赖 AAR 时没问题一旦项目里某个其他依赖或本地runtimes/目录把 linux-arm64 的.so也带进 jniLibs 合并gradle 的“后者覆盖/按字母序”规则可能让 Linux 版胜出APK 里就是错的库且只有真机运行才暴露。二、背景Android 的 native 库.so通过 AAR 的jni/目录提供构建时 gradle 把各 AAR 的jni/abi/libxxx.so合并进 APK。ONNX Runtime 的 Android AAR 内含jni/arm64-v8a/libonnxruntime.so等 Android 版。问题在于开发机上的 ORT 仓库里还有runtimes/linux-arm64/libonnxruntime.so这是给开发机 Linux x86_64用的名字叫linux-arm64但其实是 x86_64 Linux 二进制或者开发者误把主机库当 arm 库。如果构建脚本/某个依赖把runtimes/目录也加进了jniLibs.srcDirsgradle 合并时就会把这个 Linux.so带进 APK且因合并顺序/规则覆盖了 AAR 的 Android 版导致 APK 打包了错误的库。这是 Android 打包/原生库合并审查里典型的坑jniLibs 源目录包含了非 Android 的.so合并时被优先选中覆盖了正确的 AAR 库。三、根因runtimes/linux-arm64被加进 jniLibs 源构建脚本误把开发机用的 Linux.so目录设为jniLibs.srcDirs导致它被合并进 APK现象 B。合并覆盖规则让 Linux 版胜出gradle 多源合并时后添加/字母序靠后的源覆盖先前的Linux.so盖掉了 AAR 的 Android.so现象 C。缺少 APK 内容校验CI 没检查 APK 里.so的架构ELF e_machine错误库长期存在。本质是jniLibs 源误含非 Android 的 Linux.so且合并覆盖规则使其胜出且缺 APK 架构校验。四、最小可运行复现下面用 Python 模拟“jniLibs 多源合并Linux .so 覆盖 AAR 的 Android .so”def merge_jnilibs_buggy(aar_libs, extra_src_dirs): buggy: 后者覆盖前者Linux .so 盖掉 AAR 的 Android .so。 result {} # AAR 先放 for abi, so in aar_libs.items(): result[abi] so # extra 源含 linux-arm64 的 x86_64 .so后放覆盖 for d in extra_src_dirs: for abi, so in d.items(): result[abi] so # 覆盖 return result aar {arm64-v8a: android_libonnxruntime.so} linux_dir {arm64-v8a: linux_x86_64_libonnxruntime.so} # 名字同 abi 但其实是 Linux x86_64 out merge_jnilibs_buggy(aar, [linux_dir]) print(buggy bundled:, out[arm64-v8a]) # linux_x86_64 版错 def merge_jnilibs_fixed(aar_libs, extra_src_dirs): fixed: 拒绝非 Android 架构的 .so 进入合并。 result dict(aar_libs) for d in extra_src_dirs: for abi, so in d.items(): if linux in so or x86_64 in abi: continue # 跳过非 Android 库 result[abi] so return result out2 merge_jnilibs_fixed(aar, [linux_dir]) print(fixed bundled:, out2[arm64-v8a]) # android 版对buggy被 Linux 版覆盖fixed跳过非 Android 库保留 AAR 版。五、解决方案第一层最小直接修复最小修复构建脚本里不要把runtimes/下的 Linux.so目录加进jniLibs.srcDirs且对进入合并的.so做架构白名单过滤只允许 Android abi// 修正只把 AAR 的 jni 作为 native 库源排除开发机 runtimes 目录 android { sourceSets { main { // 不要写 jniLibs.srcDirs runtimes/linux-arm64 jniLibs.srcDirs [src/main/jniLibs] // 仅 AAR 提供 } } }并在合并时过滤任何含linux/x86_64的.so不进入 APK。这一层改动最小移除 Linux 目录 架构白名单APK 恢复正确的 Android.so。但依赖“每处 jniLibs 配置都审查”下看第二层。六、解决方案第二层结构性改进把“APK native 库必须来自 Android AAR、排除非 Android .so”固化成单一事实来源。下面这个 dataclass 集中管理合并契约from dataclasses import dataclass, field from typing import Dict, List dataclass class OrtAndroidSoPolicy: 单一事实来源Android APK native 库合并契约。 ALLOWED_ABIS (arm64-v8a, armeabi-v7a, x86, x86_64) def merge(self, aar_libs: Dict[str, str], extra_src_dirs: List[Dict[str, str]]) - Dict[str, str]: result {k: v for k, v in aar_libs.items() if k in self.ALLOWED_ABIS} for d in extra_src_dirs: for abi, so in d.items(): # 只允许 Android abi 且非 Linux 主机库进入 if abi in self.ALLOWED_ABIS and linux not in so: result[abi] so return result def assert_android_only(self, bundled: Dict[str, str]) - None: for abi, so in bundled.items(): if abi not in self.ALLOWED_ABIS or linux in so: raise AssertionError(fnon-Android .so bundled: {abi}/{so})这一层的关键收益abi 白名单只接受 Android abiLinux 主机库被排除AAR 优先以 AAR 库为基础extra 源仅补充合法项断言assert_android_only校验 APK 内无 Linux.so单一事实来源所有 APK native 库合并约定收口在OrtAndroidSoPolicy。七、解决方案第三层断言 / CI 守护把第二层钉成 pytest挂进 CI作为 APK 内容门禁import pytest from your_package.ort_android_so import OrtAndroidSoPolicy def test_linux_so_excluded(): # 断言 1linux-arm64 的 .so 被排除保留 AAR 的 Android 版 p OrtAndroidSoPolicy() out p.merge({arm64-v8a: android_libonnxruntime.so}, [{arm64-v8a: linux_x86_64_libonnxruntime.so}]) assert out[arm64-v8a] android_libonnxruntime.so def test_only_android_abis(): # 断言 2最终只含 Android abi p OrtAndroidSoPolicy() out p.merge({arm64-v8a: a.so, x86: b.so}, []) assert set(out.keys()) set(p.ALLOWED_ABIS) def test_non_android_caught(): # 断言 3含 linux 的 .so 必被断言抓出 p OrtAndroidSoPolicy() with pytest.raises(AssertionError): p.assert_android_only({arm64-v8a: linux_libonnxruntime.so}) def test_aar_preferred(): # 断言 4AAR 库始终保留不被 extra 覆盖除非 extra 合法 p OrtAndroidSoPolicy() out p.merge({arm64-v8a: aar.so}, [{armeabi-v7a: extra_legal.so}]) assert out[arm64-v8a] aar.so assert out[armeabi-v7a] extra_legal.so四条断言从“Linux 排除”“仅 Android abi”“非 Android 被抓”“AAR 优先”四面把打包错误钉死在 CI。八、排查清单Android 集成 ORT 运行报UnsatisfiedLinkError/加载错库时unzip -l app.apk | grep onnxruntime看.so来自哪——是否混入了runtimes/linux-arm64的 Linux 库现象 B。构建脚本的jniLibs.srcDirs是否误含开发机runtimes/目录有就移除现象 C。合并顺序是否让 Linux.so覆盖了 AAR 的 Android.so加架构白名单过滤。用第二层OrtAndroidSoPolicyabi 白名单 AAR 优先 非 Android 断言。加第三层 pytest断言“Linux 排除、仅 Android abi、非 Android 被抓、AAR 优先”。Android 打包的 native 库必须只来自 AAR 的 Android 版任何runtimes/linux-*主机库都不能进 jniLibs。九、小结Android 集成 ORT 打包错库本质是构建脚本把开发机用的runtimes/linux-arm64x86_64 Linux.so加进了jniLibs.srcDirsgradle 合并时被其覆盖 AAR 的 Android 版.so导致 APK 里是 Linux 库、真机加载失败且缺 APK 架构校验。修复分三层——第一层移除 Linux 目录 加 abi 白名单第二层用OrtAndroidSoPolicy这个 dataclass 把“仅 Android abi AAR 优先 非 Android 断言”收口成单一事实来源第三层用四条 pytest 把“Linux 排除、仅 Android abi、非 Android 被抓、AAR 优先”钉死在 CI。核心心法Android 打包的 native 库必须只来自 AAR 的 Android 版任何runtimes/linux-*主机库都不能进 jniLibs否则会被合并覆盖成错误架构。
返回列表