You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ARCore Cloud Anchor托管状态监听优化方案咨询

优化ARCore Cloud Anchor状态监听的方案

问题描述

我正在开发一款共享增强现实(AR)体验应用,想了解是否存在更优方案,可获知ARCore服务器完成Cloud Anchor托管的时机。目前我采用的实现方式是在MainActivity的onCreate函数中调用:

customArFragment.arSceneView.scene.addOnUpdateListener(arHandler::onUpdateFrame)

具体处理类代码如下:

class ArHandler( val fragment: ArFragment, val activity: Activity, val snackbarHelper: SnackbarHelper ) {
 fun onUpdateFrame(frameTime: FrameTime) {
 checkUpdatedAnchor()
 }
 @Synchronized private fun checkUpdatedAnchor() {
 if (appAnchorState != AppAnchorState.HOSTING && appAnchorState != AppAnchorState.RESOLVING) {
 return
 }
 val cloudState = cloudAnchor?.cloudAnchorState
 Log.i("CheckUpdatedAnchor", cloudState.toString())
 if (appAnchorState === AppAnchorState.HOSTING) {
 if (cloudState != null) {
 if (cloudState.isError) {
 snackbarHelper.showMessage(
 activity, activity.getString(R.string.hosting_error) + cloudState
 )
 appAnchorState = AppAnchorState.NONE
 } else if (cloudState == Anchor.CloudAnchorState.SUCCESS) {
 appAnchorState = AppAnchorState.HOSTED
 if (anchorTask == AnchorTask.CREATE) {
 storeManager.nextShortCode(object : StoreManager.ShortCodeListener {
 override fun onShortCodeAvailable(shortCode: Int?) {
 myShortCode = shortCode
 if (shortCode == null) {
 snackbarHelper.showMessage(
 activity, "Could not get shortCode"
 )
 return
 }
 cloudAnchor?.let { storeManager.storeUsingShortCode(shortCode, it.cloudAnchorId) }
 snackbarHelper.showMessage(
 activity, "Anchor hosted! Cloud Short Code: " + shortCode
 )
 }
 })
 } else if (anchorTask == AnchorTask.UPDATE) {
 myShortCode?.let { sc ->
 cloudAnchor?.let { ca ->
 storeManager.updateCloudAnchorID(
 sc, ca.cloudAnchorId
 )
 snackbarHelper.showMessage(
 activity, "Anchor Updated!: " + myShortCode
 )
 }
 }
 }
 }
 }
 } else if (appAnchorState == AppAnchorState.RESOLVING) {
 if (cloudState != null) {
 if (cloudState.isError) {
 snackbarHelper.showMessage(
 activity, "Error resolving anchor.. " + cloudState
 )
 appAnchorState = AppAnchorState.NONE
 } else if (cloudState == Anchor.CloudAnchorState.SUCCESS) {
 snackbarHelper.showMessage(
 activity, "Anchor resolved successfully"
 )
 appAnchorState = AppAnchorState.RESOLVED
 }
 }
 }
 }
 fun createCloudAnchor(customArFragment: CustomArFragment, anchor: Anchor) {
 this.cloudAnchor = customArFragment.arSceneView.session.hostCloudAnchor(anchor)
 this.anchorTask = AnchorTask.CREATE
 this.appAnchorState = ArHandler.AppAnchorState.HOSTING
 snackbarHelper.showMessage(activity, activity.getString(R.string.hosting_anchor))
 this.cloudAnchor?.let { this.placeObject(it, Uri.parse("ArcticFox_Posed.sfb")) }
 }
}

我认为这种通过SceneForm每帧回调检查锚点状态的“监听”方式不够合理,想请教是否有更好的实现思路?


优化方案

Great question! 每帧轮询云锚点状态确实不是最高效的做法——ARCore其实提供了专门的addOnCloudAnchorStateListener接口,能让你直接响应状态变化,不用在每帧做无意义的检查。下面是具体的重构思路和代码示例:

核心思路

