直接回答
原生库有依赖关系和初始化行为,应让它们在应用的 runtime 设计中可观察。Android JNI 官方指南介绍使用 System.loadLibrary 加载 native 代码,并建议放在静态初始化器或其他有意安排的早期所有者中。关键不是“多加载几个库直到能跑”,而是确定哪个库提供某个方法、它依赖什么,以及最早失败在哪一步。模型 runtime 可能依赖多个共享对象,应用层还可能有 JNI 回调或厂商 delegate。文件缺失、动态依赖无法解析、重复初始化或 native 方法注册失败会表现为不同错误,不能合并为一个模糊的“模型失败”。 改代码前先画依赖图。记录不带平台前缀的库名、由哪个模块或依赖打包,以及首次调用路径。还要区分应用包内库与系统提供或仅特定后端需要的可选库。不能假设每台手机上都有某个库,也不能为了压制警告就把可选组件声明为必需。官方 System.loadLibrary API 说明传入的名字格式;NDK JNI 说明加载和注册行为。但这些资料不能证明某个 Cove 制品具体包含哪些 native 对象。 选择一个安全的初始化所有者,让它执行一次有意的初始化并保留结果状态。不要把 System.loadLibrary 放在 Composable 副作用、反复创建的屏幕构造函数或多个并发入口中。由应用或 runtime adapter 管理初始化,记录成功或失败,并阻止后续任务静默重复执行尚未清理完的初始化。如果允许重试,先定义要重置哪些状态和资源。所有者不能吞掉 UnsatisfiedLinkError;应保留首个失败类型及去内容化阶段标识,从而分辨“找不到库”“找到但无法打开”“JNI 注册失败”以及“方法存在但内部抛错”。 测试必须针对实际安装的制品,而不只是开发机上的构建目录。比较 debug/release 包含的库,检查 APK 或设备定向 split 中实际存在的对象,并在同一进程执行冷加载和重复请求。可控测试构建中可加入缺依赖夹具,确认应用输出边界清楚的错误,不会把未完成的 engine 标成 ready。如果只有混淆后找不到 callback,应另查 R8;如果设备 ABI 不包含库,应查交付;如果方法能找到但抛错,就查 JNI 异常边界,不要仅改变加载顺序。 有用的诊断应保留构建变体、应用版本、runtime 修订、库名、ABI、首次加载阶段、异常类别及 runtime owner 是否清理,但不能记录提示词、图像、音频或完整私有文件路径。某个测试能证明特定构建的加载顺序,却不能证明普遍设备兼容或推理性能。尤其是一次 System.loadLibrary 成功只说明一个库在该进程中完成映射,并不证明模型制品、delegate 或任务都准备好了。 将恢复设计成状态转换。加载失败时按所有权释放部分创建对象,呈现不可用状态,并阻止请求进入未达到 ready 的 native 对象。重试要由用户或明确策略触发、有次数边界且可观察,不能在每次组合或应用返回前台时循环执行。后续尝试成功后,只清除该尝试自己的错误状态,并让新 session 绑定新初始化的 runtime。这能避免旧失败尝试的迟到回调改变当前状态。 多个库同时存在时,不要添加官方文档未说明的顺序假设。优先调用所属 runtime 的公开初始化流程;只有官方明确要求时才自行预加载内部依赖。把应用层顺序写成测试:进程启动、owner 初始化、engine 创建、任务执行、关闭以及可选的重新初始化。它是可验证序列,不代表所有库都使用相同的全局初始化机制。当前事实卡仅指出生命周期与内存边界,并未给出 Cove 的 linker trace,因此结论只能基于实际观察到的包内容和可重现实验。 错误分类也应影响 UI。用户无需看到动态链接器完整诊断,但应知道功能暂不可用及可执行的下一步。只有在明确且安全时才提供重试;如果必须更新应用或重新安装,应说明该选项,不应让用户反复点按钮。日志仅保存可用于工程排查的类别化证据,并对路径和安装标识做最小化处理。 验收时分别记录:native 对象是否在制品内、平台是否映射成功、JNI 接口是否注册、引擎是否初始化以及任务是否完成。某一步失败并不意味着后续状态成立。将同一制品摘要、ABI 和系统版本附在结果上,后续才可以比较。没有完整原始记录时,不推广成“所有 Android 设备兼容”或“初始化修复完成”。清楚保留边界比依赖错误消息的表面文本更可靠。
冷启动时模型引擎尚未就绪就出现 native 加载失败
release 构建启动本地 AI 功能。适配器加载一个 native 组件后,在任务提交前收到 loader 错误;另一个屏幕也可能尝试初始化。
决策
基于已安装制品追踪依赖与 owner 顺序,分类首个失败点,阻止第二个初始化器掩盖原始错误。
预期结果
用户看到明确的不可用状态,团队能区分交付、链接、注册和任务失败。
需要收集的证据
只有将以下证据与同一模型文件和测试运行关联起来,结论才可复核:
- COVE-LIFECYCLE-01:项目事实支持显式模型 runtime 生命周期测试,不提供 Cove 设备 linker 实测轨迹。
- MEMORY-03:native partial state 与恢复边界需要观察;不能把失败的半初始化 engine 标成就绪。
只有记录了模型身份、构建配置、实际后端、任务样例和失败情况,运行时比较才可复核。
实施流程
- 盘点真实 native 依赖图. 记录每个库名、打包所有者、必要依赖、平台可选属性和首次调用者。区分 APK 自带对象与系统提供或条件使用的 native 库。
- 选择唯一初始化所有者. 把加载放到 runtime adapter 或应用级 owner。UI 创建和重组只观察初始化状态,不独立触发库加载。
- 保留最早的 loader 错误. 在恢复或重试前捕获异常类别和阶段。区分文件缺失、打开/链接失败、JNI 注册或符号查找以及方法内部抛错。
- 针对已安装制品复现. 检查 APK 与设备定向 split 中的库和 ABI,再从安装后的 debug/release 制品执行冷加载与同进程重复加载。
- 断言失败清理和受控重试. 部分初始化失败时保持 engine 不可用,关闭自有临时资源;重试次数受控,并确认旧回调不能把新的初始化尝试误报为 ready。
让每个阶段的状态都可观察。失败信息应指出阶段、模型、运行时和恢复动作,同时避免记录用户私密内容。
验收标准
- 实际构建与 runtime 修订对应的依赖图和初始化 owner 已记录。
- 保留首个失败类别,不把包交付、链接、JNI 注册和任务异常压成一个错误。
- 部分初始化失败后不暴露 ready engine,也不接受推理请求。
- 重试与关闭有边界、有所有者且针对安装制品测试;诊断日志不包含用户内容。
决策对照表
| 维度 | 选择或证据 | 解读方式 |
|---|---|---|
| 安装包中没有 native 库 | 检查 APK/split 内容与 ABI 交付 | 属于交付边界;改变 runtime 加载顺序不能补上缺少的对象。 |
| 库存在但平台无法打开 | 检查依赖链接错误与加载阶段 | 属于 native 映射或依赖问题,应保留首个异常细节。 |
| 库已加载但方法或 callback 失败 | 查 JNI 注册、R8 或异常边界 | 映射成功;需分别判断符号可见性和方法行为。 |
需要避免的失败模式
- 在每个界面进入时重复调用 native 加载,或忽略 UnsatisfiedLinkError。
- 把 APK 未包含库、动态链接失败、JNI 方法找不到和方法内部异常归为同一模型错误。
还要避免静默回退、版本未固定、缺少取消处理,以及把不同配置的结果混在一起下结论。失败运行也应与成功记录一并保存。
最小复现记录
| 层级 | 记录内容 |
|---|---|
| 设备 | 厂商、型号、芯片、内存档位、操作系统版本 |
| 软件 | 应用版本和 Git 提交号 |
| 模型 | 系列、变体、版本、格式、文件长度、摘要、量化方式 |
| 运行时 | 名称、版本、请求后端及实际后端证据 |
| 工作负载 | 样例版本、输入摘要、提示词摘要、输出策略 |
| 结果 | 完成、失败、取消、回退及保护隐私的诊断信息 |
发布检查表
- ☐ 主要搜索问题已回答,且没有未经支持的数字。
- ☐ 所有模型文件和运行时版本均已固定。
- ☐ 代表性任务和失败处理策略均有说明。
- ☐ 生命周期、取消、清理和回退均已测试。
- ☐ 用户内容与网络边界均有说明。
- ☐ 结论只适用于被记录的配置。
- ☐ 新增测试结果时链接到原始方法或证据。
来源与相关证据
中文部署与排错专题见。奇连 AI 端侧专题。
常见问题
是否应在应用启动时手动加载所有 native 依赖?
不应一概而论。遵循所属 runtime 的公开初始化路径;手动预加载内部库会引入未文档化顺序假设,并可能隐藏首个错误及原始异常。
一次 loadLibrary 成功就说明可以推理了吗?
不说明。它只证明一个对象在该进程完成映射;模型验证、engine 初始化、backend 设置和任务接受仍是不同门禁,必须逐步确认并记录证据来源。
每次进入屏幕都自动重试可以吗?
不建议。重试需有次数边界、部分资源清理、唯一 owner 和可观察的新尝试;Composable 重组不应触发它,旧回调也要忽略并核对状态更新及归属。