ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

APP-android_arm64启动流程全解:从权限申请、资源安装到NativeActivity加载的10个关键步骤

APP-android_arm64启动流程全解:从权限申请、资源安装到NativeActivity加载的10个关键步骤

【免费下载链接】APP-android_arm64

项目地址:https://gitcode.com/gh_mirrors/ap/APP-android_arm64
点击查看免费下载

APP-android_arm64 是开源 3D 建模软件 Blender 移植到 Android 设备的 APP 代码仓库(应用名为OBlender)。本文面向新手,完整拆解它从点击图标到进入建模界面的启动流程:权限申请、资源安装、NativeActivity 加载、JNI 回调桥接,共 10 个关键步骤,并附上核心源码路径,帮助你快速理解"一个 C++ 引擎如何跑在手机 Java 壳里"。

启动流程总览

整个启动链路可以概括为一条流水线:

StartupActivity(Java)→ 权限申请 → 资源安装 →OBLNativeActivity(NativeActivity)→libblender.so(C++)→ Blender 引擎初始化 → 事件循环渲染

两个模块分工清晰:

  • app 模块:Java 壳 + C++ Native 层,负责启动、权限、窗口和引擎加载;
  • OBLFiles 模块:打包了 Blender 运行所需的全部资源文件(Python 库、脚本、字体、图标等),负责把 assets 资源"安装"到手机存储。

下面按启动顺序逐步拆解。🚀

10 个关键步骤详解

步骤1:系统定位启动入口 StartupActivity

在 AndroidManifest.xml 中,StartupActivity被声明为android.intent.action.MAIN+LAUNCHER,即点击桌面图标后系统启动的第一个 Activity;而真正的引擎界面OBLNativeActivity只是普通 Activity,由前者跳转打开(见 AndroidManifest.xml)。

这种"先过一遍准备逻辑,再进主界面"的双 Activity 结构,是 Android 移植大型 C++ 引擎的常见做法——把权限、文件这类耗时且可能失败的操作挡在主界面之外。

步骤2:Splash 启动画面与全屏初始化

StartupActivity.java 的onCreate做了两件事:

  • 调用ScreenUtils.fullScreen()隐藏状态栏和导航栏,进入沉浸式全屏;
  • 加载 activity_startup.xml 布局,中央显示splash.png启动图,给用户一个"正在加载"的视觉缓冲。

随后通过Handler.postDelayed延迟1500 毫秒才发出第一条权限消息——这个延迟正是 Splash 画面能稳定展示的时间窗口。

步骤3:存储权限申请(Android 11 双策略适配)

这是新手最容易困惑的一步。Blender 需要读写"我的文件"来打开/保存.blend工程,因此必须申请存储权限,且 StartupActivity.java 针对不同系统版本采用了两套策略:

系统版本权限类型申请方式
Android 11(API 30,R)及以上"所有文件访问权限"Environment.isExternalStorageManager()检查,未授权则跳转系统设置页ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION
Android 10 及以下WRITE_EXTERNAL_STORAGE弹窗 +ActivityCompat.requestPermissions运行时申请

对应权限声明见 AndroidManifest.xml:MANAGE_EXTERNAL_STORAGE、WRITE_EXTERNAL_STORAGE、READ_EXTERNAL_STORAGE三者齐备。如果用户拒绝授权,APP 会 Toast 提示"存储权限获取失败,不能保存文件",但仍会继续后续流程(只是无法保存文件)。

步骤4:网络权限检查

存储权限通过后,Handler 收到MSG_ID_INTERNETPERMISSION消息,用ContextCompat.checkSelfPermission检查INTERNET权限(见 StartupActivity.java)。由于INTERNET是安装时即授予的普通权限,这里基本只做确认,随后立即进入资源安装环节。

步骤5:资源安装(assets → 手机存储,增量复制)

这是启动流程中耗时最长的一步。InstallOBLFiles.java 将 APK 内 assets 中的 Blender 资源复制到两个目标目录:

  • Home 目录(外部存储/com.epai.oblender/):examples、Desktop、Documents、Downloads、Music、Pictures、Videos——模拟桌面系统的"用户文件区",Blender 的文件浏览器就指向这里;
  • Config 目录(APP 内部 files 目录):python(Python 3.11 运行时)、4.0(数据文件/图标/字体)、scripts(Python 脚本)、usd——引擎运行必需的"配置区"。

两个目录的完整路径由 FileUtils.java 的getExternStorageDir计算:外部存储不可用时自动降级到内部存储,并自动mkdirs创建目录。

💡增量复制设计:copyAssetFolder发现目标文件夹已存在就直接跳过(InstallOBLFiles.java),copyAsset用createNewFile的返回值判断文件是否已存在——所以只有首次启动会完整复制几百 MB 资源,之后每次启动都是秒过。这就是为什么第二次打开 APP 明显更快的原因。

步骤6:跳转 OBLNativeActivity 并传递路径参数

一切就绪后,StartupActivity.java 构造 Intent 把HomePath、ConfigPath两个字符串传给OBLNativeActivity,然后finish()关闭 Splash 页面。

注意一个细节:如果启动来源是Intent.ACTION_SEND(从其他 APP 分享文件进来),会跳过资源复制直接进建模页,避免重复 IO。

