【NDK / JNI】Sceneform-EQR 集成 Filament JNI 源码:关键点与逐步操作记录

摘要: 在 Sceneform-EQR 项目中,原有对 Filament 的使用方式仅依赖官方预编译产物,Native 层不可控且 SO 体积较大。 为提升工程可维护性并优化产物体积,本文记录了在不修改 Filament Java API 的前提下,引入 Filament JNI 源码、合并 filament-android / filament-utils-android / gltfio-android 三个模块,并统一 JNI_OnLoad 的完整实践过程。

Sceneform-EQR 集成 Filament JNI 源码的工程优化实践

结论:从 Java-only 依赖到 Native 一体化编译,压缩 SO 体积 35%+ so库从4.3M压缩至2.8M
仓库地址:github.com/eqgis/Scene...

一、项目优化背景

1. Sceneform-EQR 与 Filament 的现状

Sceneform-EQR 本质上是一个基于 Filament 的 Android 3D / AR 渲染框架。 在现有工程结构中:

  • Java 层:直接依赖 Filament 官方提供的 Java API

  • Native 层 :使用官方预编译好的 *.so / *.a 产物

  • Filament 模块拆分

    • filament-android
    • filament-utils-android
    • gltfio-android

这种方式虽然接入成本低,但在实际工程中逐渐暴露出几个问题。


2. 原有方案的痛点

(1)SO 体积偏大

Filament 官方 Android 产物以模块级 so形式存在:

  • 多个 so 文件
  • 每个 so 内部存在一定符号冗余
  • 无法针对 Sceneform-EQR 的真实使用场景裁剪

最终 APK / AAR 体积不友好


(2)Native 层不可控
  • 无法修改 JNI 注册逻辑
  • 无法裁剪不需要的 Native 功能
  • 排查 Native Crash 时,只能"对着黑盒猜"

(3)后续深度定制受限

随着 Sceneform-EQR 后续规划:

  • 自定义渲染管线
  • 深度接入 glTF / IBL / 视频纹理
  • 可能修改 Filament JNI 行为

故:必须依赖 Native 源码


3. 优化目标

综合考虑,最终确定本次优化目标:

  1. 引入 Filament JNI 源码
  2. 将 filament / utils / gltfio 三个模块合并为一个 so
  3. 统一 JNI_OnLoad,消除多 so 冲突
  4. 在不修改 Java API 的前提下,完成底层重构
  5. 降低 SO 体积,提升工程可维护性

二、准备阶段

1. 准备 Filament 源码

从官方仓库获取 Filament 源码:


2. 获取 Android Native 编译产物

有两种方式:

  • 方式一:自行编译 Filament

参考filament源码工程的build.md,按步骤操作即可获取

  • 方式二:直接使用官方 Release

当前推荐方式二,直接下载官方 Release 中的 android-native 产物:

github.com/google/fila...

