spine 在重设SkeletonData之后, web正常 。模拟器,安卓机闪退

  • Creator 版本: 2.4.13

  • 目标平台:
    chrome 127.0 正常
    Creator自带模拟器 闪退
    Android Oppo PECM30 闪退

  • 重现方式:
    骨骼大于300的spine资源
    skeletonA 第一次创建并设置skeletonData , setAnimation 一切正常
    当一个技能播放完成回调触发了, 再次播放skeletonA的动作, skeletonA = skeletonData ,之后的几帧会闪退。

  • 首个报错:

  • 之前哪个版本是正常的: 2.0.9

  • 手机型号: Oppo Pecm30

  • 编辑器操作系统: window 10

  • 重现概率:模拟器,手机 必现

首先 更新同一个skeleton动画,其实不需要重设skeletonData , 这个问题触发原因是业务上有写错地方,导致没有复用skeleton而是,重设了 skeletonA = skeletonData
目前用了复用就正常。

同时把我探索排查过程发现的反馈一下,
根据报错信息定位到引擎对skeletonData的重新赋值可能存在泄露
Js层的实现

CPP的实现

如图,js有对listener做了清理
但CPP中似乎没有清理,就把新的state给赋过来,导致旧的state原本应该变成垃圾内存,但如果它下面的listener没清理干净,是否就导致泄露了呢?
从报错看,update - > drain() 是从自更新触发的闪退
报错时,_state中 _eventQueueEntries.size 是有长度的,但里面有些引用已经是null的了

按照重现流程高频重设 skeletonData
我调试安卓时,断点了 _eventQueueEntries下面 trackEntry==null,还真会触发到。_eventQueueEntries里面很多数据占在数组中但具体内容都是丢失,看起来似乎这个state就是废弃的一样,从而推测它是否就是那个被新的state挤掉的,本该被垃圾回收掉的state依旧触发了update

看了一下3.8.3 目前cpp也是同样的写法 。希望官方有空排查一下

另外,如果其他开发者 如果遇到spine 真机闪退,web正常的话。
1.尽量避免大骨骼。 我150骨骼以下的,这操作不会出现闪退
2.检查是否在其他spine动画 结束回调时,调用了重设 skeleton = skeletonData ,
同时应该避免高频率的重设skeletonData

384有修复 setXXXListener中,添加 enable,disable组件造成的崩溃问题;麻烦在384上看下是否能复现,另外是否可以提供demo?

有哪条合并编号嘛? git找最新的SkeletonAnimatin 文件 貌似依旧没有清理。 384是有新地址嘛?貌似找不到,麻烦给我个链接?


我找的是这个 , 看起来也是没有清理

这个是原生的

好,我去看看,谢谢啦


挑了里面的改进2.4.13
其他的版本跨度太多,结构差别太大 不好改了 哎

384是没问题的?

看git上面还没最终确认,我这边没试3.8.4了。 只移了 cache = null 不过我这边还是不行

方便的话,烦请上传可复现demo

公司项目目前不方便哈,
另外 那条3.8.4 https://github.com/cocos/cocos-engine/pull/17432/commits/7c88e9dfd4ce313c3ba73e888d0ea03b18c2b799

这条是处理了ts里面的, 我在断点查模拟器实现时,看到已经是有清理掉了,但还是模拟器会崩溃
可能是我同屏骨骼动画实在太多了。 应该角色+技能有几千个。
角色复用,但技能没有复用,播多几个技能就会崩溃
image

看是否方便我远程看下?

也不方便公司机密了 :sweat:

2.4.13 本想去找3.8.4的合并,但是里面的实现改动很大,增删了很多内容,牵扯太多其他文件,没办法只合并一条,我估计我至少要把项目升到3.7才匹配上结构

不过我通过从业务上规避了 感谢你的关注! :+1: 我顺便分享一些规避要点

从业务规避闪退:
1.真机花屏:
skeleton.setCompleteListener里面避免调自己重复的隐藏与显示。
之前项目中有个错误写法,就是在整个回调函数里面,不小心调了某个方法设了自己active=false,同时为了正常显示,打了补丁把自己显示回来active=true, 这时候在web是正常,但在真机是花屏.
解决: 避免同一帧做隐藏与显示. 特别是回调嵌套时

2.真机闪退
会闪退的写法:
角色修改动作但重新加载了赋值了skeletonData:
A角色已经加载了一次skeletonData,播放了动作A, 然后再播放动作B时,没有判断好此节点是否已经加载过spine数据, 导致重新赋值了一次一样的 skeleton.skeletonData 这会导致真机在几帧之后就闪退.

处理: 写法上判断是否当前skeleton是否已经有赋值了一样的这个skeletonData,有的话直接播动画就行,不重新赋值skeletonData

  1. 技能回收 引起的真机闪退
    一开始以为是spine导出版本跟不上导致,之前是3.6, 重新导了3.8的spine, 不过还是一样
    首先,skeleton是挂在一个可回收的技能节点skilleffect上, skeleton 回收时 skeletonData = null , 然后skilleffect入池回收. 在从池子拿出来复用时,赋值新的skeletonData几帧之后就出现真机闪退

我的处理: 技能移除时,调用
skilleffect.stopAllActions()
skeleton.clearTracks() ,
skeleton.removeStateListener() , (这里是自己改引擎, 本质是 _state.removeListener(this._listener) 把state侦听给移除了. 因为从之前真机报错上看,是因为skeleton虽然data清理了,但真机时_state还有东西在跑导致)
skeleton.enabled = false;
skeleton.skeletonData = null;

此外过程改了点cpp代码真机的实现, (不确定有没有用)
同时合并了cpp的 MiddlewareManager:update ,的 判空 editor
MeshBuffer.cpp , MeshBuffer , ib.setMaxSize(INIT_INDEX_BUFFER_SIZE * 100) 乘多了100 (不一定有用)