步骤7:加载 libblender.so 并启动 Native 线程

OBLNativeActivity继承自系统NativeActivity,类头部静态块执行System.loadLibrary("blender")(见 OBLNativeActivity.java)。这个libblender.so就是由 CMakeLists.txt 以SHARED方式构建、链接了整套 Blender 静态库的巨型动态库。

而NativeActivity的精髓在于 AndroidManifest.xml 里这行元数据:

<meta-data android:name="android.app.lib_name" android:value="blender" />

系统框架会自动 dlopen 该库,并在新开的 Native 线程中调用 C 函数android_main()——Java 与 C++ 两条世界从此并行运转。

步骤8:JNI 建立双向回调桥(initial)

Native 线程跑起来之前,Java 侧onCreate里调用的initial(strHomePath, strConfigPath)先把两个存储路径传给 C++(见 OBLNativeActivity.java)。

JNI 实现 android_native_app_glue.cc 中,这个函数做了关键铺垫:

  1. 对 Activity 对象取NewGlobalRef,防止被 GC 回收;
  2. 缓存showWindow、GetAsyncKeyState、SetValue、getClipboard、putClipboard等方法 ID——之后 C++ 引擎需要弹设置面板、呼出软键盘、读写剪贴板时,就能通过反向 JNI直接回调这些 Java 方法,无需每次查找。

这是"Java 管 UI、C++ 管引擎"架构的桥梁所在。🔌

步骤9:窗口创建触发 Blender 全量子系统初始化

C++ 侧android_main(native-lib.cpp)注册了命令回调engine_handle_cmd并进入ALooper_pollAll事件循环。当 Android 窗口就绪、收到APP_CMD_INIT_WINDOW命令时,触发engine_init_display(native-lib.cpp),完成三件大事:

  1. 日志落地:freopen把 stderr 重定向到 Home 目录的error.log,引擎日志写入log.log,方便排查问题;
  2. 注入环境变量:BLI_setenv设置HOME、BLENDER_SYSTEM_DATAFILES(指向4.0/config/datafiles)、BLENDER_SYSTEM_SCRIPTS、PYTHONPATH/PYTHONHOME等,让引擎找到步骤5安装好的资源;
  3. 引擎初始化:以{"blender", "-d"}参数调用 creator.cc 的mainBlenderInitial。

mainBlenderInitial就是桌面版 Blender 的main()函数体,依次完成:日志系统CLG_init、上下文CTX_create、线程 API 初始化、DNA/RNA 系统注册、渲染引擎RE_engines_init(Cycles/Eevee)、窗口管理器WM_init、键位配置,最后调用Wm_loop_pre准备进入主循环(creator.cc)。

步骤10:Native 事件循环驱动每帧渲染

初始化完成后isInitial置为 true,android_main的循环从"等待事件"切换为每帧调用engine_draw_frame(native-lib.cpp):

engine_draw_frame → mainBlenderLoop → Wm_loop(事件处理 + 重绘)

渲染窗口则由 Java 侧WindowGLSurfaceView提供,Surface 创建/销毁时通过updateSurfaceJNI 更新 C++ 的ANativeWindow(android_native_app_glue.cc)。C++ 引擎打开子窗口(如左上角设置面板)时再反向调用 Java 的showWindow(OBLNativeActivity.java),至此一个完整可交互的 Blender 建模界面呈现在手机上。🎉

核心文件速查表

环节文件路径说明
启动入口声明AndroidManifest.xml权限声明、LAUNCHER 入口、lib_name 元数据
权限与 SplashStartupActivity.java四段式 Handler 状态机
资源安装InstallOBLFiles.javaassets 增量复制到 Home/Config
路径工具FileUtils.java内外存储选择与目录创建
Native 主界面OBLNativeActivity.javaNativeActivity + 反向回调 UI
Native 入口native-lib.cppandroid_main 事件循环与显示初始化
JNI 桥接android_native_app_glue.ccinitial/updateSurface 等 JNI 实现
引擎初始化creator/creator.ccBlender main() 逻辑移植

总结:这套启动设计妙在哪?

✅失败前移:权限、资源等可能失败或耗时长的操作全部放在 Splash 阶段,主界面只做"点亮引擎";

✅状态机驱动:Handler + 消息枚举(MSG_ID_PERMISSION → INTERNETPERMISSION → COPYFILE → STARTACTIVITY)让每一步跳转清晰可追踪;

✅增量安装:首次全量复制、后续秒开,兼顾了大体积资源与启动速度;

✅双向 JNI 桥:Java 持有引擎 UI 能力(窗口/键盘/剪贴板),C++ 持有事件与渲染能力,各司其职。

理解这 10 个步骤后,你就可以在 Doc/编译流程_cn.txt 的指引下,完整编译并运行这个把 Blender 装进口袋的 APP 了。

【免费下载链接】APP-android_arm64

项目地址:https://gitcode.com/gh_mirrors/ap/APP-android_arm64
点击查看免费下载
上一篇:Everything Claude Code 规则架构详解:如何按需安装通用与语言特定规则
下一篇:不碰 git 历史的撤销魔法:Context+ Shadow 恢复点系统完全指南

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

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进