解压后结构如下:

  • include/ → Filament 头文件
  • lib/arm64-v8a/*.a → 静态库

三、代码合并过程

1. Java 层代码合并

(1)覆盖 Filament Java API

当前 Sceneform-EQR 未修改 Filament Java 源码,因此可以直接覆盖:

复制代码
com.google.android.filament.android.*

⚠️ 后续 Sceneform-EQR 将会对 Java API 有定制,需通过 Git 进行差异合并。


(2)合并三个 Android 模块的 Java 源码

将以下模块中的 src/main/java 内容合并至 Sceneform-EQR:

  • filament-android
  • filament-utils-android
  • gltfio-android

2. JNI 源码合并

(1)拷贝 filament-android JNI 源码
css 复制代码
filament-1.67.1/android/filament-android/src/main/cpp

拷贝至:

bash 复制代码
Sceneform-eqr/cpp/filament-android

(2)同样处理 gltfio / filament-utils


3. 拷贝 common 代码

Filament JNI 依赖 common 层工具类:

拷贝至工程中


4. 拷贝 libs 与 third_party

从 Filament 源码中拷贝 必要的依赖库

实践建议: 只拷贝编译所需库,避免 third_party 全量引入


四、解决 JNI_OnLoad 冲突(核心问题)

1. 问题本质

官方 Filament:

!!!每个模块都有自己的 JNI_OnLoad!!!

而现在:

  • 需要 合并为一个 so
  • 只能存在一个 JNI_OnLoad

2. 解决思路

  • 各模块:

    • 移除 JNI_OnLoad
    • 提供 registerXXX() 方法
  • 核心 so:

    • 统一 JNI_OnLoad
    • 手动调用各模块注册函数

3. 修改 Filament.cpp

cpp 复制代码
extern "C"
JNIEXPORT jint registerFilament(JavaVM* vm, void* reserved) {
    JNIEnv* env;
    if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
        return -1;
    }
    ::filament::VirtualMachineEnv::JNI_OnLoad(vm);
    return JNI_VERSION_1_6;
}

4. 修改 Utils.cpp(filament-utils)

cpp 复制代码
extern "C"
JNIEXPORT jint registerUtils(JavaVM* vm, void*) {
    ...
    env->RegisterNatives(...)
    return JNI_VERSION_1_6;
}

5. 重写统一 JNI_OnLoad

cpp 复制代码
extern "C"
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void*) {
    registerFilament(vm, nullptr);
    registerUtils(vm, nullptr);
    return JNI_VERSION_1_6;
}

五、Native 编译配置

1. 引入 Filament 静态库

将 android-native 产物引入工程:


2. CMakeLists 配置

!!!关键点:!!!

  • 使用 STATIC IMPORTED
  • 显式指定 .a 路径
  • 严格控制 target_link_libraries 顺序

3. 主模块 whole-archive

cmake 复制代码
target_link_libraries(eqr-core
    -Wl,--whole-archive
    filament-jni
    filament-utils-jni
    gltfio-jni
    -Wl,--no-whole-archive
)

否则部分 JNI 符号会被裁剪


4. 头文件缺失问题

Filament 1.67.1 中:

  • backend/private 未包含在 android-native

解决方案:

从 Filament 源码工程补拷头文件

补充说明:当前需采用ndk 27以上版本


六、编译结果与优化收益

1. 最终 AAR 产物


2. SO 体积对比

方案 SO 大小
原多 so 方案 4.3 MB
合并 JNI 源码后 2.8 MB
  • 合并编译前
  • 合并编译后

体积减少约 35%


七、小结

本次优化带来的收益

  • SO 体积显著下降
  • Native 层完全可控
  • JNI 注册逻辑清晰统一
  • 为 Sceneform-EQR 后续深度定制打下基础

缺点:Sceneform-EQR升级filament的工作量增大,故以此文备忘~


相关推荐
C_心欲无痕2 小时前
vue3 - shallowReactive浅层响应式对象(只对顶层属性)
前端·javascript·vue.js
AY呀2 小时前
新手必读:React组件从入门到精通,一篇文章搞定所有核心概念
前端·javascript·react.js
葛葵葵2 小时前
使用 AI Workflow 规范化团队 Commit 信息:从混乱到有序
前端
Maxkim2 小时前
「✍️JS原子笔记 」一文搞懂 call、apply、bind 特征及手写实现
前端·javascript·面试
iccb10132 小时前
客服系统前端主题配色动态切换的一种实现思路(含代码)
前端
karshey2 小时前
【前端】svelte支持scss,包管理器是webpack
前端·webpack·scss
Можно2 小时前
深入理解 HTML 中的 iframe:特性、用法与现代实践
前端·html
布局呆星2 小时前
Vue 3 事件处理与列表渲染---02
前端·javascript·vue.js
漫天黄叶远飞2 小时前
🎄2025年圣诞节,单身的我只能用 Gemini 3 “嘴遁”出了一棵赛博圣诞树
前端·人工智能·gemini