不用给场景绑定OnUpdateListener,而是在创建或解析云锚点时,直接给ARCore的Session注册状态监听器。这个监听器只会在云锚点状态发生变化时触发,既节省资源,也更符合ARCore的设计逻辑。

重构后的代码示例

首先修改createCloudAnchor方法,在托管云锚点时添加监听器:

fun createCloudAnchor(customArFragment: CustomArFragment, anchor: Anchor) {
    val session = customArFragment.arSceneView.session
    this.cloudAnchor = session.hostCloudAnchor(anchor)
    this.anchorTask = AnchorTask.CREATE
    this.appAnchorState = AppAnchorState.HOSTING
    snackbarHelper.showMessage(activity, activity.getString(R.string.hosting_anchor))
    this.cloudAnchor?.let { this.placeObject(it, Uri.parse("ArcticFox_Posed.sfb")) }

    // 注册云锚点状态监听器
    session.addOnCloudAnchorStateListener { cloudAnchor, cloudAnchorState ->
        // 只处理当前目标云锚点的状态变化
        if (cloudAnchor != this.cloudAnchor) return@addOnCloudAnchorStateListener
        
        handleCloudAnchorStateChange(cloudAnchorState)
    }
}

// 单独提取状态变化处理逻辑,提升代码可读性
@Synchronized private fun handleCloudAnchorStateChange(cloudState: Anchor.CloudAnchorState) {
    Log.i("CloudAnchorState", cloudState.toString())
    
    when(appAnchorState) {
        AppAnchorState.HOSTING -> {
            if (cloudState.isError) {
                snackbarHelper.showMessage(
                    activity, activity.getString(R.string.hosting_error) + cloudState
                )
                appAnchorState = AppAnchorState.NONE
            } else if (cloudState == Anchor.CloudAnchorState.SUCCESS) {
                appAnchorState = AppAnchorState.HOSTED
                handleHostSuccess()
            }
        }
        AppAnchorState.RESOLVING -> {
            if (cloudState.isError) {
                snackbarHelper.showMessage(
                    activity, "Error resolving anchor.. " + cloudState
                )
                appAnchorState = AppAnchorState.NONE
            } else if (cloudState == Anchor.CloudAnchorState.SUCCESS) {
                snackbarHelper.showMessage(
                    activity, "Anchor resolved successfully"
                )
                appAnchorState = AppAnchorState.RESOLVED
            }
        }
        else -> return // 忽略无关状态
    }
}

// 提取托管成功后的处理逻辑
private fun handleHostSuccess() {
    if (anchorTask == AnchorTask.CREATE) {
        storeManager.nextShortCode(object : StoreManager.ShortCodeListener {
            override fun onShortCodeAvailable(shortCode: Int?) {
                myShortCode = shortCode
                if (shortCode == null) {
                    snackbarHelper.showMessage(
                        activity, "Could not get shortCode"
                    )
                    return
                }
                cloudAnchor?.let { storeManager.storeUsingShortCode(shortCode, it.cloudAnchorId) }
                snackbarHelper.showMessage(
                    activity, "Anchor hosted! Cloud Short Code: " + shortCode
                )
            }
        })
    } else if (anchorTask == AnchorTask.UPDATE) {
        myShortCode?.let { sc ->
            cloudAnchor?.let { ca ->
                storeManager.updateCloudAnchorID(
                    sc, ca.cloudAnchorId
                )
                snackbarHelper.showMessage(
                    activity, "Anchor Updated!: " + myShortCode
                )
            }
        }
    }
}

额外优化建议

  • 清理监听器:当不再需要云锚点时(比如活动销毁、锚点被移除),记得调用session.removeOnCloudAnchorStateListener()避免内存泄漏。
  • 分离关注点:把状态处理、成功逻辑拆成独立方法,让代码更易维护和调试。
  • 避免冗余检查:因为监听器只在状态变化时触发,所以不用额外判断状态是否更新,直接处理新状态即可。

内容的提问来源于stack exchange,提问作者Ivan Carrasco Alonso

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.11 09:11:57