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

能否不使用UnityPlayerActivity,在Fragment中运行Unity?

是否可以在Fragment中运行Unity,实现与UnityPlayerActivity完全一致的效果?

可以,你遇到的二次进入崩溃是UnityPlayer实例生命周期处理不当导致的,有成熟的稳定运行方案。

崩溃原因分析

你当前的实现逻辑每次创建Fragment时新建UnityPlayer实例,销毁Fragment时调用quit()方法完全终止Unity native运行时。Unity 2020及以上版本的Unity运行时全局仅支持单实例,第一次调用quit()后native层资源已经完全释放,第二次新建UnityPlayer时会访问已释放的内存地址,触发你日志中的SIGSEGV空指针错误。

稳定实现方案

方案1:全局复用UnityPlayer单实例(推荐,性能最优)

全程只初始化一次UnityPlayer,避免反复销毁重建带来的性能损耗和崩溃问题,实现步骤:

  • 在Application类或者自定义全局单例中首次需要时初始化UnityPlayer实例,全局持有
  • Fragment创建视图时,先将UnityPlayer的view从原有父容器中移除,再添加到当前Fragment的布局容器中
  • Fragment销毁视图时,仅将UnityPlayer的view从当前容器移除,调用pause()暂停运行,不要调用quit()
  • 应用完全退出时再调用quit()释放Unity资源

方案2:按需销毁重建(适合Unity使用频率极低的场景)

如果确实需要每次进入Fragment重新加载Unity内容,需要调整销毁逻辑:

  • 调用quit()后主动等待1~2秒,确保native层资源完全释放后再新建UnityPlayer实例
  • 重建前检查UnityPlayer的native实例状态,避免访问已释放的资源

修正后的核心代码示例

// 全局Unity单例管理类
object UnityManager {
    var unityPlayer: UnityPlayer? = null
        private set

    fun initUnity(context: Context) {
        if (unityPlayer == null) {
            unityPlayer = UnityPlayer(context)
        }
    }

    fun destroyUnity() {
        unityPlayer?.quit()
        unityPlayer = null
    }
}

// 修正后的AvatarFragment
class AvatarFragment : BaseFragment() {
    private val avatarViewModel: AvatarViewModel by viewModel()
    private var unityPlayer: UnityPlayer? = null

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        // 全局初始化UnityPlayer
        UnityManager.initUnity(requireActivity())
        unityPlayer = UnityManager.unityPlayer

        val view = inflater.inflate(R.layout.fragment_avatar, container, false)
        initAvatar(view)
        avatarViewModel.run {
            screenState.observe(viewLifecycleOwner, ScreenStateObserver())
            initAvatar()
        }
        return view
    }

    private fun initAvatar(view: View) {
        // 先把UnityView从原有父容器移除
        (unityPlayer?.view?.parent as? ViewGroup)?.removeView(unityPlayer?.view)
        // 添加到当前容器
        view.avatarFrameLayout.addView(
            unityPlayer?.view,
            FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT
        )
        unityPlayer?.requestFocus()
        unityPlayer?.windowFocusChanged(true)
    }

    override fun onDestroyView() {
        // 销毁视图时仅移除UnityView,不销毁实例
        (unityPlayer?.view?.parent as? ViewGroup)?.removeView(unityPlayer?.view)
        super.onDestroyView()
    }

    override fun onPause() {
        super.onPause()
        unityPlayer?.pause()
    }

    override fun onResume() {
        super.onResume()
        unityPlayer?.resume()
    }

    override fun onLowMemory() {
        super.onLowMemory()
        unityPlayer?.lowMemory()
    }
}

额外注意事项

  • 承载Fragment的Activity需要重写onWindowFocusChanged、onConfigurationChanged等系统回调,同步调用到UnityPlayer的对应方法,保证行为和原生UnityPlayerActivity完全一致
  • 如果需要在Unity和Android原生侧交互,注意不要在Unity运行时释放交互相关的上下文资源,避免空指针问题

内容的提问来源于stack exchange,提问作者Bartosz Bańczerowski

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.30 02:30:02