ARCore Session 生命周期正确管理
Session 的生命周期模型
ARCore Session 是一个长生命周期对象——它的生命周期和 Activity/Fragment 对齐,但内部状态有自己的变化规律:
Session 创建
↓
configure() → 设置模式和 API
↓
resume() → 开始 SLAM 跟踪(Camera ON)
↓
每一帧 update() → 处理最新画面,获取 Frame 和 Trackable
↓
pause() → 停止跟踪(Camera OFF),Session 保留
↓
resume() → 恢复跟踪(Camera 重新 ON)
↓
destroy()(可选)→ 释放所有资源
一个 Session 对象可以 resume/pause 任意次——不需要每次都 new 新的。
Activity 各阶段的正确做法
onCreate:创建 Session(但不 resume)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 1. 检查设备是否支持
val availability = ArCoreApk.getInstance().checkAvailability(this)
if (availability.isUnsupported) {
finish()
return
}
// 2. 请求安装 Google Play Services for AR(首次打开)
if (availability == ArCoreApk.Availability.SUPPORTED_NOT_INSTALLED ||
availability == ArCoreApk.Availability.SUPPORTED_APK_TOO_OLD) {
ArCoreApk.getInstance().requestInstall(this, true)
return // 用户装完后 Activity 会自动重建,走到这里
}
// 3. 创建 Session(只创建一次)
try {
arSession = Session(this)
} catch (e: UnavailableException) {
// 设备完全不支持
finish()
return
}
// 4. 配置 Session(只需要配置一次,后续 resume 不需要重配)
arSession?.configure(
Session.Config(this).apply {
// 在这里设置所有 API 模式
}
)
}
onResume:恢复 Session + 开启渲染循环
override fun onResume() {
super.onResume()
if (arSession == null) return
// 1. 恢复 Session(打开摄像头,开始 SLAM)
try {
arSession?.resume()
} catch (e: CameraException) {
// 摄像头被其他应用占用
showCameraBusyError()
return
}
// 2. 开启 GLSurfaceView(恢复渲染)
glSurfaceView?.onResume()
// 3. 启动帧更新回调(每一帧调用 session.update())
startFrameLoop()
}
onPause:暂停 Session + 停止渲染循环
override fun onPause() {
super.onPause()
// 1. 停止帧更新回调(先停渲染循环,防止渲染 Pause 后的 Session)
stopFrameLoop()
// 2. 暂停 Session(关闭摄像头,停止 SLAM)
arSession?.pause()
// 3. 暂停 GLSurfaceView
glSurfaceView?.onPause()
}
onDestroy:可选的 destroy
override fun onDestroy() {
super.onDestroy()
// destroy 不是必须的,但如果你确定不再使用这个 Session,
// 调用它可以释放 GL 资源和 native 内存
arSession?.destroy()
arSession = null
}
帧更新循环
Session.update() 是 ARCore 的核心——必须在每一帧调用,在 GL 渲染之前:
// GLSurfaceView.Renderer 中
override fun onDrawFrame(gl: GL10?) {
// 1. Session.update() —— 必须在任何渲染之前调用
val frame = try {
arSession?.update()
} catch (e: SessionException) {
// Session 已经 pause,忽略这一帧
return
} ?: return
// 2. 更新 Trackable(平面、特征点、Augmented Image 等)
// 3. 用 frame.camera.pose 渲染摄像头画面
// 4. 在适当位置叠加虚拟物体
}
帧循环频率
Session.update() 的调用频率决定了 ARCore 的更新频率:
- GLSurfaceView 默认 60fps → Session.update() 也是 60fps
- ARCore 内部摄像头可能只有 30fps → 但 Session.update() 每次都返回最新的 Frame
注意:不要把 Session.update() 放在后台线程里——它必须在 GL 渲染线程中串行调用,否则会有线程安全问题。
常见的生命周期坑
坑一:onPause 时不关 Session
// 错误
override fun onPause() {
super.onPause()
// 忘记 pause Session!
// 结果:摄像头一直开着、SLAM 一直跑、电池疯狂消耗
}
坑二:resume/pause 的顺序反了
// 错误顺序
override fun onPause() {
glSurfaceView?.onPause() // 先关渲染
arSession?.pause() // 再关 Session
// 问题:关渲染后 Session 还在跑,浪费几毫秒
}
// 正确顺序
override fun onPause() {
arSession?.pause() // 先停 Session(Camera OFF)
glSurfaceView?.onPause() // 再关渲染
}
坑三:每次 onResume 都重新创建 Session
// 错误
override fun onResume() {
arSession = Session(this) // 每次都 new!
// 结果:旧 Session 没被 destroy → 内存泄漏 + 摄像头资源没释放
}
// 正确
override fun onResume() {
if (arSession == null) {
arSession = Session(this)
}
arSession?.resume()
}
坑四:Activity 旋转屏幕
// 错误:Activity 配置变更会触发重建
// 如果 Activity 在 Manifest 中声明了 configChanges,onCreate 不会再被调用
// Session 可能处于不一致状态
// 正确:要么在 Manifest 中声明,要么处理生命周期
<activity
android:configChanges="orientation|screenSize|keyboardHidden" />
坑五:Fragment 中的 Session
// Fragment 的生命周期和 Activity 不同
// Session 最好由 Activity 管理,Fragment 通过 ViewModel 共享
// 不要在 Fragment.onDestroyView() 里 destroy Session
// 因为 View 销毁了但 Activity 还活着
Unity 中的 Session 生命周期
Unity AR Foundation 自动处理 Session 生命周期:
- ARSession 组件在场景中存在就自动管理
- 切换场景时如果有 ARSession 则自动保留 Session
- 后台时自动 pause,前台时自动 resume
需要手动干预时:
var sessionManager = GetComponent<ARSessionManager>();
// 手动暂停
sessionManager.enabled = false;
// 手动恢复
sessionManager.enabled = true;
后台恢复的注意事项
当用户把应用切到后台再切回来时:
| 组件 | 状态 | 恢复行为 |
|---|---|---|
| Session | Paused | Resume 后 SLAM 从当前位置继续跟踪 |
| 摄像头 | 关闭 | Resume 后重新打开 |
| Trackable | 保留 | 之前检测到的 Plane/Anchor 仍然存在 |
| Cloud Anchors | 保留 | 托管/解析状态不变 |
Session 会丢失的情况:
- 手机重启
- Google Play Services for AR 更新
- Activity 被系统强制杀掉(内存不足)
- App 数据被清除
这些情况下 Session 需要重新创建,之前的本地 Anchor 全部丢失。Cloud Anchors 不受影响(在云端)。