能否不使用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
相关产品推荐
相关产品推荐

